diff --git a/Webhook.php b/Webhook.php new file mode 100644 index 0000000..699e40b --- /dev/null +++ b/Webhook.php @@ -0,0 +1,97 @@ +$base_link."/api.php?hash=".$tr_hash, + "method"=>"GET" + ); + + $amount = array ( + 'sent'=> $amount, + 'type'=> $type_tr, + 'currency' => $server_name + ); + + + $resources = array ( + 'id'=>$tr_hash, + 'create_time'=> $time, + 'state'=>'completed', + 'store_id' => $store_id, + 'reference' => $store_ref, + 'links'=>[$link], + + 'addr_to' => $dest, + 'amount'=>$amount + ); + if (strlen($from_add)>0) { + $resources['addr_from']=$from_add; + } + + + $data = array ('id'=>$tr_hash, + 'create_time'=>$time, + 'resource_type'=>'sale', + 'event_type'=> 'PAYMENT.SALE.COMPLETED', + 'summary'=>'A sale has been completed. The payement has been processed.', + 'links'=>[$link], + 'resources'=>$resources + ); + + return $data; +} + +// Sign and send message +function sendWebhook($url, $message) { + $json_message = json_encode($message); + $hash = crc32($json_message); + $sign = ""; + $sign_key = openssl_pkey_get_private($private_key_path); + if (openssl_sign($hash, $sign, $sign_key)) { + $signed = base64_encode($sign); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json', + 'COMCHAIN-TRANSMISSION-SIG:'.$signed, + 'COMCHAIN-AUTH-ALGO:RSA', + 'COMCHAIN-CERT-URL:'.$public_key_url)); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $json_message); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + $passed=true; + if (!$response = curl_exec( $ch )){ + $passed=false; + } + curl_close($ch); + + if ($passed) { + $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); + $passed = $code>=200 and $code<300; + } + return $passed; + } else { + return false; + } +} + + +?> diff --git a/api.php b/api.php index 66b51c4..5accdc6 100644 --- a/api.php +++ b/api.php @@ -1,5 +1,7 @@ withCredentials("transactions_ro", "Public_transactions")->build(); $keyspace = 'comchain'; $session = $cluster->connect($keyspace); - $query = "SELECT * FROM sellers WHERE store_id = ? AND server_name = ? "; + $query = "SELECT webhook_url FROM sellers WHERE store_id = ? AND server_name = ? "; $options = array('arguments' => array($shop_id, $server_name)); foreach ($session->execute(new Cassandra\SimpleStatement($query), $options) as $row) { - return true; + return $row['webhook_url']; } - return false; + return ""; } @@ -176,10 +181,14 @@ function validateShopData() { $shop_id=$_REQUEST['shopId']; $shop_ref=$_REQUEST['txId']; $server_name=$_REQUEST['serverName']; - return (isset($shop_id) && isset($server_name) && isset($shop_ref) && validateShop( $shop_id, $server_name)); + if (isset($shop_id) && isset($server_name) && isset($shop_ref)) { + return validateShop( $shop_id, $server_name); + } else { + return ""; + } } -function storeAdditionalData($transaction_ash) { +function storeAdditionalData($is_valid_shop, $transaction_ash, $web_hook_status) { $shop_id=$_REQUEST['shopId']; $shop_ref=$_REQUEST['txId']; $delegate=$_REQUEST['delegate']; @@ -189,7 +198,11 @@ function storeAdditionalData($transaction_ash) { $do_insert=false; $fields =array(); $val =array(); - if (validateShopData()) { + + $fields['wh_status'] = $web_hook_status; + $val[]='?'; + + if ($is_valid_shop) { $fields['store_id'] = $shop_id; $fields['store_ref'] = $shop_ref; $val[]='?'; @@ -211,7 +224,8 @@ function storeAdditionalData($transaction_ash) { $val[]='?'; } - if (sizeof($fields)>0) { + // Add if not only the status + if (sizeof($fields)>1) { $fields['hash'] = $transaction_ash; $val[]='?'; // build the query @@ -228,16 +242,59 @@ function storeAdditionalData($transaction_ash) { function sendRawTransaction($rawtx,$gethRPC){ $data = getDefaultResponse(); + + $shop_url = validateShopData(); + + + + $contract = ''; + $dest = ''; + $amount = 0; + $to_bal = 0; + $wh_status = 0; try { + if (strlen($shop_url)>0) { + $config = getServerConfig($_REQUEST['serverName']); + $contract = $config->{'contract_1'}; + // if so get the dest + $dest = '0x'.$rawtx.substr(110,40); + // get the sender + // TODO $sender = TransactionEcRecover($rawtx)[0]; + + // get the amount + $amount = hexdec($rawtx.substr(150,64)); + // get the balances for dest + $to_bal = getBalance($dest, $contract); + // TODO $from_bal = getBalance($sender, $contract); + $wh_status = 1; + } + $data['data'] = getRPCResponse($gethRPC->eth_sendRawTransaction($rawtx)); + + if (strlen($shop_url)>0 && $amount > 0) { + // get the balances check if changes compatible the the amount + $to_bal_after = getBalance($dest, $contract); + $from_bal_after = getBalance($sender, $contract); + if ($to_bal_after - $to_bal >= $amount) { // TODO} && $from_bal - $from_bal_after >= $amount) { + // if so : send the webhook + $message = createWebhookMessage($data['data'], $_REQUEST['serverName'], + $_REQUEST['shopId'], $_REQUEST['txId'], + "", $rawtx); // TODO ""=> $sender + $res = sendWebhook($shop_url, $message); + if ($res) { + $wh_status = 3; + } else { + $wh_status = 2; + } + } + } } catch (exception $e) { $data['error'] = true; $data['msg'] = 'E1'.$e->getMessage(); } - try { - storeAdditionalData($data['data']); + storeAdditionalData(strlen($shop_url)>0, $data['data'], $wh_status); } catch (exception $e) { $data['error'] = true; @@ -246,6 +303,7 @@ function sendRawTransaction($rawtx,$gethRPC){ return json_encode($data); } + function formatAddress($addr){ if (substr($addr, 0, 2) == "0x") return $addr; diff --git a/checkAdmin.php b/checkAdmin.php index e5ff039..87cbd25 100644 --- a/checkAdmin.php +++ b/checkAdmin.php @@ -29,7 +29,7 @@ function getServerConfig($server){ return $json->{'server'}; } -function getAccType($address,$contract){ +function getAccType($address, $contract){ $url = getServerAddress()."/api.php"; $ch = curl_init(); $ethCall = ['to' =>$contract, @@ -53,7 +53,7 @@ function getAccType($address,$contract){ return substr($data,-1); } -function getAccStatus($address,$contract){ +function getAccStatus($address, $contract){ $url = getServerAddress()."/api.php"; $ch = curl_init(); $ethCall = ['to' =>$contract, @@ -78,6 +78,33 @@ function getAccStatus($address,$contract){ return substr($data,-1); } +function getBalance($address, $contract){ + $url = getServerAddress()."/api.php"; + $ch = curl_init(); + $ethCall = ['to' =>$contract, + 'data' => '0x70a08231000000000000000000000000'.substr($address,2) + ]; + $fields = ['ethCall'=>$ethCall]; + $fields_string = http_build_query($fields); + + curl_setopt($ch, CURLOPT_URL, $url); + // Set so curl_exec returns the result instead of outputting it. + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, count($fields)); + curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string); + + // Get the response and close the channel. + $response = curl_exec($ch); + curl_close($ch); + + $json = json_decode($response); + $data= $json->{'data'}; + + return hexdec($data); +} + + + function checkSign($dat, $signature, $caller){ return $caller==personal_ecRecover($dat, $signature); diff --git a/ecrecover_helper.php b/ecrecover_helper.php index a782efd..bc8f041 100644 --- a/ecrecover_helper.php +++ b/ecrecover_helper.php @@ -6,6 +6,21 @@ require_once './Keccak.php'; use kornrunner\Keccak; + +function TransactionEcRecover($rawTx) { + // get the signature, last 134 chars + $len = strlen($rawTx); + $len_data = $len - 134; + $data = substr($rawTx, 0, $len_data); + $signature = substr($rawTx, $len_data); + $v = substr($signature,0,2); + $r = substr($signature,4,64); + $s = substr($signature,70,64); + $signed = '0x'.$r.$s.$v; + return ecRecoverPublic($data, $signed); +} + + function personal_ecRecover($msg, $signed) { return personal_ecRecoverPublic($msg, $signed)[0]; } @@ -16,7 +31,7 @@ function ecRecover($hex, $signed) { function personal_ecRecoverPublic($msg, $signed) { $personal_prefix_msg = "\x19Ethereum Signed Message:\n". strlen($msg). $msg; - $hex = keccak256($personal_prefix_msg); + $hex = keccak256WithPrefix($personal_prefix_msg); return ecRecoverPublic($hex, $signed); } @@ -45,7 +60,7 @@ function ecRecoverPublic($hex, $signed) { $publicKey = Signature::recoverPublicKey($rGmp, $sGmp, $messageGmp, $recovery); $publicKeyString = $publicKey["x"] . $publicKey["y"]; - return array('0x'. substr(keccak256(hex2bin($publicKeyString)), -40),$publicKeyString); + return array('0x'. substr(keccak256WithPrefix(hex2bin($publicKeyString)), -40),$publicKeyString); } function strToHex($string) @@ -54,7 +69,7 @@ function strToHex($string) return '0x' . array_shift($hex); } -function keccak256($str) { +function keccak256WithPrefix($str) { return '0x'. Keccak::hash($str, 256); } diff --git a/parser.py b/parser.py index ad8b308..8536697 100644 --- a/parser.py +++ b/parser.py @@ -173,7 +173,7 @@ def find_zip(libprefix): session.execute(cqlInsertHash) # Check if the transaction is in the pending transaction table (webshop_transactions) - cqlcommand = "SELECT hash, store_id, store_ref, delegate , message_from, message_to, toTimestamp(now()) AS stamp FROM webshop_transactions WHERE hash='{}'".format(transHash) + cqlcommand = "SELECT hash, store_id, store_ref, wh_status, delegate , message_from, message_to, toTimestamp(now()) AS stamp FROM webshop_transactions WHERE hash='{}'".format(transHash) rows = sessioStaging.execute(cqlcommand) additional_fields = [] additional_values = [] @@ -202,11 +202,16 @@ def find_zip(libprefix): additional_fields.append('store_id') additional_values.append("'{}'".format(row.store_id)) additional_fields.append('store_ref') - additional_values.append("'{}'".format(row.store_ref)) # check for ' + additional_values.append("'{}'".format(row.store_ref)) + + status = row.wh_status + nb_attempt ='0' + if status>1: # 2 failed attempt / 3 success + nb_attempt ='1' additional_fields.append('status') - additional_values.append("1") # New shop transction + additional_values.append(status) # New shop transction additional_fields.append('tr_attempt_nb') - additional_values.append("0") + additional_values.append(nb_attempt) additional_fields.append('tr_attempt_date') additional_values.append("'{}'".format(row.stamp-10800000)) diff --git a/webhook.py b/webhook.py index 727d21e..b35eb79 100644 --- a/webhook.py +++ b/webhook.py @@ -245,48 +245,7 @@ def buildWebhookMessage(transaction, server_name): return data -def buildPreWebhookMessage(tr_hash, server_name, store_id, store_ref, rawtx): - function = rawtx[78:86] - type_tr = 'TransferCredit' if '0x'+function == CM_TRANSFERT else 'Transfer' - - dest = '0x'+ rawtx[110:150] - amount = int(rawtx[150:214],16)/100.0 - - h = platform.uname()[1] - data = {} - data['id']=tr_hash - data['create_time']=transaction['time'] - data['resource_type']='sale' - data['event_type']='PAYMENT.SALE.PENDING' - data['summary']='A sale is pending. The payement has been recieved and is currently processed.' - - # Link to the transaction data through the API - link={} - link['href']= '{}/api.php?hash={}'.format(h, tr_hash) - link['method']='GET' - - resource={} - resource['id']=tr_hash - resource['create_time']= int(time.time()) - resource['state']="pending" - resource['store_id'] = store_id - resource['reference'] = store_ref - resource['links']=[link] - resource['addr_to']=dest - - amount={} - amount['sent']=amount - amount['type']=type_tr - amount['currency']=server_name - - resource['amount']=amount - data['resource']=resource - data['links']=[link] - - return data - - def sendWebhook(url, message): @@ -298,7 +257,7 @@ def sendWebhook(url, message): # prepare the string to be signed crc = binascii.crc32(json_message) - sign_str = "{}|{}|{}|{}".format(message['id'], message['create_time'], message[resource]['store_id'], crc) + sign_str = crc #load the key private_key = open(private_key_path, 'r').read()