how display balance of token through Ethereum RPC?
$id = 0;
$data = array();
$data['jsonrpc'] = '2.0';
$data['id'] = $id++;
$data['method'] = 'eth_call';
$data['params'] = [['from' => '0x0...', 'to' => '0x0...', 'data' => 'contract byte code here 0x0...'], 'latest'];
$ch = curl_init();
...
Return:
{"jsonrpc":"2.0","id":0,"result":"0x"}
What to do next? Call contract method balanceOf? How to do that?
To get token balance with eth_call you need to and data parameter. to is contract address, here we need to generate the data parameter. As the doc eth_call says,
data: DATA - (optional) Hash of the method signature and encoded
parameters. For details see Ethereum-Contract-ABI
Take this EOS token transaction as a example.
Contract address:0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0
Token Holder address:0x0b88516a6d22bf8e0d3657effbd41577c5fd4cb7
You can see the contract code here.
contract ERC20 {
function totalSupply() constant returns (uint supply);
function balanceOf( address who ) constant returns (uint value);
function allowance( address owner, address spender ) constant returns (uint _allowance);
function transfer( address to, uint value) returns (bool ok);
function transferFrom( address from, address to, uint value) returns (bool ok);
function approve( address spender, uint value ) returns (bool ok);
event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}
Function Selector
>>> from web3 import Web3
>>> Web3.sha3("balanceOf(address)")
HexBytes('0x70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be')
Take the first four bytes 70a08231
Argument Encoding
address: equivalent to uint160, except for the assumed interpretation
and language typing.
int: enc(X) is the big-endian two's complement encoding of X,
padded on the higher-order (left) side with 0xff for negative X and
with zero bytes for positive X such that the length is a multiple of
32 bytes.
Padding the 20 bytes token address to 32 bytes with 0 to token holder address:
0000000000000000000000000b88516a6d22bf8e0d3657effbd41577c5fd4cb7
Then concat the function selector and encoded parameter, we get data parameter:
0x70a082310000000000000000000000000b88516a6d22bf8e0d3657effbd41577c5fd4cb7
Make the request with:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"to": "0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0", "data":"0x70a082310000000000000000000000000b88516a6d22bf8e0d3657effbd41577c5fd4cb7"}, "latest"],"id":67}' -H "Content-Type: application/json" http://127.0.0.1:8545/
here is the curl result (You may get different answer here, as there may be some transaction with this address done after my polling the request)
{"jsonrpc":"2.0","id":67,"result":"0x00000000000000000000000000000000000000000000014a314d9ff9b20b9800"}
You can change convert hex format balance to decimal
>>> 0x00000000000000000000000000000000000000000000014a314d9ff9b20b9800
6090978215900000000000
Check the result,
When calling a Solidity contract function, in general, data should be the following, encoded as a hex string:
The "function selector," which is the first four bytes of the keccak-256 hash of the signature of the function you're calling.
The ABI-encoded arguments to the function you're calling.
The function signature for an ERC20 token's balanceOf is balanceOf(address). The keccak-256 hash is 70a08231b98ef4ca268c9cc3f6b4590e4bfec28280db06bb5d45e689f2a360be, so the first four bytes are 70a08231.
The function only takes a single parameter: the address of the account whose balance you're trying to look up. To ABI-encode it, simply left-pad it with zeros until it's 32 bytes long. Since addresses are 20 bytes, this means adding 12 bytes of zeros (or 24 characters in hex).
So the full data field should be "0x70a08231" + "000000000000000000000000" + address.
May I recommend a proper ERC20 library for PHP that I have developed myself.
https://www.furqansiddiqui.com/libraries/erc20-php/
https://github.com/furqansiddiqui/erc20-php
sample code to retrieve balance:
<?php
$geth = new EthereumRPC('127.0.0.1', 8545);
$erc20 = new \ERC20\ERC20($geth);
// Pass ERC20 contract address as argument below
$token = $erc20->token('0xd26114cd6EE289AccF82350c8d8487fedB8A0C07');
var_dump($token->name()); # string(8) "OMGToken"
var_dump($token->symbol()); # string(3) "OMG"
var_dump($token->decimals()); # int(18)
var_dump($token->balanceOf('0x...')); // Enter ethereum address here
For token transaction you need to use eth_sendTransaction.
Related
I'm trying to verify an external call to one of our endpoints, this endpoint is triggered by a third party, we receive a transaction data and a signature based on that transaction information, with that, we need to decrypt the signature and compare the result to verify the authenticity.
I'm trying to use openssl_public_decrypt to decrypt the signature using the provider's public key.
This is how I'm trying:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo $data;
}else{
echo 'Error';
}
I don't get any error but the $data value is not what I expect, is something like this
v_~�#&�W��q�&Ș�uQ���֔�
I'm sure I'm missing something but I can't find out what is it, due to the $data value looks like is encrypted.
The result that I expect from the decrypt is 167619085f7ed94026e357930b18dc011971f226c898ef7551cdf6ec9ad694cf this is the result of the following code
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$hashed = hash('sha256', $canonical);
That last part is how the provider generates the signature.
For the given example, canonic form is as follows:
cec4b9bf-5a39-4bd7-bc8b826ebc18208d|Internal_0005|12|39679|BP7610|947d589a40dece13c28f2b63c41ae451
We sign the response by hashing the canonic form with SHA-256 and encrypting the
resulting bytes with our private key.
RSA_ENCRYPT(SHA256(canonicForm), privkey.key)
To verify the payload, you must recalculate the canonic form and apply SHA-256 to the
result. The resulting value must be compared with the result of decrypting the signature
parameter with our public key.
Any hint would be appreciated.
perhaps post the public key and some valid test data so we can test ourselves?
anyway, v_~�#&�W��q�&Ș�uQ���֔� could be a valid signature, remember that SHA256 is 256 random bits, it's binary data, not ascii data, not hex, and not printable. SHA256 is also exactly 32 bytes long (256 bits, and 1 byte is 8 bits, and 256/8 is 32 bytes), so if you run var_dump(strlen($data)) after decryption, it should print 32, if it does not print 32, it implies they're using a padding scheme, try checking the strlen of both OPENSSL_PKCS1_PADDING and OPENSSL_NO_PADDING , when you get the correct padding scheme, strlen($data) after decryption should be int(32)
but my best guess is:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo "signature decryption success! ";
if(hash_equals(hash("sha256",$canonical,true),$data)){
echo "checksum verification success!";
} else{
echo "checksum verification failed (after decryption was successful..)";
}
}else{
echo 'checksum decryption error';
}
but again, experiment with both
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_PKCS1_PADDING)){
and
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_NO_PADDING)){
1 of them is probably correct (and when it is correct, var_dump(strlen($data)) should print int(32) )
I have made an PHP rest api. I want to connect to the API via an IOS and Android app. But I don't know how to secure everything.
I register the device at my database when the app fires for the first time
Table devices:
id random enabled
1 12345 1
Every device has an id and an random. The random value is unique in that table. The actual device receives the id and random value.
What I have right now:
I validate each request at the php side:
private function validateUrl(){
$url = $_SERVER['REQUEST_URI'];
$signature = isset($_GET['signature']) ? $_GET["signature"] : null;
$url = str_replace('&signature=' . $signature, '',$url);
$url = "" . $url;
$correctSignature = md5($url . "TNynVX9k2HqYSXnd");
if($signature != $correctSignature){
echo die(json_encode([array('status' => "not valid")]));
}
}
The request at (in this case) the IOS side:
private func random () -> Int {
var result = "";
for _ in 1...3 {
let randomNumber = arc4random_uniform(99)
result += String(randomNumber);
}
return Int(result)!;
}
private func md5(string string: String) -> String {
var digest = [UInt8](count: Int(CC_MD5_DIGEST_LENGTH), repeatedValue: 0)
if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_MD5(data.bytes, CC_LONG(data.length), &digest)
}
var digestHex = ""
for index in 0..<Int(CC_MD5_DIGEST_LENGTH) {
digestHex += String(format: "%02x", digest[index])
}
return digestHex
}
func createUrl(url : String) -> String {
var newUrl = url;
newUrl += "?&random=\(random())";
let secret = "TNynVX9k2HqYSXnd"
let signature = md5(string: newUrl + secret)
newUrl += "&signature=" + signature;
return newUrl;
}
This works great but as you see I have an static API key. Something where I have my concerns about. So I thought maybe I can create an API key based on the id and random from my database. Is that more secure?
Something like:
func createUrl(url : String) -> String {
var newUrl = url;
let signature = md5(string: [device id here] + [device random here])
newUrl += "&signature=" + signature;
newUrl += "&deviceId=" + [device id here];
return newUrl;
}
And at my PHP side I can get the deviceId property from the url. Compare it to the database, retrieve the id and random value. MD5 them. Compare that to the signature. And when there is an match it's ok. Otherwise not. Is that an solid implementation?
Or in simple terms. Can I replace the api key with the combination of id + random?
Looks like you are using the same secret client side and server side, which can be an issue since anyone who has access to the *.apk or *.ipa could disassemble it and find the token, which is pretty easy with open source tools (https://github.com/iBotPeaches/Apktool). Especially on Android where the APK is pretty much a jar with some other assets. Are you generating the random token client side? Since it looks to be an int, that's only 32 bits of entropy, not enough to be secure.
Usually, the token is created server side rather than client side. There are many different ways of doing this. One is a JSON Web Token (JWT) which basically encodes data like an id or expires data into a token which is signed with a private key. Only the server knows the private key, and thus it's the trusted source for creating them, but others can have access to a public key which can be used to verify the token.
If you don't want to deal with JWT's and signing, a second option is just opaque tokens that are created server side. The key is a large amount of entropy like a large UUID.
There is a lot of information on asymmetric algorithms that generate a mathematically linked public/private key pair.
You can read more here: https://www.moesif.com/blog/technical/restful-apis/Authorization-on-RESTful-APIs/
I'm using the json_decode function to decode (and verify a postback from a payment processor). the json object received looks as follow
{
"notification":{
"version":6.0,
"attemptCount":0,
"role":"VENDOR",
.....
"lineItems":[
{
"itemNo":"1",
"productTitle":"A passed in title",
"shippable":false,
"recurring":false,
"customerProductAmount":1.00,
"customerTaxAmount":0.00
}
]
},
"verification":"9F6E504D"
}
The verification works as follows, one takes the notification node and append a secret key. The first eight characters of the SHA1 hash of this string should match the content of the validation node.
However, I noticed that whilst using json_decode, the double value 6.0, 0.00 etc are truncated to integers (6, 0 ,etc). This messes up the string (in terms of it not generating the correct SHA1-hash). Do note, I cannot use the depth limit to prevent decoding of the notification branch, since I need to support PHP 5.0. How can I tackle this issue. The (defect) validation code I wrote is:
public function IPN_Check(){
$o = (json_decode($this->test_ipn));
$validation_string = json_encode($o->notification);
}
I tried the following:
<?php
var_dump(json_decode('
{
"notification":{
"version":6.0,
"attemptCount":0
}
}
'));
and got this output:
object(stdClass)#1 (1) {
["notification"]=>
object(stdClass)#2 (2) {
["version"]=>
float(6)
["attemptCount"]=>
int(0)
}
}
PHP does make a difference between float and int, maybe you could do something like gettype($o->notification[$i]) == 'float' to check whether you need to add a zero using a string.
UPD.
PHP does make a difference between float and int, but json_encode() - may not. To be sure, that you encode all values as they are - use json_encode() with JSON_PRESERVE_ZERO_FRACTION parameter, and all your float/double types will be saved correctly.
It looks like ClickBank they always send it in the same format with only the two top level fields "notification" and "verification". So you can just use substr to remove the first 16 characters ({"notification":) and the last 27 characters (,"verification":"XXXXXXXX"}) from the raw JSON and then proceed from there:
$notification = substr($json, 16, -27);
$verification = strtoupper( substr( hash('sha1', $notification . $secret), 0, 8) );
I have a signed PDF document. It was signed by using TCPDF. Now I want to verify it. This is my solution:
Get content of signed pdf.
Get original content and signature value base on /ByRange field.
Get encrypted digest message from signature value. It's octet string at the end of signature value.
Use Openssl_public_decrypt() function to decrypt the encrypted digest message with public key. Then we have a string which has a prefix ("3021300906052b0e03021a05000414"). This prefix denotes the hash function used is SHA-1. After removing the prefix, we obtain digest message D1.
Use SHA1() function to hash original content, we obtain digest message D2.
Compare D1 with D2. If D1 = D2 then signature is valid and vice versa.
My problem is in last step, when I compare D1 with D2, they are not equal. I don't know why.
Thanks for any help.
You should try based on following example
<?php
// $data and $signature are assumed to contain the data and the signature
// fetch public key from certificate and ready it
$pubkeyid = openssl_pkey_get_public("file://src/openssl-0.9.6/demos/sign/cert.pem");
// state whether signature is okay or not
$ok = openssl_verify($data, $signature, $pubkeyid);
if ($ok == 1) {
echo "good";
} elseif ($ok == 0) {
echo "bad";
} else {
echo "ugly, error checking signature";
}
// free the key from memory
openssl_free_key($pubkeyid);
?>
more Examples ad explanation
http://www.php.net/manual/en/function.openssl-verify.php
Using the example of CreateEnvelope sample code
I receive a message:
Guid should contain 32 digits with 4 dashes
(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
Using the method of guid() of SDK, which returns a string in the form {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
I receive a message:
The 'http://www.docusign.net/API/3.0:ID' element is invalid - The
value '0 'is invalid according to its datatype
What is the format for the recipient ID is valid?
In DocuSign, when you are adding recipients to your envelopes you need at least 3 pieces of data to uniquely identify each recipient. You need to set recipient name, email, and recipientId. (If you are using the Embedding feature then you need to set the CaptiveInfo as well for SOAP or the clientUserId if using REST)
The recipientId is user-defined, meaning you can set it to whatever you like- however it needs to be a non-negative number such as 1, 2, 1000, 2000. I believe the data type for the recipientId is string so you're not limited to just numbers either.
However, 0 is the one value that you can not set it to. So try changing the value of your recipientId to 1 or 2 or one or two or 1abc or 2def and see if that resolves your issue.
I use the following code:
$r = new Recipient();
$r->ID = '1';
$r->UserName = 'john';
$r->SignerName = 'John Doe';
$r->Email = 'john#example.com';
$r->Type = RecipientTypeCode::Signer;
$r->RoutingOrder = 1;
$r->RequireIDLookup = false;
$r->CaptiveInfo = new RecipientCaptiveInfo();
$r->CaptiveInfo->ClientUserId = '1';
...
And getting an error message:
Uncaught SoapFault exception: [soap:Client] 358101: Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
If I use the following code:
$uid = $this->getUid();
$r->ID = $uid;
...
$r->CaptiveInfo->ClientUserId = $uid;
...
$this->getUid() using the method of guid() of SDK, which returns a string in the form {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
Getting an error message:
Uncaught SoapFault exception: [soap:Client] Validation error: The 'http://www.docusign.net/API/3.0:ID' element is invalid - The value '0' is invalid according to its datatype 'http://www.docusign.net/API/3.0:LocalId' - Value '0' was either too large or too small for PositiveInteger.