#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <pthread.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#ifndef TLS
#define TLS __thread
#endif
TLS char STORAGE_PATH[PATH_MAX] = "/var/lib/ripple/data.bin";
TLS char USERNAME[33] = "Default";
TLS char SERVER_ADDRESS[33] = "127.0.0.1";
TLS unsigned short PORT = 2012;
#define SECRET_KEY { \
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 \
}
TLS unsigned char secret_block[32+4] = SECRET_KEY;
/*
* FIPS 180-2 SHA-224/256/384/512 implementation
* Last update: 02/02/2007
* Issue date: 04/30/2005
*
* Copyright (C) 2013, Con Kolivas <
[email protected]>
* Copyright (C) 2005, 2007 Olivier Gay <
[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define SHA256_DIGEST_SIZE ( 256 / 8)
#define SHA256_BLOCK_SIZE ( 512 / 8)
#define SHFR(x, n) (x >> n)
#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define CH(x, y, z) ((x & y) ^ (~x & z))
#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3))
#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10))
typedef struct {
unsigned int tot_len;
unsigned int len;
unsigned char block[2 * SHA256_BLOCK_SIZE];
uint32_t h[8];
} sha256_ctx;
#define UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8_t) ((x) ); \
*((str) + 2) = (uint8_t) ((x) >> 8); \
*((str) + 1) = (uint8_t) ((x) >> 16); \
*((str) + 0) = (uint8_t) ((x) >> 24); \
}
#define PACK32(str, x) \
{ \
*(x) = ((uint32_t) *((str) + 3) ) \
| ((uint32_t) *((str) + 2) << 8) \
| ((uint32_t) *((str) + 1) << 16) \
| ((uint32_t) *((str) + 0) << 24); \
}
#define SHA256_SCR(i) \
{ \
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \
+ SHA256_F3(w[i - 15]) + w[i - 16]; \
}
uint32_t sha256_h0[8] =
{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
uint32_t sha256_k[64] =
{0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
/* SHA-256 functions */
void sha256_transf(sha256_ctx *ctx, const unsigned char *message,
unsigned int block_nb)
{
uint32_t w[64];
uint32_t wv[8];
uint32_t t1, t2;
const unsigned char *sub_block;
int i;
int j;
for (i = 0; i < (int) block_nb; i++) {
sub_block = message + (i << 6);
for (j = 0; j < 16; j++) {
PACK32(&sub_block[j << 2], &w[j]);
}
for (j = 16; j < 64; j++) {
SHA256_SCR(j);
}
for (j = 0; j < 8; j++) {
wv[j] = ctx->h[j];
}
for (j = 0; j < 64; j++) {
t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6])
+ sha256_k[j] + w[j];
t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
ctx->h[j] += wv[j];
}
}
}
void sha256_init(sha256_ctx *ctx)
{
int i;
for (i = 0; i < 8; i++) {
ctx->h[i] = sha256_h0[i];
}
ctx->len = 0;
ctx->tot_len = 0;
}
void sha256_update(sha256_ctx *ctx, const unsigned char *message,
unsigned int len)
{
unsigned int block_nb;
unsigned int new_len, rem_len, tmp_len;
const unsigned char *shifted_message;
tmp_len = SHA256_BLOCK_SIZE - ctx->len;
rem_len = len < tmp_len ? len : tmp_len;
memcpy(&ctx->block[ctx->len], message, rem_len);
if (ctx->len + len < SHA256_BLOCK_SIZE) {
ctx->len += len;
return;
}
new_len = len - rem_len;
block_nb = new_len / SHA256_BLOCK_SIZE;
shifted_message = message + rem_len;
sha256_transf(ctx, ctx->block, 1);
sha256_transf(ctx, shifted_message, block_nb);
rem_len = new_len % SHA256_BLOCK_SIZE;
memcpy(ctx->block, &shifted_message[block_nb << 6],
rem_len);
ctx->len = rem_len;
ctx->tot_len += (block_nb + 1) << 6;
}
void sha256_final(sha256_ctx *ctx, unsigned char *digest)
{
unsigned int block_nb;
unsigned int pm_len;
unsigned int len_b;
int i;
block_nb = (1 + ((SHA256_BLOCK_SIZE - 9)
< (ctx->len % SHA256_BLOCK_SIZE)));
len_b = (ctx->tot_len + ctx->len) << 3;
pm_len = block_nb << 6;
memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
ctx->block[ctx->len] = 0x80;
UNPACK32(len_b, ctx->block + pm_len - 4);
sha256_transf(ctx, ctx->block, block_nb);
for (i = 0 ; i < 8; i++) {
UNPACK32(ctx->h[i], &digest[i << 2]);
}
}
void sha256(const unsigned char *message, unsigned int len, unsigned char *digest)
{
sha256_ctx ctx;
sha256_init(&ctx);
sha256_update(&ctx, message, len);
sha256_final(&ctx, digest);
}
TLS unsigned char one_time_key_buf[SHA256_DIGEST_SIZE];
#define MAX_DG 508
TLS unsigned char send_buf[MAX_DG];
void compute_one_time_key() {
memcpy(secret_block + 32, send_buf + 1, 4);
sha256(secret_block, 36, one_time_key_buf);
}
#define SCHEDULER_PERIOD 60
#define BUFFER_SIZE 16
#define TIMEOUT 300
#define MAX_DEPTH 7
#define PENALTY_RATE_SEC 300
#define FEE_RATE 0.02
#define COMMIT 0
#define SEAL 1
#define FINALIZE 2
#define CANCEL 3
#define CLEANUP 4
#define SYNCING_MASK 0x06
struct account {
unsigned char username[32];
unsigned char server_address[32];
unsigned short port;
unsigned char secret_key[32];
unsigned int counter_in;
unsigned int counter_out;
long long balance;
unsigned long long creditlimit;
unsigned long long creditlimit_in;
unsigned char ack_pending : 1;
float width;
unsigned long long tax_syncing;
unsigned long long sync_in;
unsigned long long sync_out;
};
struct payment {
unsigned char identifier[32];
unsigned long long amount;
signed char incoming;
signed char outgoing;
unsigned int penalty_rate;
unsigned long long fee_in;
unsigned long long fee_out;
unsigned long long commit_penalty;
unsigned long long finalize_out;
unsigned char preimage[32];
unsigned char status : 3;
unsigned char counterpart : 1;
unsigned char commit_out : 1;
unsigned char ack_preimage : 1;
unsigned char ack_sync : 1;
unsigned char synced : 1;
long long created_at;
unsigned long long tax;
};
struct receipt {
unsigned char identifier[32];
unsigned char username[32];
unsigned char server_address[32];
long long amount;
long long timestamp;
};
struct storage {
unsigned int counter;
struct account accounts[BUFFER_SIZE];
unsigned short acc_bitmap;
struct payment payments[BUFFER_SIZE];
unsigned char num_payments;
struct receipt receipts[BUFFER_SIZE];
unsigned char receipts_head;
float trust_index;
};
TLS struct storage storage;
void save_storage(void) {
FILE *fp = fopen(STORAGE_PATH, "wb");
if (!fp) return;
fwrite(&storage, sizeof(struct storage), 1, fp);
fclose(fp);
}
void load_storage() {
FILE *fp = fopen(STORAGE_PATH, "rb");
if (!fp) return;
fread(&storage, sizeof(struct storage), 1, fp);
fclose(fp);
}
signed char lookup_account_idx(unsigned char *identifier) {
unsigned short temp_bitmap = storage.acc_bitmap;
signed char i = 0;
while (temp_bitmap) {
struct account *a = &storage.accounts[i];
if ((temp_bitmap & 1) && strncmp(a->username, identifier, 32) == 0 &&
strncmp(a->server_address, identifier + 32, 32) == 0) {
return i;
}
i++;
temp_bitmap >>= 1;
};
return -1;
}
TLS struct {
unsigned char username[32];
unsigned char server_address[32];
unsigned short port;
unsigned char secret_key[32];
unsigned int counter_in;
unsigned int counter_out;
} counterpart;
struct pathfinding {
unsigned char identifier[32];
unsigned long long amount;
signed char incoming;
signed char outgoing;
unsigned int penalty_rate;
unsigned long long fee;
unsigned char hops;
unsigned char depth;
unsigned char counterpart : 1;
unsigned char in_or_out : 1;
unsigned char commit : 1;
long long timeout;
unsigned long long tax;
};
TLS struct pathfinding paths[BUFFER_SIZE+1];
TLS unsigned char num_paths;
TLS unsigned char preimage[32];
TLS unsigned long long tax_buffer[BUFFER_SIZE];
signed char get_pathfinding(unsigned char *payment_id) {
signed char idx = 0;
while (idx < num_paths) {
if (memcmp(paths[idx].identifier, payment_id, 32) == 0) {
break;
}
idx++;
}
if (idx == num_paths) {
return -1;
}
if (paths[idx].timeout < time(NULL)) {
paths[idx] = paths[--num_paths];
return -1;
}
return idx;
}
void set_commit(struct pathfinding *pf) {
pf->timeout = time(NULL) + TIMEOUT;
pf->commit = 1;
}
unsigned long long fee_in(struct pathfinding *pf) {
return ((unsigned long long)pf->hops + 1) * pf->fee;
}
unsigned long long fee_out(struct pathfinding *pf) {
return (unsigned long long)pf->hops * pf->fee;
}
unsigned long long preview_bandwidth_in(signed char account_idx) {
struct account *acc = &storage.accounts[account_idx];
long long bandwidth = (long long)acc->creditlimit - acc->balance;
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->incoming == account_idx) {
bandwidth -= (long long)(p->amount + p->fee_in + p->tax);
}
}
for (unsigned char i = 0; i < num_paths; i++) {
struct pathfinding *pf = &paths[i];
if (paths[i].timeout < time(NULL)) {
paths[i] = paths[--num_paths];
i--;
continue;
}
if (pf->incoming == account_idx && pf->commit) {
bandwidth -= (long long)(pf->amount + fee_in(pf) + pf->tax);
}
}
return (unsigned long long)(bandwidth > 0 ? bandwidth : 0);
}
unsigned long long preview_bandwidth_out(signed char account_idx) {
struct account *acc = &storage.accounts[account_idx];
long long bandwidth = (long long)acc->creditlimit_in + acc->balance;
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->outgoing == account_idx) {
bandwidth -= (long long)(p->amount + p->fee_out + p->tax);
}
}
for (unsigned char i = 0; i < num_paths; i++) {
struct pathfinding *pf = &paths[i];
if (paths[i].timeout < time(NULL)) {
paths[i] = paths[--num_paths];
i--;
continue;
}
if (pf->outgoing == account_idx && pf->commit) {
bandwidth -= (long long)(pf->amount + fee_out(pf) + pf->tax);
}
}
return (unsigned long long)(bandwidth > 0 ? bandwidth : 0);
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#define DATA_TYPE 0
#define ACK_TYPE 1
#define HEADER_SIZE 5
#define MAX_RETRIES 8
#define BASE_DELAY 1
#define MAX_DELAY 16
#define ACCOUNT_SESSION 0
#define USER_SESSION 2
#define COUNTERPART_SESSION 4
#define USER_SENDER BUFFER_SIZE
#define CPT_SENDER BUFFER_SIZE+1
#define TX_ENVELOPE (1+32+64+4+32)
#define REQ_ENVELOPE (1+4+32)
short endianess;
TLS int sockfd;
TLS time_t next_timer;
struct datagram {
unsigned char payload[MAX_DG];
unsigned short len;
struct sockaddr_in addr;
};
struct send_rtx {
signed char sender_idx;
struct datagram send_queue[BUFFER_SIZE];
unsigned char head;
unsigned char tail;
unsigned char size;
time_t deadline;
unsigned char retries;
};
TLS struct send_rtx send_tasks[BUFFER_SIZE+2];
TLS unsigned char num_active_sends = 0;
void remove_send_task(unsigned char idx) {
struct send_rtx* t = &send_tasks[idx];
t->head = (t->head + 1) % BUFFER_SIZE;
t->size--;
if (t->size == 0) {
send_tasks[idx] = send_tasks[num_active_sends - 1];
num_active_sends--;
return;
}
struct datagram *dg = &t->send_queue[t->head];
sendto(sockfd, dg->payload, dg->len, 0, (struct sockaddr *)&dg->addr, sizeof(struct sockaddr_in));
t->retries = 0;
time_t deadline = time(NULL) + BASE_DELAY;
t->deadline = deadline;
if (deadline < next_timer) {
next_timer = deadline;
}
}
void retransmit(time_t current_time) {
for (unsigned char i = 0; i < num_active_sends; i++) {
struct send_rtx *t = &send_tasks[i];
if (t->deadline <= current_time) {
struct datagram *dg = &t->send_queue[t->head];
sendto(sockfd, dg->payload, dg->len, 0, (struct sockaddr *)&dg->addr, sizeof(struct sockaddr_in));
t->retries++;
if (t->retries >= MAX_RETRIES) {
remove_send_task(i);
i--;
continue;
}
unsigned char delay = BASE_DELAY << t->retries;
if (delay > MAX_DELAY) delay = MAX_DELAY;
t->deadline = current_time + delay;
}
if (t->deadline < next_timer) {
next_timer = t->deadline;
}
}
}
void enqueue_send(signed char sender_idx, unsigned short len, struct sockaddr_in *addr) {
unsigned char idx = 0;
while (idx < num_active_sends) {
if (send_tasks[idx].sender_idx == sender_idx) {
break;
}
idx++;
}
struct send_rtx* t = &send_tasks[idx];
if (idx == num_active_sends) {
memset(&t->head, 0, sizeof(struct send_rtx) - ((char*)&t->head-(char*)t));
t->sender_idx = sender_idx;
num_active_sends++;
} else if (t->size >= BUFFER_SIZE) {
return;
}
struct datagram *slot = &t->send_queue[t->tail];
memcpy(slot->payload, send_buf, len);
slot->len = len;
slot->addr = *addr;
t->tail = (t->tail + 1) % BUFFER_SIZE;
t->size++;
if (t->size == 1) {
time_t deadline = time(NULL) + BASE_DELAY;
t->deadline = deadline;
if (deadline < next_timer) {
next_timer = deadline;
}
sendto(sockfd, slot->payload, slot->len, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
}
}
#include <netdb.h>
int create_sockaddr_in(const char *addr_str, unsigned short port, struct sockaddr_in *addr) {
memset(addr, 0, sizeof(struct sockaddr_in));
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
if (inet_pton(AF_INET, addr_str, &addr->sin_addr) == 1) {
return 0;
} else {
struct hostent *host = gethostbyname(addr_str);
if (host == NULL) {
return -1;
}
memcpy(&addr->sin_addr, host->h_addr, host->h_length);
return 0;
}
}
int verify_signature(unsigned char *data, unsigned short len, unsigned char *secret_key) {
unsigned char hash[SHA256_DIGEST_SIZE];
unsigned char signature[32];
memcpy(signature, data + len - 32, 32);
memcpy(data + len - 32, secret_key, 32);
sha256(data, len, hash);
return memcmp(hash, signature, 32) == 0;
}
void sign_data(unsigned short len, unsigned char *secret_key) {
unsigned char signature[SHA256_DIGEST_SIZE];
memcpy(send_buf + len, secret_key, 32);
sha256(send_buf, len+32, signature);
memcpy(send_buf + len, signature, 32);
}
void bswapn(unsigned char* dst, unsigned char* src, unsigned char n) {
unsigned char fwd = 0;
unsigned char rev = n - 1;
unsigned char half = n/2;
while (fwd < half) {
dst[fwd] = src[rev];
dst[rev] = src[fwd];
fwd++;
rev--;
}
}
void ntohn(void* dst, unsigned char* src, unsigned char n, unsigned char size) {
unsigned char *dst_c = (unsigned char*)dst;
unsigned char padding = size-n;
if (endianess) {
bswapn(dst_c, src, n);
dst_c = dst_c+n;
} else {
memcpy(dst_c+padding, src, n);
}
if (padding) {
memset(dst_c, 0, padding);
}
}
void htonn(void* src, unsigned char* dst, unsigned char n, unsigned char size) {
unsigned char *src_c = (unsigned char*)src;
if (endianess) {
bswapn(dst, src_c, n);
} else {
memcpy(dst, src_c+size-n, n);
}
}
unsigned short error_message(unsigned char *msg, unsigned short len) {
send_buf[5] = 1;
memcpy(send_buf + 6, msg, len);
return len + 1;
}
unsigned short success_message(unsigned char *msg, unsigned short len) {
send_buf[5] = 0;
memcpy(send_buf + 6, msg, len);
return len + 1;
}
unsigned short add_account(unsigned char *data) {
unsigned char *id = data;
unsigned char *portBytes = data+64;
unsigned char *acc_secret_key = data + 66;
if (lookup_account_idx(id) != -1) {
return error_message("Peer already exists", 20);
}
signed char account_idx = 0;
unsigned short temp_bitmap = storage.acc_bitmap;
while (temp_bitmap) {
if ((temp_bitmap & 1) == 0) {
break;
}
account_idx++;
temp_bitmap >>= 1;
}
if (account_idx == BUFFER_SIZE) {
return error_message("Peer slots full", 16);
}
compute_one_time_key();
for (unsigned char i = 0; i < 32; i++) {
acc_secret_key[i] ^= one_time_key_buf[i];
}
struct account *acc = &storage.accounts[account_idx];
strncpy(acc->username, id, 32);
strncpy(acc->server_address, id + 32, 31);
acc->server_address[31] = '\0';
ntohn(&acc->port, portBytes, 2, sizeof(short));
memcpy(acc->secret_key, acc_secret_key, 32);
memset(&acc->counter_in, 0, sizeof(struct account) - ((char*)&acc->counter_in - (char*)acc));
storage.acc_bitmap |= (1 << account_idx);
save_storage();
return success_message("Add account", 12);
}
unsigned short remove_account(unsigned char *data) {
unsigned char *id = data;
signed char account_idx = lookup_account_idx(id);
if (account_idx == -1) {
return error_message("Peer does not exist", 20);
}
for (unsigned char i = 0; i < num_paths; ) {
struct pathfinding *pf = &paths[i];
if (pf->incoming == account_idx || pf->outgoing == account_idx) {
paths[i] = paths[--num_paths];
} else {
i++;
}
}
for (unsigned char i = 0; i < storage.num_payments; ) {
struct payment *p = &storage.payments[i];
if (p->incoming == account_idx) {
p->incoming = -1;
}
if (p->outgoing == account_idx) {
p->outgoing = -1;
}
if (p->incoming == -1 && p->outgoing == -1) {
storage.payments[i] = storage.payments[--storage.num_payments];
} else {
i++;
}
}
storage.acc_bitmap &= ~(1 << account_idx);
save_storage();
return success_message("Remove account", 15);
}
void tx_header(unsigned char session_type, unsigned char * username) {
send_buf[0] = session_type;
strncpy(send_buf+1, username, 32);
strcpy(send_buf+33, USERNAME);
strcpy(send_buf+65, SERVER_ADDRESS);
}
void account_sign_and_send(signed char account_idx, unsigned short len) {
struct account *acc = &storage.accounts[account_idx];
tx_header(ACCOUNT_SESSION, acc->username);
acc->counter_out++;
save_storage();
htonn(&acc->counter_out, send_buf+97, 4, sizeof(int));
sign_data(TX_ENVELOPE + len - 32, acc->secret_key);
struct sockaddr_in addr;
create_sockaddr_in(acc->server_address, acc->port, &addr);
enqueue_send(account_idx, TX_ENVELOPE + len, &addr);
}
void cpt_sign_and_send(unsigned char cmd) {
tx_header(COUNTERPART_SESSION, counterpart.username);
counterpart.counter_out++;
htonn(&counterpart.counter_out, send_buf + 97, 4, sizeof(int));
send_buf[101] = cmd;
sign_data(TX_ENVELOPE + 1 - 32, counterpart.secret_key);
struct sockaddr_in addr;
create_sockaddr_in(counterpart.server_address, counterpart.port, &addr);
enqueue_send(CPT_SENDER, TX_ENVELOPE + 1, &addr);
}
void account_send_32_bytes(signed char account_idx, unsigned char cmd, unsigned char* bytes) {
send_buf[101] = cmd;
memcpy(send_buf + 102, bytes, 32);
account_sign_and_send(account_idx, 1 + 32);
}
void account_send_32_1(signed char account_idx, unsigned char cmd, unsigned char* bytes, unsigned char byte) {
send_buf[101] = cmd;
memcpy(send_buf + 102, bytes, 32);
send_buf[134] = byte;
account_sign_and_send(account_idx, 1 + 32 + 1);
}
void account_send_sync_payment(signed char account_idx, unsigned char* bytes, unsigned long long amount) {
send_buf[101] = 10;
memcpy(send_buf + 102, bytes, 32);
htonn(&amount, send_buf + 134, 8, sizeof(long long));
account_sign_and_send(account_idx, 1 + 40);
}
void account_send_8(signed char account_idx, unsigned char cmd, unsigned long long val) {
send_buf[101] = cmd;
htonn(&val, send_buf + 102, 8, sizeof(long long));
account_sign_and_send(account_idx, 1 + 8);
}
void account_send_16(signed char account_idx, unsigned char cmd, unsigned long long val1, unsigned long long val2) {
send_buf[101] = cmd;
htonn(&val1, send_buf + 102, 8, sizeof(long long));
htonn(&val2, send_buf + 110, 8, sizeof(long long));
account_sign_and_send(account_idx, 1 + 16);
}
unsigned short set_trustline(unsigned char *data) {
unsigned char *id = data;
unsigned long long value;
ntohn(&value, data + 64, 8, sizeof(long long));
signed char account_idx = lookup_account_idx(id);
if (account_idx == -1) {
return error_message("Peer does not exist", 20);
}
struct account *acc = &storage.accounts[account_idx];
acc->creditlimit = value;
acc->ack_pending = 1;
save_storage();
account_send_8(account_idx, 14, value);
return success_message("Set trustline", 14);
}
unsigned short new_payment(unsigned char *data) {
unsigned char *id = data;
unsigned char *portBytes = data+64;
unsigned char *cpt_secret_key = data + 66;
unsigned long long amount;
ntohn(&amount, data+98, 8, sizeof(long long));
unsigned char in_or_out = data[106];
unsigned int penalty_rate;
ntohn(&penalty_rate, data+107, 4, sizeof(int));
unsigned long long fee;
ntohn(&fee, data+111, 8, sizeof(long long));
unsigned long long tax;
ntohn(&tax, data+119, 8, sizeof(long long));
strncpy(counterpart.username, id, 32);
strncpy(counterpart.server_address, id + 32, 31);
counterpart.server_address[31] = '\0';
ntohn(&counterpart.port, portBytes, 2, sizeof(short));
compute_one_time_key();
for (unsigned char i = 0; i < 32; i++) {
cpt_secret_key[i] ^= one_time_key_buf[i];
}
memcpy(counterpart.secret_key, cpt_secret_key, 32);
counterpart.counter_in = 0;
counterpart.counter_out = 0;
unsigned char pay_id_buf[SHA256_DIGEST_SIZE];
sha256(preimage, 32, pay_id_buf);
unsigned char idx = 0;
while (idx < num_paths) {
if (memcmp(paths[idx].identifier, pay_id_buf, 32) == 0) {
break;
}
idx++;
}
memcpy(preimage, cpt_secret_key, 32);
sha256(cpt_secret_key, 32, pay_id_buf);
struct pathfinding *path = &paths[idx];
if (idx == num_paths) {
num_paths++;
}
memcpy(path->identifier, pay_id_buf, 32);
path->amount = amount;
path->incoming = -1,
path->outgoing = -1,
path->counterpart = 1;
path->in_or_out = in_or_out;
path->penalty_rate = penalty_rate;
path->fee = fee;
path->hops = 0,
path->depth = 0,
path->commit = 0,
path->timeout = time(NULL) + TIMEOUT;
path->tax = tax;
return success_message("New payment", 12);
}
void forward_find_path(unsigned char path_idx) {
struct pathfinding *path = &paths[path_idx];
signed char from;
unsigned long long (*bandwidth_fn)(signed char);
if (path->in_or_out) {
bandwidth_fn = preview_bandwidth_in;
from = path->outgoing;
} else {
bandwidth_fn = preview_bandwidth_out;
from = path->incoming;
}
unsigned short temp_bitmap = storage.acc_bitmap;
signed char i = 0;
while (temp_bitmap) {
if (temp_bitmap & 1 && i != from && bandwidth_fn(i) >= path->amount + fee_in(path) + path->tax) {
struct account *a = &storage.accounts[i];
send_buf[101] = 0;
unsigned char *data = send_buf+102;
memcpy(data, path->identifier, 32);
htonn(&path->amount, data+32, 8, sizeof(long long));
data[40] = path->in_or_out;
htonn(&path->penalty_rate, data+41, 4, sizeof(int));
htonn(&path->fee, data+45, 8, sizeof(long long));
data[53] = path->hops;
htonn(&path->tax, data+54, 8, sizeof(long long));
account_sign_and_send(i, 1 + 62);
}
i++;
temp_bitmap >>= 1;
};
}
unsigned short start_payment(unsigned char *data) {
unsigned char pay_id_buf[SHA256_DIGEST_SIZE];
sha256(preimage, 32, pay_id_buf);
signed char idx = get_pathfinding(pay_id_buf);
if (idx == -1) {
return error_message("No active payment", 18);
}
forward_find_path(idx);
return success_message("Start payment", 14);
}
unsigned short get_num_accounts(unsigned char *data) {
unsigned short temp_bitmap = storage.acc_bitmap;
unsigned char count = 0;
while (temp_bitmap) {
if (temp_bitmap & 1) {
count++;
}
temp_bitmap >>= 1;
};
send_buf[5] = 0;
send_buf[6] = count;
return 2;
}
unsigned short get_account(unsigned char *data) {
unsigned char idx = data[0];
unsigned short temp_bitmap = storage.acc_bitmap;
unsigned char count = 0;
unsigned char account_idx = 0;
while (temp_bitmap) {
if (temp_bitmap & 1) {
if (count == idx) break;
count++;
}
temp_bitmap >>= 1;
account_idx++;
}
if (!temp_bitmap) {
return error_message("Account not found", 18);
}
struct account *a = &storage.accounts[account_idx];
send_buf[5] = 0;
unsigned char *resp_data = send_buf+6;
memcpy(resp_data, a->username, 32);
memcpy(resp_data+32, a->server_address, 32);
htonn(&a->balance, resp_data+64, 8, sizeof(long long));
htonn(&a->creditlimit, resp_data+72, 8, sizeof(long long));
htonn(&a->creditlimit_in, resp_data+80, 8, sizeof(long long));
return 89;
}
unsigned short get_payment(unsigned char *data) {
unsigned char pay_id_buf[SHA256_DIGEST_SIZE];
sha256(preimage, 32, pay_id_buf);
unsigned char idx = 0;
while (idx < num_paths) {
if (memcmp(paths[idx].identifier, pay_id_buf, 32) == 0) {
break;
}
idx++;
}
if (idx == num_paths || paths[idx].timeout < time(NULL)) {
return error_message("No active payment", 18);
}
send_buf[5] = 0;
unsigned char *resp_data = send_buf + 6;
char hex[] = "0123456789abcdef";
for (unsigned char i = 0; i < 32; i++) {
unsigned char byte = pay_id_buf[i];
resp_data[i * 2] = hex[(byte >> 4) & 0xf];
resp_data[i * 2 + 1] = hex[byte & 0xf];
}
return 65;
}
unsigned short set_trust_index(unsigned char *data) {
uint32_t bits;
ntohn(&bits, data, 4, sizeof(uint32_t));
float val = *((float*)&bits);
storage.trust_index = val;
save_storage();
return success_message("Set trust index", 15);
}
unsigned char is_path_found(struct pathfinding *pf) {
return pf->incoming != -1 && (pf->outgoing != -1 || pf->counterpart);
}
void find_path(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
unsigned long long amount;
ntohn(&amount, args + 32, 8, sizeof(long long));
unsigned char in_or_out = args[40];
unsigned int penalty_rate;
ntohn(&penalty_rate, args + 41, 4, sizeof(int));
unsigned long long fee;
ntohn(&fee, args + 45, 8, sizeof(long long));
unsigned char hops = args[53];
unsigned long long tax;
ntohn(&tax, args + 54, 8, sizeof(long long));
if (!in_or_out && preview_bandwidth_in(account_idx) < amount) {
return;
}
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) {
if (amount == 0 || penalty_rate < PENALTY_RATE_SEC / 2 || (float)fee / (float)amount < FEE_RATE / 2) {
printf("11111111111111");
return;
}
float tax_rate = (float)tax / (float)amount;
if(tax_rate < storage.trust_index / 2 || tax_rate > storage.trust_index * 1.5) {
return;
}
if (num_paths >= BUFFER_SIZE - storage.num_payments) {
return;
}
pf_idx = num_paths++;
struct pathfinding *pf = &paths[pf_idx];
memcpy(pf->identifier, payment_id, 32);
pf->amount = amount;
pf->counterpart = 0;
pf->in_or_out = in_or_out;
pf->penalty_rate = penalty_rate;
pf->fee = fee;
pf->depth = 0;
pf->commit = 0;
pf->timeout = time(NULL) + TIMEOUT;
pf->tax = tax;
if (!in_or_out) {
pf->incoming = account_idx;
pf->outgoing = -1;
pf->hops = 0;
} else {
pf->incoming = -1;
pf->outgoing = account_idx;
pf->hops = hops+1;
}
account_send_32_1(account_idx, 1, payment_id, 0);
return;
}
struct pathfinding *pf = &paths[pf_idx];
if (pf->commit || is_path_found(pf) || pf->depth >= MAX_DEPTH) {
return;
}
if (in_or_out != pf->in_or_out) {
if (amount != pf->amount ||
penalty_rate != pf->penalty_rate ||
fee != pf->fee ||
tax != pf->tax) {
return;
}
if (!in_or_out) {
pf->incoming = account_idx;
} else {
pf->outgoing = account_idx;
pf->hops = hops + 1;
}
if (pf->counterpart) {
if (!pf->in_or_out) {
set_commit(pf);
account_send_32_bytes(account_idx, 3, payment_id);
return;
}
}
account_send_32_1(pf->incoming, 2, payment_id, pf->hops);
return;
}
if (!in_or_out && pf->incoming == account_idx || in_or_out && pf->outgoing == account_idx) {
forward_find_path(pf_idx);
}
}
void path_recurse(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
unsigned char depth = args[32];
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) {
return;
}
struct pathfinding *pf = &paths[pf_idx];
if (pf->commit || is_path_found(pf) || depth < pf->depth || depth >= MAX_DEPTH) {
return;
}
pf->depth = depth + 1;
if (pf->counterpart) {
cpt_sign_and_send(0);
return;
}
signed char prev_idx;
if (!pf->in_or_out) {
prev_idx = pf->incoming;
} else {
prev_idx = pf->outgoing;
}
account_send_32_1(prev_idx, 1, payment_id, pf->depth);
}
void path_found(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
unsigned char hops = args[32];
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) {
return;
}
struct pathfinding *pf = &paths[pf_idx];
if (is_path_found(pf) || pf->commit) {
return;
}
if (pf->counterpart) {
if (pf->in_or_out) {
return;
}
pf->outgoing = account_idx;
pf->hops = hops + 1;
set_commit(pf);
account_send_32_bytes(account_idx, 3, payment_id);
return;
}
if (pf->incoming == -1) {
return;
}
pf->outgoing = account_idx;
pf->hops = hops + 1;
account_send_32_1(pf->incoming, 2, payment_id, pf->hops);
}
void prepare_path(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) {
return;
}
struct pathfinding *pf = &paths[pf_idx];
if (pf->commit || preview_bandwidth_in(account_idx) < pf->amount + fee_in(pf) + pf->tax) {
return;
}
pf->incoming = account_idx;
set_commit(pf);
if (pf->counterpart) {
if (!pf->in_or_out) {
return;
}
cpt_sign_and_send(1);
return;
}
if (pf->outgoing == -1) {
return;
}
account_send_32_bytes(pf->outgoing, 3, payment_id);
}
void commit_payment(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) {
return;
}
struct pathfinding *pf = &paths[pf_idx];
if (!pf->commit || account_idx != pf->incoming) {
return;
}
struct payment *payment = &storage.payments[storage.num_payments];
storage.num_payments++;
memcpy(payment->identifier, payment_id, 32);
payment->amount = pf->amount;
payment->incoming = account_idx;
payment->penalty_rate = pf->penalty_rate;
payment->fee_in = fee_in(pf);
payment->created_at = time(NULL);
payment->tax = pf->tax;
if (pf->counterpart) {
payment->counterpart = 1;
payment->outgoing = -1;
memcpy(payment->preimage, preimage, 32);
save_storage();
paths[pf_idx] = paths[--num_paths];
cpt_sign_and_send(2);
return;
}
paths[pf_idx] = paths[--num_paths];
payment->outgoing = pf->outgoing;
payment->fee_out = fee_out(pf);
payment->status = COMMIT;
save_storage();
account_send_32_bytes(pf->outgoing, 4, payment_id);
}
unsigned long long penalty_ticker(struct payment *payment) {
long long now = time(NULL);
if (now > payment->created_at && payment->penalty_rate > 0) {
unsigned long long ticker = (unsigned long long)(now - payment->created_at) / (unsigned long long)payment->penalty_rate;
return ticker > payment->amount ? payment->amount : ticker;
}
return 0;
}
signed char get_payment_idx(unsigned char *payment_id) {
for (signed char i = 0; i < storage.num_payments; i++) {
if (memcmp(storage.payments[i].identifier, payment_id, 32) == 0) {
return i;
}
}
return -1;
}
void seal_payment(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (payment->status != COMMIT || payment->incoming != account_idx) {
return;
}
unsigned long long ticker = penalty_ticker(payment);
payment->commit_penalty = ticker;
if (ticker >= payment->amount) {
payment->status = CLEANUP;
save_storage();
account_send_32_bytes(account_idx, 8, payment_id);
if (payment->outgoing != -1) {
payment->finalize_out = payment->amount + payment->fee_out + payment->tax;
save_storage();
account_send_32_bytes(payment->outgoing, 8, payment_id);
}
return;
}
if (payment->counterpart) {
payment->status = FINALIZE;
save_storage();
account_send_32_bytes(account_idx, 6, payment->preimage);
return;
}
payment->status = SEAL;
save_storage();
if (payment->outgoing != -1) {
account_send_32_bytes(payment->outgoing, 5, payment_id);
}
}
unsigned long long seal_penalty(struct payment *payment, unsigned long long ticker) {
return ticker > payment->commit_penalty ? ticker - payment->commit_penalty : 0;
}
unsigned long long finalize_out(struct payment *payment) {
unsigned long long ticker = penalty_ticker(payment);
unsigned long long amount = payment->amount - seal_penalty(payment, ticker);
unsigned long long tax = payment->tax * amount / payment->amount;
unsigned long long fee = payment->fee_out * ticker / payment->amount;
return amount + tax + fee;
}
static void send_ack_preimage(signed char sender_idx, unsigned char *payment_id) {
account_send_32_bytes(sender_idx, 9, payment_id);
}
void finalize_payment(signed char account_idx, unsigned char *args) {
unsigned char *preimage_arg = args;
unsigned char payment_id[32];
sha256(preimage_arg, 32, payment_id);
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
send_ack_preimage(account_idx, payment_id);
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (payment->status == FINALIZE || payment->status == CLEANUP) {
send_ack_preimage(account_idx, payment_id);
return;
}
if (payment->status != SEAL || payment->outgoing != account_idx) {
return;
}
payment->finalize_out = finalize_out(payment);
payment->status = FINALIZE;
memcpy(payment->preimage, preimage_arg, 32);
save_storage();
account_send_sync_payment(account_idx, payment_id, payment->finalize_out);
send_ack_preimage(account_idx, payment_id);
if (payment->incoming != -1) {
account_send_32_bytes(payment->incoming, 6, preimage_arg);
}
}
unsigned long long cancel_out(struct payment *payment) {
unsigned long long ticker = penalty_ticker(payment);
unsigned long long total = payment->amount + payment->fee_out;
if (ticker >= payment->amount) {
return total;
} else {
return total * ticker / payment->amount;
}
}
void cancel_payment(signed char account_idx, unsigned char *args) {
unsigned char *preimage_arg = args;
unsigned char payment_id[32];
sha256(preimage_arg, 32, payment_id);
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
send_ack_preimage(account_idx, payment_id);
return;
}
struct payment *payment = &storage.payments[pay_idx];
unsigned char status = payment->status;
if (status == CANCEL || status == CLEANUP) {
send_ack_preimage(account_idx, payment_id);
return;
}
if (status != COMMIT || payment->incoming != account_idx) {
return;
}
memcpy(payment->preimage, preimage_arg, 32);
payment->status = CANCEL;
if (payment->outgoing != -1) {
payment->finalize_out = cancel_out(payment);
}
save_storage();
if (payment->outgoing != -1) {
account_send_32_bytes(payment->outgoing, 7, payment->preimage);
}
send_ack_preimage(account_idx, payment_id);
}
unsigned long long cleanup_out(struct payment *payment) {
return payment->amount - seal_penalty(payment, payment->amount) + payment->fee_out;
}
void cleanup_payment(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
return;
}
struct payment *payment = &storage.payments[pay_idx];
if ((payment->status & SYNCING_MASK) || penalty_ticker(payment) < payment->amount) {
return;
}
if (payment->status == COMMIT) {
payment->commit_penalty = payment->amount;
}
payment->status = CLEANUP;
if (payment->outgoing != -1) {
payment->finalize_out = cleanup_out(payment);
save_storage();
if (payment->outgoing != account_idx) {
account_send_32_bytes(payment->outgoing, 8, payment_id);
}
}
save_storage();
if (payment->incoming != -1 && payment->incoming != account_idx) {
account_send_32_bytes(payment->incoming, 8, payment_id);
}
}
unsigned char counterpart_in(struct payment *payment) {
return payment->counterpart && payment->incoming == -1;
}
unsigned char synchronized_in(struct payment *payment) {
unsigned char ack_preimage = payment->status != FINALIZE || payment->ack_preimage;
return payment->synced && ack_preimage || counterpart_in(payment);
}
unsigned char synchronized_out(struct payment *payment) {
unsigned char ack_preimage = payment->status != CANCEL || payment->ack_preimage;
unsigned char commit_out = payment->status == FINALIZE || payment->commit_out;
return payment->ack_sync && ack_preimage && commit_out || payment->counterpart && payment->outgoing == -1;
}
void ack_preimage(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
return;
}
struct payment *payment = &storage.payments[pay_idx];
unsigned char status = payment->status;
if (payment->ack_preimage ||
!((status == FINALIZE && payment->incoming == account_idx) ||
(status == CANCEL && payment->outgoing == account_idx))) {
return;
}
payment->ack_preimage = 1;
if (synchronized_in(payment)) {
payment->incoming = -1;
}
if (synchronized_out(payment)) {
payment->outgoing = -1;
}
if (payment->incoming == -1 && payment->outgoing == -1) {
storage.payments[pay_idx] = storage.payments[--storage.num_payments];
}
save_storage();
}
void write_receipt(unsigned char* payment_id, long long amount) {
struct receipt *r = &storage.receipts[storage.receipts_head];
storage.receipts_head = (storage.receipts_head + 1) % BUFFER_SIZE;
save_storage();
memcpy(r->identifier, payment_id, 32);
r->amount = amount;
r->timestamp = time(NULL);
unsigned char pay_id_buf[32];
sha256(counterpart.secret_key, 32, pay_id_buf);
if (memcmp(pay_id_buf, payment_id, 32) == 0) {
memcpy(r->username, counterpart.username, 32);
memcpy(r->server_address, counterpart.server_address, 32);
} else {
memset(r->username, 0, 64);
}
}
long long preview_balance(signed char account_idx) {
struct account *acc = &storage.accounts[account_idx];
long long bal = acc->balance;
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->incoming == account_idx) {
bal -= (long long)(p->amount + p->fee_in + p->tax);
}
}
for (unsigned char i = 0; i < num_paths; i++) {
struct pathfinding *pf = &paths[i];
if (pf->timeout < time(NULL)) {
paths[i] = paths[--num_paths];
i--;
continue;
}
if (pf->incoming == account_idx && pf->commit) {
bal -= (long long)(pf->amount + fee_in(pf) + pf->tax);
}
}
return bal;
}
unsigned long long calculate_tax(struct payment *payment) {
return payment->tax * (payment->amount - seal_penalty(payment, penalty_ticker(payment)))/payment->amount;
}
void redistribute(signed char from_idx, unsigned long long tax, float tax_rate) {
if (tax == 0) return;
struct { signed char idx; unsigned long long val; } positive_balances[BUFFER_SIZE];
unsigned char num_positive = 0;
float total_width = 0.0;
unsigned short temp_bitmap = storage.acc_bitmap;
signed char i = 0;
while (temp_bitmap) {
if (temp_bitmap & 1 && i != from_idx) {
long long preview = preview_balance(i);
if (preview > 0) {
positive_balances[num_positive].idx = i;
positive_balances[num_positive].val = (unsigned long long)preview;
total_width += storage.accounts[i].width;
num_positive++;
}
}
i++;
temp_bitmap >>= 1;
}
if (total_width == 0.0) return;
if (total_width < tax_rate) {
tax = (unsigned long long)((float)tax * (total_width / tax_rate));
}
for (unsigned char j = 0; j < num_positive; j++) {
signed char idx = positive_balances[j].idx;
struct account *acc = &storage.accounts[idx];
float proportion = acc->width / total_width;
unsigned long long dist = (unsigned long long)((float)tax * proportion);
if (dist == 0) continue;
if (dist > positive_balances[j].val) dist = positive_balances[j].val;
tax_buffer[idx] += dist;
if (acc->tax_syncing == 0) {
acc->sync_out++;
acc->tax_syncing = tax_buffer[idx];
save_storage();
account_send_16(idx, 16, acc->tax_syncing, acc->sync_out);
tax_buffer[idx] = 0;
}
}
}
float get_tax_rate(struct payment *p) {
if (p->tax == 0) {
return 0;
}
return (float)p->tax / (float)(p->amount + p->tax);
}
void increase_balance(signed char account_idx, unsigned long long amount, float tax_rate) {
struct account *acc = &storage.accounts[account_idx];
long long old_balance = acc->balance;
acc->balance += (long long)amount;
long long new_balance = acc->balance;
if (new_balance > 0) {
float weight;
if (old_balance > 0) {
weight = (float)old_balance / (float)new_balance;
acc->width = weight * acc->width + (1 - weight) * tax_rate;
} else {
acc->width = tax_rate;
tax_buffer[account_idx] = 0;
}
}
}
void decrease_balance(signed char account_idx, unsigned long long amount, float tax_rate) {
struct account *acc = &storage.accounts[account_idx];
long long old_balance = acc->balance;
acc->balance -= (long long)amount;
long long new_balance = acc->balance;
if (new_balance < 0) {
float weight;
if (old_balance < 0) {
weight = (float)(-old_balance) / (float)(-new_balance);
acc->width = weight * acc->width + (1 - weight) * tax_rate;
} else {
acc->width = tax_rate;
}
}
}
void sync_payment(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
unsigned long long amount;
ntohn(&amount, args + 32, 8, sizeof(long long));
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
account_send_32_bytes(account_idx, 11, payment_id);
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (!(payment->status & SYNCING_MASK) || (payment->incoming != -1 && payment->incoming != account_idx) || amount > payment->amount + payment->fee_in + payment->tax) {
return;
}
if (payment->incoming == account_idx && !payment->synced) {
float tax_rate = get_tax_rate(payment);
increase_balance(payment->incoming, amount, tax_rate);
payment->synced = 1;
if (synchronized_in(payment)) {
payment->incoming = -1;
}
if (payment->status == FINALIZE && payment->counterpart) {
write_receipt(payment_id, (long long)payment->amount);
}
save_storage();
redistribute(account_idx, calculate_tax(payment), tax_rate);
}
if (payment->incoming == -1 && payment->outgoing == -1) {
storage.payments[pay_idx] = storage.payments[--storage.num_payments];
save_storage();
}
account_send_32_bytes(account_idx, 11, payment_id);
}
void ack_sync(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (!(payment->status & SYNCING_MASK) || payment->ack_sync || payment->outgoing != account_idx) {
return;
}
payment->ack_sync = 1;
decrease_balance(payment->outgoing, payment->finalize_out, get_tax_rate(payment));
if (payment->status == FINALIZE && payment->counterpart) {
write_receipt(payment_id, -(long long)payment->amount);
}
if (synchronized_out(payment)) {
payment->outgoing = -1;
}
if (payment->incoming == -1 && payment->outgoing == -1) {
storage.payments[pay_idx] = storage.payments[--storage.num_payments];
}
save_storage();
}
void verify_commit(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
account_send_32_1(account_idx, 13, payment_id, 1);
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (payment->incoming != -1 && payment->incoming != account_idx) {
return;
}
account_send_32_1(account_idx, 13, payment_id, 0);
}
void ack_commit(signed char account_idx, unsigned char *args) {
unsigned char *payment_id = args;
unsigned char status = args[32];
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) {
return;
}
struct payment *payment = &storage.payments[pay_idx];
if (payment->commit_out || payment->outgoing != account_idx) {
return;
}
if (status == 0) {
payment->commit_out = 1;
} else {
payment->outgoing = -1;
if (payment->incoming == -1) {
storage.payments[pay_idx] = storage.payments[--storage.num_payments];
}
}
save_storage();
}
void sync_trustline(signed char account_idx, unsigned char *args) {
unsigned long long limit;
ntohn(&limit, args, 8, sizeof(long long));
struct account *acc = &storage.accounts[account_idx];
acc->creditlimit_in = limit;
save_storage();
account_send_8(account_idx, 15, limit);
}
void ack_trustline(signed char account_idx, unsigned char *args) {
unsigned long long limit;
ntohn(&limit, args, 8, sizeof(long long));
struct account *acc = &storage.accounts[account_idx];
if (!acc->ack_pending || acc->creditlimit != limit) {
return;
}
acc->ack_pending = 0;
save_storage();
}
void swarm_redistribution(signed char account_idx, unsigned char *args) {
unsigned long long amount;
ntohn(&amount, args, 8, sizeof(long long));
unsigned long long counter;
ntohn(&counter, args + 8, 8, sizeof(long long));
struct account *acc = &storage.accounts[account_idx];
if (counter > acc->sync_in) {
if (acc->balance + (long long)amount > 0) return;
acc->balance += (long long)amount;
acc->sync_in = counter;
save_storage();
redistribute(account_idx, amount, acc->width);
}
account_send_8(account_idx, 17, counter);
}
void redistribution_ack(signed char account_idx, unsigned char *args) {
unsigned long long counter;
ntohn(&counter, args, 8, sizeof(long long));
struct account *acc = &storage.accounts[account_idx];
if (acc->tax_syncing > 0 && counter == acc->sync_out) {
acc->balance -= (long long)acc->tax_syncing;
acc->tax_syncing = 0;
save_storage();
}
}
void cpt_find_path(void) {
unsigned char payment_id[32];
sha256(counterpart.secret_key, 32, payment_id);
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) return;
forward_find_path(pf_idx);
}
void cpt_commit_payment(void) {
unsigned char payment_id[32];
sha256(counterpart.secret_key, 32, payment_id);
signed char pf_idx = get_pathfinding(payment_id);
if (pf_idx == -1) return;
struct pathfinding *pf = &paths[pf_idx];
if (!pf->counterpart || pf->in_or_out || !pf->commit) return;
struct payment *payment = &storage.payments[storage.num_payments];
storage.num_payments++;
memcpy(payment->identifier, payment_id, 32);
payment->amount = pf->amount;
payment->incoming = -1;
payment->outgoing = pf->outgoing;
payment->penalty_rate = pf->penalty_rate;
payment->fee_in = 0;
payment->fee_out = fee_out(pf);
memcpy(payment->preimage, preimage, 32);
payment->status = COMMIT;
payment->counterpart = 1;
payment->created_at = time(NULL);
payment->tax = pf->tax;
save_storage();
paths[pf_idx] = paths[--num_paths];
account_send_32_bytes(pf->outgoing, 4, payment_id);
}
void cpt_seal_payment(void) {
unsigned char payment_id[32];
sha256(counterpart.secret_key, 32, payment_id);
signed char pay_idx = get_payment_idx(payment_id);
if (pay_idx == -1) return;
struct payment *payment = &storage.payments[pay_idx];
if (payment->status != COMMIT || payment->incoming != -1) return;
unsigned long long ticker = penalty_ticker(payment);
payment->commit_penalty = ticker;
if (ticker >= payment->amount) {
payment->status = CLEANUP;
save_storage();
account_send_32_bytes(payment->outgoing, 8, payment_id);
return;
}
payment->status = SEAL;
save_storage();
account_send_32_bytes(payment->outgoing, 5, payment_id);
}
unsigned short (*user_handlers[])(unsigned char*) = {
add_account, remove_account, set_trustline, new_payment,
start_payment, get_num_accounts, get_account, get_payment,
set_trust_index
};
void (*acc_handlers[])(signed char, unsigned char*) = {
find_path, path_recurse, path_found, prepare_path, commit_payment, seal_payment,
finalize_payment, cancel_payment, cleanup_payment, ack_preimage, sync_payment,
ack_sync, verify_commit, ack_commit, sync_trustline, ack_trustline,
swarm_redistribution, redistribution_ack
};
void (*cpt_handlers[])(void) = { cpt_find_path, cpt_commit_payment, cpt_seal_payment };
unsigned short acc_args_len[] = {62, 33, 33, 32, 32, 32, 32, 32, 32, 32, 40, 32, 32, 33, 8, 8, 16, 8};
unsigned short user_args_len[] = {98, 64, 72, 127, 0, 0, 1, 0, 4};
void push_finalize_routine(void) {
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->status == FINALIZE && !p->ack_preimage && p->incoming != -1) {
account_send_32_bytes(p->incoming, 6, p->preimage);
}
}
}
void push_cancel_routine(void) {
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->status == CANCEL && !p->ack_preimage && p->outgoing != -1) {
account_send_32_bytes(p->outgoing, 7, p->preimage);
}
}
}
void push_sync_payment(void) {
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (!(p->status & SYNCING_MASK) || p->outgoing == -1 || p->ack_sync) continue;
if ((p->status == CANCEL || p->status == CLEANUP) && !p->commit_out) {
account_send_32_bytes(p->outgoing, 12, p->identifier);
continue;
}
account_send_sync_payment(p->outgoing, p->identifier, p->finalize_out);
}
}
void push_sync_trustline(void) {
unsigned short temp_bitmap = storage.acc_bitmap;
signed char idx = 0;
while (temp_bitmap) {
if ((temp_bitmap & 1) && storage.accounts[idx].ack_pending) {
account_send_8(idx, 14, storage.accounts[idx].creditlimit);
}
idx++;
temp_bitmap >>= 1;
}
}
void push_redistribution_routine(void) {
unsigned short temp_bitmap = storage.acc_bitmap;
signed char idx = 0;
while (temp_bitmap) {
if ((temp_bitmap & 1) && storage.accounts[idx].tax_syncing > 0) {
account_send_16(idx, 16, storage.accounts[idx].tax_syncing, storage.accounts[idx].sync_out);
}
idx++;
temp_bitmap >>= 1;
}
}
void auto_cancel_routine(void) {
long long now = time(NULL);
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if (p->status != COMMIT || !counterpart_in(p) || now - p->created_at <= TIMEOUT) continue;
p->status = CANCEL;
save_storage();
account_send_32_bytes(p->outgoing, 7, p->preimage);
}
}
void cleanup_payment_routine(void) {
for (unsigned char i = 0; i < storage.num_payments; i++) {
struct payment *p = &storage.payments[i];
if ((p->status & SYNCING_MASK) || penalty_ticker(p) < p->amount) continue;
if (p->status == COMMIT) p->commit_penalty = p->amount;
if (p->incoming != -1) {
account_send_32_bytes(p->incoming, 8, p->identifier);
}
if (p->outgoing != -1) {
p->finalize_out = cleanup_out(p);
account_send_32_bytes(p->outgoing, 8, p->identifier);
}
p->status = CLEANUP;
save_storage();
}
}
void ack_received(signed char sender_idx, unsigned char *data, unsigned char offset) {
for (unsigned char i = 0; i < num_active_sends; i++) {
struct send_rtx *t = &send_tasks[i];
if (t->sender_idx == sender_idx) {
if (memcmp(t->send_queue[t->head].payload + offset, data+offset, 4) == 0) {
remove_send_task(i);
}
break;
}
}
}
unsigned int receive_tx(unsigned char session_type, signed char sender_idx, struct sockaddr_in *addr, unsigned char *username, unsigned char *server_address, unsigned char *secret_key, unsigned int counter_in, unsigned char *data, unsigned short len) {
if (!verify_signature(data, len, secret_key)) {
return 0;
}
if (data[0]&1) {
ack_received(sender_idx, data, 97);
return 0;
}
unsigned int counter;
ntohn(&counter, data+97, 4, sizeof(int));
if (counter < counter_in) {
return 0;
}
tx_header(session_type | 1, username);
memcpy(send_buf+97, data+97, 4);
sign_data(TX_ENVELOPE-32, secret_key);
sendto(sockfd, send_buf, TX_ENVELOPE, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
if (counter == counter_in) {
return 0;
}
return counter;
}
#if 1
#define DEBUG_PRINT_MESSAGE(addr, type, cmd) print_message(addr, type, cmd)
static const char* command_names[] = {
"ACCOUNT_FIND_PATH", "ACCOUNT_PATH_RECURSE", "ACCOUNT_PATH_FOUND", "ACCOUNT_PREPARE_PATH",
"ACCOUNT_COMMIT_PAYMENT", "ACCOUNT_SEAL_PAYMENT", "ACCOUNT_FINALIZE_PAYMENT", "ACCOUNT_CANCEL_PAYMENT",
"ACCOUNT_CLEANUP_PAYMENT", "ACCOUNT_ACK_PREIMAGE", "ACCOUNT_SYNC_PAYMENT", "ACCOUNT_ACK_SYNC",
"ACCOUNT_VERIFY_COMMIT", "ACCOUNT_ACK_COMMIT", "ACCOUNT_SYNC_TRUSTLINE", "ACCOUNT_ACK_TRUSTLINE",
"ACCOUNT_SWARM_REDISTRIBUTION", "ACCOUNT_REDISTRIBUTION_ACK", "USER_ADD_ACCOUNT", "USER_REMOVE_ACCOUNT",
"USER_SET_TRUSTLINE", "USER_NEW_PAYMENT", "USER_START_PAYMENT", "USER_GET_NUM_ACCOUNTS",
"USER_GET_ACCOUNT", "USER_GET_PAYMENT", "USER_SET_TRUST_INDEX", "COUNTERPART_FIND_PATH",
"COUNTERPART_COMMIT_PAYMENT", "COUNTERPART_SEAL_PAYMENT"
};
void print_message(struct sockaddr_in *addr, unsigned char type, unsigned char cmd) {
printf("%s:%d: %s (0x%02X)\n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), command_names[cmd + ((type&2)>>1)*18 + (type>>2)*27], cmd);
}
#else
#define DEBUG_PRINT_MESSAGE(addr, type, cmd) ((void)0)
#endif
void transport_datagram(unsigned char *data, unsigned short len, struct sockaddr_in *addr) {
if (len<1) return;
unsigned char session_type = data[0]&6;
if (session_type%4==0) {
if (len < TX_ENVELOPE) {
return;
}
if (session_type==ACCOUNT_SESSION) {
signed char account_idx = lookup_account_idx(data+33);
if (account_idx == -1) {
return;
}
struct account *acc = &storage.accounts[account_idx];
unsigned int counter = receive_tx(ACCOUNT_SESSION, account_idx, addr, acc->username, acc->server_address, acc->secret_key, acc->counter_in, data, len);
if (!counter) {
return;
}
acc->counter_in = counter;
save_storage();
unsigned char cmd = data[101];
if (cmd <= 17 && len-TX_ENVELOPE >= 1 + acc_args_len[cmd]) {
DEBUG_PRINT_MESSAGE(addr, session_type, cmd);
acc_handlers[cmd](account_idx, data+102);
}
} else {
if (strncmp(counterpart.username, data+33, 32) != 0 ||
strncmp(counterpart.server_address, data+65, 32) != 0) {
return;
}
unsigned int counter = receive_tx(COUNTERPART_SESSION, CPT_SENDER, addr, counterpart.username, counterpart.server_address, counterpart.secret_key, counterpart.counter_in, data, len);
if (!counter) {
return;
}
counterpart.counter_in = counter;
unsigned char cmd = data[101];
if (cmd <= 2) {
DEBUG_PRINT_MESSAGE(addr, session_type, cmd);
cpt_handlers[cmd]();
}
}
} else if ((len >= REQ_ENVELOPE) && (verify_signature(data, len, secret_block))) {
if (data[0]&1) {
ack_received(USER_SENDER, data, 1);
return;
}
unsigned int counter;
ntohn(&counter, data+1, 4, sizeof(int));
if (counter < storage.counter) {
return;
}
send_buf[0] = 1;
memcpy(send_buf+1, data+1, 4);
sign_data(REQ_ENVELOPE-32, secret_block);
sendto(sockfd, send_buf, REQ_ENVELOPE, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
if (counter == storage.counter) {
return;
}
storage.counter = counter;
save_storage();
unsigned short out_len;
unsigned char cmd = data[5];
if (cmd <= 8 && len-REQ_ENVELOPE >= 1 + user_args_len[cmd]) {
DEBUG_PRINT_MESSAGE(addr, session_type, cmd);
out_len = user_handlers[cmd](data+6);
} else {
out_len = error_message("Invalid instruction", 20);
}
send_buf[0] = 0;
memcpy(send_buf+1, data+1, 4);
sign_data(5+out_len, secret_block);
enqueue_send(USER_SENDER, REQ_ENVELOPE+out_len, addr);
}
}
#include <signal.h>
static volatile sig_atomic_t running = 1;
static void on_shutdown_signal(int sig) { running = 0; }
void run_loop() {
unsigned int period = 0;
struct timeval tv;
tv.tv_usec = 0;
unsigned char buf[MAX_DG];
struct sockaddr_in src_addr;
socklen_t addrlen;
while (running) {
time_t now = time(NULL);
unsigned int current_period = (unsigned int)now / SCHEDULER_PERIOD;
if (current_period > period) {
period = current_period;
int tz = __builtin_ctzl(current_period);
push_finalize_routine();
push_cancel_routine();
push_sync_payment();
push_sync_trustline();
push_redistribution_routine();
if (tz >= 1) auto_cancel_routine();
if (tz >= 2) cleanup_payment_routine();
}
next_timer = (current_period + 1) * SCHEDULER_PERIOD;
retransmit(now);
tv.tv_sec = next_timer - now;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
addrlen = sizeof(struct sockaddr_in);
ssize_t n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &addrlen);
if (n >= REQ_ENVELOPE) {
transport_datagram(buf, (unsigned short)n, &src_addr);
}
}
}
static int server_main(void) {
load_storage();
struct sockaddr_in server_addr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return 1;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
return 1;
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = on_shutdown_signal;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
fprintf(stderr, "[%s:%u] server started (storage=%s)\n",
USERNAME, (unsigned)PORT, STORAGE_PATH);
run_loop();
close(sockfd);
fprintf(stderr, "[%s:%u] server stopped\n", USERNAME, (unsigned)PORT);
return 0;
}
typedef struct {
const char *username;
const char *storage_path;
unsigned short port;
unsigned char secret32[32];
} ServerConfig;
static void *server_thread(void *arg) {
ServerConfig *cfg = (ServerConfig*)arg;
strncpy(USERNAME, cfg->username, 32); USERNAME[32] = 0;
strncpy(SERVER_ADDRESS, "127.0.0.1", 32);
strncpy(STORAGE_PATH, cfg->storage_path, PATH_MAX-1); STORAGE_PATH[PATH_MAX-1] = 0;
PORT = cfg->port;
memcpy(secret_block, cfg->secret32, 32);
server_main();
return NULL;
}
static const unsigned char SECRET_A[32] = { [0 ... 31] = 0x00 };
static const unsigned char SECRET_B[32] = { [0 ... 31] = 0x11 };
static const unsigned char SECRET_C[32] = { [0 ... 31] = 0x22 };
static const unsigned char SECRET_33[32] = { [0 ... 31] = 0x33 };
static const unsigned char SECRET_44[32] = { [0 ... 31] = 0x44 };
static const unsigned char SECRET_AC[32] = { [0 ... 31] = 0x55 };
static int ctl_sock = -1;
static void sign_user_packet(unsigned char *buf, unsigned short len_wo_sig, const unsigned char *secret32) {
unsigned char sig[SHA256_DIGEST_SIZE];
memcpy(buf + len_wo_sig, secret32, 32);
sha256(buf, len_wo_sig + 32, sig);
memcpy(buf + len_wo_sig, sig, 32);
}
static void derive_one_time_key(const unsigned char *secret32, uint32_t counter, unsigned char out32[32]) {
unsigned char tmp[36];
memcpy(tmp, secret32, 32);
htonn(&counter, tmp+32, 4, sizeof(uint32_t));
sha256(tmp, 36, out32);
}
static unsigned short build_user_request(unsigned char *out,
uint32_t counter,
unsigned char cmd,
const unsigned char *args,
unsigned short args_len,
const unsigned char *secret32)
{
out[0] = USER_SESSION | DATA_TYPE;
htonn(&counter, out+1, 4, sizeof(uint32_t));
out[5] = cmd;
if (args_len) memcpy(out + 6, args, args_len);
unsigned short len_wo_sig = (unsigned short)(1 + 4 + 1 + args_len);
sign_user_packet(out, len_wo_sig, secret32);
return (unsigned short)(len_wo_sig + 32);
}
typedef struct {
const char *ip;
unsigned short port;
unsigned char secret32[32];
uint32_t user_counter;
const char *name;
} ClientTarget;
static int send_user_cmd(const char *ip, unsigned short port,
const unsigned char *secret32,
uint32_t *ctr_io,
unsigned char cmd, const unsigned char *args, unsigned short alen,
unsigned char *resp_buf, unsigned short *resp_len)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) return -1;
if (ctl_sock < 0) {
ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
if (ctl_sock < 0) { perror("client socket"); return -1; }
struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
setsockopt(ctl_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
uint32_t ctr = ++(*ctr_io);
unsigned char packet[REQ_ENVELOPE + 256];
unsigned short len = build_user_request(packet, ctr, cmd, args, alen, secret32);
if (sendto(ctl_sock, packet, len, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("sendto");
return -1;
}
for (;;) {
struct sockaddr_in from;
socklen_t flen = sizeof(from);
ssize_t n = recvfrom(ctl_sock, resp_buf, 1024, 0, (struct sockaddr*)&from, &flen);
if (n < 0) {
return -2;
}
if (n >= REQ_ENVELOPE) {
if ((resp_buf[0] & 1) == 0) {
unsigned char ack[REQ_ENVELOPE];
ack[0] = USER_SESSION | ACK_TYPE;
memcpy(ack + 1, resp_buf + 1, 4);
sign_user_packet(ack, REQ_ENVELOPE - 32, secret32);
sendto(ctl_sock, ack, REQ_ENVELOPE, 0, (struct sockaddr*)&addr, sizeof(addr));
*resp_len = (unsigned short)n;
return 0;
}
}
}
}
static int cmd_add_account(ClientTarget *T,
const char *peer_user, unsigned short peer_port,
const unsigned char peer_secret32[32])
{
unsigned char args[64 + 2 + 32];
memset(args, 0, sizeof(args));
strncpy((char*)args, peer_user, 32);
strncpy((char*)args + 32, "127.0.0.1", 32);
htonn(&peer_port, args + 64, 2, sizeof(short));
unsigned char otk[32];
derive_one_time_key(T->secret32, T->user_counter + 1, otk);
for (int i = 0; i < 32; i++) args[66 + i] = (unsigned char)(peer_secret32[i] ^ otk[i]);
unsigned char resp[1024]; unsigned short rn = 0;
int rc = send_user_cmd(T->ip, T->port, T->secret32, &T->user_counter, 0, args, sizeof(args), resp, &rn);
if (rc == 0) {
if (rn >= REQ_ENVELOPE + 1 && resp[5] == 0) {
printf("[auto] %s: add_account('%s',%u) → OK\n", T->name, peer_user, (unsigned)peer_port);
} else {
printf("[auto] %s: add_account('%s',%u) → ERROR\n", T->name, peer_user, (unsigned)peer_port);
}
} else {
printf("[auto] %s: add_account('%s',%u) → rc=%d\n", T->name, peer_user, (unsigned)peer_port, rc);
}
return rc;
}
static int cmd_set_trustline(ClientTarget *T, const char *peer_user, unsigned long long limit)
{
unsigned char args[64 + 8];
memset(args, 0, sizeof(args));
strncpy((char*)args, peer_user, 32);
strncpy((char*)args + 32, "127.0.0.1", 32);
htonn(&limit, args + 64, 8, sizeof(long long));
unsigned char resp[1024]; unsigned short rn = 0;
int rc = send_user_cmd(T->ip, T->port, T->secret32, &T->user_counter,
2 /* USER_SET_TRUSTLINE */, args, sizeof(args),
resp, &rn);
if (rc == 0) {
if (rn >= REQ_ENVELOPE + 1 && resp[5] == 0) {
printf("[auto] %s: set_trustline('%s', %llu) → OK\n",
T->name, peer_user, (unsigned long long)limit);
} else {
printf("[auto] %s: set_trustline('%s', %llu) → ERROR\n",
T->name, peer_user, (unsigned long long)limit);
}
} else {
printf("[auto] %s: set_trustline('%s', %llu) → rc=%d\n",
T->name, peer_user, (unsigned long long)limit, rc);
}
return rc;
}
static int cmd_new_payment(ClientTarget *T,
const char *cp_user, unsigned short cp_port,
const unsigned char cp_secret32[32],
unsigned long long amount,
unsigned char in_or_out,
unsigned int penalty_rate,
unsigned long long fee,
unsigned long long tax)
{
unsigned char args[127];
memset(args, 0, sizeof(args));
strncpy((char*)args, cp_user, 32);
strncpy((char*)args + 32, "127.0.0.1", 32);
htonn(&cp_port, args + 64, 2, sizeof(unsigned short));
unsigned char otk[32];
derive_one_time_key(T->secret32, T->user_counter + 1, otk);
for (int i = 0; i < 32; i++) args[66 + i] = (unsigned char)(cp_secret32[i] ^ otk[i]);
htonn(&amount, args + 98, 8, sizeof(unsigned long long));
args[106] = in_or_out;
htonn(&penalty_rate, args + 107, 4, sizeof(unsigned int));
htonn(&fee, args + 111, 8, sizeof(unsigned long long));
htonn(&tax, args + 119, 8, sizeof(unsigned long long));
unsigned char resp[1024]; unsigned short rn = 0;
int rc = send_user_cmd(T->ip, T->port, T->secret32, &T->user_counter,
3, args, sizeof(args), resp, &rn);
if (rc == 0) {
printf("[auto] %s: new_payment(cp=%s:%u, amount=%llu, in_or_out=%u, fee=%llu, tax=%llu) → %s\n",
T->name, cp_user, (unsigned)cp_port,
(unsigned long long)amount, (unsigned)in_or_out,
(unsigned long long)fee, (unsigned long long)tax,
(rn >= REQ_ENVELOPE + 1 && resp[5] == 0) ? "OK" : "ERROR");
}
return rc;
}
static int cmd_start_payment(ClientTarget *T)
{
unsigned char resp[1024]; unsigned short rn = 0;
int rc = send_user_cmd(T->ip, T->port, T->secret32, &T->user_counter,
4, NULL, 0, resp, &rn);
if (rc == 0) {
printf("[auto] %s: start_payment → %s\n",
T->name, (rn >= REQ_ENVELOPE + 1 && resp[5] == 0) ? "OK" : "ERROR");
}
return rc;
}
int main(void) {
if ((unsigned char)~0 != 0xFF) return 1;
short tmp = 0x0001;
endianess = *(char*)&tmp;
ServerConfig cfg[3] = {
{ "Alice", "/tmp/ripple_A.bin", 2012, {0} },
{ "Bob", "/tmp/ripple_B.bin", 2013, {0} },
{ "Carol", "/tmp/ripple_C.bin", 2014, {0} }
};
memcpy(cfg[0].secret32, SECRET_A, 32);
memcpy(cfg[1].secret32, SECRET_B, 32);
memcpy(cfg[2].secret32, SECRET_C, 32);
pthread_t th[3];
for (int i = 0; i < 3; i++) {
if (pthread_create(&th[i], NULL, server_thread, &cfg[i]) != 0) {
perror("pthread_create");
running = 0;
for (int j = 0; j < i; j++) pthread_kill(th[j], SIGTERM);
for (int j = 0; j < i; j++) pthread_join(th[j], NULL);
return 1;
}
}
usleep(200 * 1000);
ClientTarget T[3] = {
{ "127.0.0.1", cfg[0].port, {0}, 0, cfg[0].username },
{ "127.0.0.1", cfg[1].port, {0}, 0, cfg[1].username },
{ "127.0.0.1", cfg[2].port, {0}, 0, cfg[2].username }
};
memcpy(T[0].secret32, cfg[0].secret32, 32);
memcpy(T[1].secret32, cfg[1].secret32, 32);
memcpy(T[2].secret32, cfg[2].secret32, 32);
cmd_add_account(&T[0], "Bob", cfg[1].port, SECRET_33);
cmd_add_account(&T[1], "Alice", cfg[0].port, SECRET_33);
cmd_add_account(&T[1], "Carol", cfg[2].port, SECRET_44);
cmd_add_account(&T[2], "Bob", cfg[1].port, SECRET_44);
printf("[auto] added accounts (Alice<->Bob key=33.., Bob<->Carol key=44..)\n");
cmd_set_trustline(&T[1], "Alice", 10000ULL);
cmd_set_trustline(&T[2], "Bob", 10000ULL);
printf("[auto] trustlines set (Bob->Alice=10000, Carol->Bob=10000)\n");
const unsigned long long amount = 1000ULL;
const unsigned long long fee = 20ULL;
const unsigned long long tax = 0ULL;
const unsigned int penalty= 300U;
cmd_new_payment(&T[0], "Carol", cfg[2].port, SECRET_AC, amount, 0 , penalty, fee, tax);
cmd_new_payment(&T[2], "Alice", cfg[0].port, SECRET_AC, amount, 1, penalty, fee, tax);
cmd_start_payment(&T[2]);
printf("[auto] registered new_payment (A:out, C:in), start_payment from C.\n");
for (int i = 0; i < 3; i++) pthread_join(th[i], NULL);
if (ctl_sock >= 0) close(ctl_sock);
return 0;
}