Browse Source

#213 Add tx history cache for ETH/ERC20 (getting from Etherscan).

blackjok3r
Artem Pikulin 6 years ago
parent
commit
d205788777
  1. 10
      iguana/exchanges/LP_commands.c
  2. 103
      iguana/exchanges/LP_etomic.c
  3. 2
      iguana/exchanges/LP_etomic.h
  4. 3
      iguana/exchanges/LP_include.h
  5. 4
      iguana/exchanges/LP_rpc.c
  6. 10
      iguana/exchanges/LP_socket.c
  7. 66
      iguana/exchanges/etomicswap/etomiccurl.c
  8. 15
      iguana/exchanges/etomicswap/etomiccurl.h
  9. 9
      iguana/exchanges/etomicswap/etomiclib.cpp
  10. 2
      iguana/exchanges/etomicswap/etomiclib.h

10
iguana/exchanges/LP_commands.c

@ -634,6 +634,8 @@ version\n\
if (ptr->decimals == 0) {
return(clonestr("{\"error\":\"Could not get token decimals or token has zero decimals which is not supported!\"}"));
}
} else if (strcmp(coin, "ETH") == 0) {
ptr->decimals = 18;
}
}
#endif
@ -641,6 +643,14 @@ version\n\
{
cJSON *array;
ptr->inactive = 0;
#ifndef NOTETOMIC
if (ptr->etomic[0] != 0 && OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_etomic_txhistory_loop,(void *)ptr) != 0 )
{
printf("error launching LP_etomic_txhistory_loop %s\n",ptr->symbol);
ptr->inactive = (uint32_t)time(NULL);
return(clonestr("{\"error\":\"couldnt launch tx history thread\"}"));
}
#endif
LP_unspents_load(coin,ptr->smartaddr);
if ( strcmp(ptr->symbol,"KMD") == 0 )
LP_importaddress("KMD",BOTS_BONDADDRESS);

103
iguana/exchanges/LP_etomic.c

@ -73,7 +73,7 @@ uint8_t LP_etomic_verify_alice_fee(struct basilisk_swap *swap)
printf("Alice fee %s was sent to wrong address %s\n", swap->otherfee.I.ethTxid, data.to);
return(0);
}
uint64_t txValue = weiToSatoshi(data.valueHex);
uint64_t txValue = weiToSatoshi(data.valueHex, 18);
if (txValue != LP_DEXFEE(swap->I.alicerealsat)) {
printf("Alice fee %s amount %" PRIu64 " is not equal to expected %" PRId64 "\n", swap->otherfee.I.ethTxid, txValue, LP_DEXFEE(swap->I.alicerealsat));
return(0);
@ -170,7 +170,7 @@ uint8_t LP_etomic_verify_alice_payment(struct basilisk_swap *swap, char *txId)
}
AliceSendsEthPaymentInput input; AliceSendsErc20PaymentInput input20;
if ( strcmp(swap->I.alicestr,"ETH") == 0 ) {
uint64_t paymentAmount = weiToSatoshi(data.valueHex);
uint64_t paymentAmount = weiToSatoshi(data.valueHex, 18);
if (paymentAmount != swap->I.alicerealsat) {
printf("Alice payment amount %" PRIu64 " does not match expected %" PRIu64 "\n", paymentAmount, swap->I.alicerealsat);
return(0);
@ -380,7 +380,7 @@ uint8_t LP_etomic_verify_bob_deposit(struct basilisk_swap *swap, char *txId)
memset(&input,0,sizeof(input));
memset(&input20,0,sizeof(input20));
if ( strcmp(swap->I.bobstr,"ETH") == 0 ) {
uint64_t depositAmount = weiToSatoshi(data.valueHex);
uint64_t depositAmount = weiToSatoshi(data.valueHex, 18);
if (depositAmount != LP_DEPOSITSATOSHIS(swap->I.bobrealsat)) {
printf("Bob deposit %s amount %" PRIu64 " != expected %" PRIu64 "\n", txId, depositAmount, LP_DEPOSITSATOSHIS(swap->I.bobrealsat));
return(0);
@ -534,7 +534,7 @@ uint8_t LP_etomic_verify_bob_payment(struct basilisk_swap *swap, char *txId)
memset(&input,0,sizeof(input));
memset(&input20,0,sizeof(input20));
if ( strcmp(swap->I.bobstr,"ETH") == 0 ) {
uint64_t paymentAmount = weiToSatoshi(data.valueHex);
uint64_t paymentAmount = weiToSatoshi(data.valueHex, 18);
if (paymentAmount != swap->I.bobrealsat) {
printf("Bob payment %s amount %" PRIu64 " != expected %" PRIu64 "\n", txId, paymentAmount, swap->I.bobrealsat);
return(0);
@ -785,3 +785,98 @@ uint64_t LP_etomic_get_balance(struct iguana_info *coin, char *coinaddr, int *er
return getErc20BalanceSatoshi(coinaddr, coin->etomic, coin->decimals, error);
}
}
void LP_etomic_process_tx_history_json(struct iguana_info *coin, cJSON *transactions) {
int history_size = cJSON_GetArraySize(transactions);
struct LP_tx_history_item *iter;
for (int i = history_size - 1; i >= 0; i--) {
cJSON *transaction = jitem(transactions, i);
char *tx_hash = jstr(transaction, "hash");
int found = 0;
portable_mutex_lock(&coin->tx_history_mutex);
DL_FOREACH(coin->tx_history, iter) {
if (strcmp(iter->txid, tx_hash) == 0) {
found = 1;
break;
}
}
portable_mutex_unlock(&coin->tx_history_mutex);
if (found || juint(transaction, "confirmations") == 0) {
continue;
}
struct LP_tx_history_item *item = malloc(sizeof(struct LP_tx_history_item));
memset(item, 0, sizeof(struct LP_tx_history_item));;
strcpy(item->txid, jstr(transaction, "hash"));
strcpy(item->category, "receive");
if (compareAddresses(coin->smartaddr, jstr(transaction, "from"))) {
strcpy(item->category, "send");
}
if (strcmp(item->category, "receive") == 0) {
item->amount = dstr(weiToSatoshi(jstr(transaction, "value"), coin->decimals));
} else {
item->amount = 0. - dstr(weiToSatoshi(jstr(transaction, "value"), coin->decimals));
}
item->time = juint(transaction, "timestamp");
strcpy(item->blockhash, jstr(transaction, "blockHash"));
item->blockindex = juint(transaction, "blockNumber");
item->blocktime = juint(transaction, "timestamp");
portable_mutex_lock(&coin->tx_history_mutex);
DL_APPEND(coin->tx_history, item);
portable_mutex_unlock(&coin->tx_history_mutex);
}
}
void LP_etomic_txhistory_loop(void *_coin)
{
struct iguana_info *coin = _coin;
if (coin->etomic[0] == 0) {
printf("Calling ETOMIC tx history loop for non-ETOMIC coin %s\n", coin->symbol);
return;
}
while (coin != NULL && coin->inactive == 0) {
coin->height = (int32_t) getEthBlockNumber();
char *result;
cJSON *json;
if (strcmp(coin->symbol, "ETH") == 0) {
// process standard transfers
result = eth_tx_history_etherscan(coin->smartaddr);
if (result) {
json = cJSON_Parse(result);
if (json && is_cJSON_Array(jobj(json, "result"))) {
LP_etomic_process_tx_history_json(coin, jobj(json, "result"));
free_json(json);
}
free(result);
}
// process "internal" transactions - transactions received during smart contract executions
// these are not added to standard list to extra processing is required
result = internal_eth_tx_history_etherscan(coin->smartaddr);
if (result) {
json = cJSON_Parse(result);
if (json && is_cJSON_Array(jobj(json, "result"))) {
LP_etomic_process_tx_history_json(coin, jobj(json, "result"));
free_json(json);
}
free(result);
}
} else {
result = erc20_tx_history_etherscan(coin->smartaddr, coin->etomic);
if (result) {
json = cJSON_Parse(result);
if (json && is_cJSON_Array(jobj(json, "result"))) {
LP_etomic_process_tx_history_json(coin, jobj(json, "result"));
free_json(json);
}
free(result);
}
}
int (*ptr)(struct LP_tx_history_item*, struct LP_tx_history_item*) = &history_item_cmp;
portable_mutex_lock(&coin->tx_history_mutex);
DL_SORT(coin->tx_history, ptr);
portable_mutex_unlock(&coin->tx_history_mutex);
sleep(30);
}
}

2
iguana/exchanges/LP_etomic.h

@ -53,4 +53,6 @@ uint64_t LP_etomic_get_balance(struct iguana_info *coin, char *coinaddr, int *er
void LP_etomic_pubkeystr_to_addr(char *pubkey, char *output);
void LP_etomic_txhistory_loop(void *_coin);
#endif //SUPERNET_LP_ETOMIC_H

3
iguana/exchanges/LP_include.h

@ -623,5 +623,6 @@ int bech32_decode(char *hrp,uint8_t *data,int32_t *data_len,const char *input);
int bech32_encode(char *output,const char *hrp,const uint8_t *data,int32_t data_len);
void HashGroestl(void * buf, const void * pbegin, int len);
bits256 LP_privkey(char *symbol,char *coinaddr,uint8_t taddr);
cJSON *electrum_address_history_cached(struct iguana_info *coin);
cJSON *address_history_cached(struct iguana_info *coin);
int history_item_cmp(struct LP_tx_history_item *item1, struct LP_tx_history_item *item2);
#endif

4
iguana/exchanges/LP_rpc.c

@ -538,7 +538,7 @@ cJSON *LP_listtransactions(char *symbol,char *coinaddr,int32_t count,int32_t ski
if (coinaddr == NULL) {
coinaddr = coin->smartaddr;
}
if ( coin->electrum == 0 )
if ( coin->electrum == 0 && (coin->etomic == 0 || coin->etomic[0] == 0))
{
if ( count == 0 )
count = 10;
@ -559,7 +559,7 @@ cJSON *LP_listtransactions(char *symbol,char *coinaddr,int32_t count,int32_t ski
}
return(retjson);
} else {
return(electrum_address_history_cached(coin));
return(address_history_cached(coin));
}
}

10
iguana/exchanges/LP_socket.c

@ -1225,7 +1225,7 @@ cJSON *tx_history_to_json(struct LP_tx_history_item *item, struct iguana_info *c
return json;
}
int it_cmp(struct LP_tx_history_item *item1, struct LP_tx_history_item *item2) {
int history_item_cmp(struct LP_tx_history_item *item1, struct LP_tx_history_item *item2) {
return(item1->time < item2->time);
}
@ -1254,6 +1254,7 @@ void LP_electrum_txhistory_loop(void *_coin)
struct LP_tx_history_item *iter;
int found = 0;
int confirmed = 0;
portable_mutex_lock(&coin->tx_history_mutex);
DL_FOREACH(coin->tx_history, iter) {
if (strcmp(iter->txid, tx_hash) == 0) {
found = 1;
@ -1263,6 +1264,7 @@ void LP_electrum_txhistory_loop(void *_coin)
break;
}
}
portable_mutex_unlock(&coin->tx_history_mutex);
struct LP_tx_history_item *item;
if (!found) {
// allocate new if not found
@ -1332,11 +1334,13 @@ void LP_electrum_txhistory_loop(void *_coin)
item->time = (uint32_t)time(NULL);
}
if (!found) {
portable_mutex_lock(&coin->tx_history_mutex);
DL_APPEND(coin->tx_history, item);
portable_mutex_unlock(&coin->tx_history_mutex);
}
free_json(tx_item);
}
int (*ptr)(struct LP_tx_history_item*, struct LP_tx_history_item*) = &it_cmp;
int (*ptr)(struct LP_tx_history_item*, struct LP_tx_history_item*) = &history_item_cmp;
// we don't want the history to be accessed while sorting
portable_mutex_lock(&coin->tx_history_mutex);
DL_SORT(coin->tx_history, ptr);
@ -1451,7 +1455,7 @@ cJSON *LP_electrumserver(struct iguana_info *coin,char *ipaddr,uint16_t port)
return(retjson);
}
cJSON *electrum_address_history_cached(struct iguana_info *coin) {
cJSON *address_history_cached(struct iguana_info *coin) {
cJSON *retjson = cJSON_CreateArray();
struct LP_tx_history_item *item;
portable_mutex_lock(&coin->tx_history_mutex);

66
iguana/exchanges/etomicswap/etomiccurl.c

@ -54,7 +54,7 @@ cJSON *parseEthRpcResponse(char *requestResult)
return result;
}
char* sendRequest(char *request, char *url)
char *send_post_json_request(char *request, char *url)
{
CURL *curl;
CURLcode res;
@ -89,16 +89,51 @@ char* sendRequest(char *request, char *url)
}
}
char *send_get_json_request(char *url)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
curl = curl_easy_init();
if (curl) {
struct string s;
init_eth_string(&s);
headers = curl_slist_append(headers, "Accept: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(curl, CURLOPT_URL, url);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK) {
printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return NULL;
}
/* always cleanup */
curl_easy_cleanup(curl);
return s.ptr;
} else {
printf("Couldn't init the curl\n");
return NULL;
}
}
cJSON *sendRpcRequest(char *method, cJSON *params)
{
char* string;
cJSON *request = cJSON_CreateObject();
cJSON_AddStringToObject(request, "jsonrpc", "2.0");
cJSON_AddStringToObject(request, "method", method);
cJSON_AddItemToObject(request, "params", cJSON_Duplicate(params, 1));
if (params) {
cJSON_AddItemToObject(request, "params", cJSON_Duplicate(params, 1));
}
cJSON_AddNumberToObject(request, "id", 1);
string = cJSON_PrintUnformatted(request);
char* requestResult = sendRequest(string, ETOMIC_URL);
char* requestResult = send_post_json_request(string, ETOMIC_URL);
free(string);
cJSON_Delete(request);
cJSON *result = parseEthRpcResponse(requestResult);
@ -350,7 +385,7 @@ int32_t waitForConfirmation(char *txId)
if (txData.exists == 0) {
retries++;
if (retries >= 30) {
printf("Have not found ETH tx %s after 10 checks, aborting\n", txId);
printf("Have not found ETH tx %s after 30 checks, aborting\n", txId);
return (-1);
}
}
@ -380,7 +415,7 @@ uint8_t get_etomic_from_faucet(char *etomic_addr)
cJSON *request = cJSON_CreateObject();
cJSON_AddStringToObject(request, "etomicAddress", etomic_addr);
string = cJSON_PrintUnformatted(request);
char* requestResult = sendRequest(string, FAUCET_URL);
char* requestResult = send_post_json_request(string, FAUCET_URL);
free(string);
cJSON_Delete(request);
@ -405,3 +440,24 @@ uint8_t get_etomic_from_faucet(char *etomic_addr)
cJSON_Delete(json);
return result;
}
char *eth_tx_history_etherscan(char *addr)
{
char resulting_url[4097];
sprintf(resulting_url, "%s?module=account&action=txlist&address=%s&startblock=0&endblock=99999999&sort=asc", ETHERSCAN_API, addr);
return send_get_json_request(resulting_url);
}
char *internal_eth_tx_history_etherscan(char *addr)
{
char resulting_url[4097];
sprintf(resulting_url, "%s?module=account&action=txlistinternal&address=%s&startblock=0&endblock=99999999&sort=asc", ETHERSCAN_API, addr);
return send_get_json_request(resulting_url);
}
char *erc20_tx_history_etherscan(char *addr, char *token_address)
{
char resulting_url[4097];
sprintf(resulting_url, "%s?module=account&action=tokentx&address=%s&contractaddress=%s&startblock=0&endblock=99999999&sort=asc", ETHERSCAN_API, addr, token_address);
return send_get_json_request(resulting_url);
}

15
iguana/exchanges/etomicswap/etomiccurl.h

@ -14,9 +14,11 @@ extern "C"{
#ifdef ETOMIC_TESTNET
#define ETOMIC_URL "http://195.201.0.6:8545"
#define DEFAULT_GAS_PRICE 100
#define ETHERSCAN_API "https://ropsten.etherscan.io/api"
#else
#define ETOMIC_URL "http://195.201.0.6:8555"
#define DEFAULT_GAS_PRICE 10
#define ETHERSCAN_API "https://api.etherscan.io/api"
#endif
#define FAUCET_URL "http://195.201.116.176:8000/getEtomic"
@ -38,12 +40,12 @@ typedef struct
uint8_t exists;
} EthTxData;
char* sendRawTx(char* rawTx);
char* sendRawTxWaitConfirm(char* rawTx);
char* ethCall(char* to, const char* data);
char *sendRawTx(char *rawTx);
char *sendRawTxWaitConfirm(char *rawTx);
char *ethCall(char *to, const char *data);
uint64_t estimateGas(char *from, char *to, const char *data);
int64_t getNonce(char* address);
char* getEthBalanceRequest(char* address);
int64_t getNonce(char *address);
char *getEthBalanceRequest(char *address);
EthTxReceipt getEthTxReceipt(char *txId);
EthTxData getEthTxData(char *txId);
uint64_t getEthBlockNumber();
@ -51,6 +53,9 @@ uint64_t getGasPriceFromStation(uint8_t defaultOnErr);
int32_t waitForConfirmation(char *txId);
void unlock_send_tx_mutex();
uint8_t get_etomic_from_faucet(char *etomic_addr);
char *eth_tx_history_etherscan(char *addr);
char *internal_eth_tx_history_etherscan(char *addr);
char *erc20_tx_history_etherscan(char *addr, char *token_address);
#ifdef __cplusplus
}

9
iguana/exchanges/etomicswap/etomiclib.cpp

@ -666,10 +666,13 @@ void satoshisToWei(char *dest, uint64_t input)
strcat(dest, "0000000000");
}
uint64_t weiToSatoshi(char *wei)
uint64_t weiToSatoshi(char *wei, uint8_t decimals)
{
u256 satoshi = jsToU256(wei) / boost::multiprecision::pow(u256(10), 10);
return static_cast<uint64_t>(satoshi);
u256 satoshi = jsToU256(wei);
if (decimals < 18) {
satoshi = satoshi * boost::multiprecision::pow(u256(10), 18 - decimals);
}
return static_cast<uint64_t>(satoshi / boost::multiprecision::pow(u256(10), 10));
}
char *sendEth(char *to, char *amount, char *privKey, uint8_t waitConfirm, int64_t gas, int64_t gasPrice, uint8_t defaultGasOnErr)

2
iguana/exchanges/etomicswap/etomiclib.h

@ -184,7 +184,7 @@ uint64_t getErc20Allowance(char *owner, char *spender, char *tokenAddress, uint8
void uint8arrayToHex(char *dest, uint8_t *input, int len);
void satoshisToWei(char *dest, uint64_t input);
uint64_t weiToSatoshi(char *wei);
uint64_t weiToSatoshi(char *wei, uint8_t decimals);
char *sendEth(char *to, char *amount, char *privKey, uint8_t waitConfirm, int64_t gas, int64_t gasPrice, uint8_t defaultGasOnErr);
char *sendErc20(

Loading…
Cancel
Save