From d4f19ce7eb863c2a5d10d8e4f824157f1116989d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 5 Sep 2016 01:43:33 -0300 Subject: [PATCH] txdetails and helper functions, proposed fix for #59 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/jl777/SuperNET/issues/59 required retrieving additional details from transaction info RPC. both getrawtransaction and get transaction use the same code path and list transactions also uses the get transaction path, so all paths should now have the txdetails fields: "category": "receive", "amount": 0.50000000, "confirmations": 24466, "blockhash": "00000000000000000517ce625737579f91162c46ad9eaccad0f52ca13715b156", "blocktime": 1448045745, "blockindex": 78, "label": "", the block index and label fields are currently not supported. Also since iguana has data for all addresses, not just wallet addresses and including ‘push’ addresses, the ‘category’ field has additional return possibilities: ‘isp2sh’ is returned for any ‘p2sh’ address, regardless of whether some or all of the required privkeys to spend the ‘p2sh’ address are in the wallet. to determine partial or full signature capability would require interpreting the script in place, and the returned information would need to indicate conditional states, i.e. cltv, multisig, if/else, etc. so this part is a won’t fix for non-p2sh addresses, if it is in the wallet it will return “receive” or “send” depending on whether the txid/vout outpoint has been spent or not. for addresses no in the wallet, it will return “unspent” or “spent”. Note that a locked wallet might or might not get as far as calling this txdetails, but if it did, it would return “unspent”/“spent” even for an address in the wallet as it has no way to know if it is indeed a wallet address --- iguana/iguana_payments.c | 63 ++++++++++++++++++++++------- iguana/iguana_realtime.c | 86 ++++++++++++++++++++++++++++++++-------- iguana/iguana_wallet.c | 6 +-- includes/iguana_funcs.h | 4 ++ 4 files changed, 124 insertions(+), 35 deletions(-) diff --git a/iguana/iguana_payments.c b/iguana/iguana_payments.c index 79f58a38c..d811bfdf8 100755 --- a/iguana/iguana_payments.c +++ b/iguana/iguana_payments.c @@ -831,9 +831,44 @@ THREE_STRINGS(bitcoinrpc,verifymessage,address,sig,message) } else return(clonestr("{\"error\":\"sig is too long\"}")); } +int64_t iguana_txdetails(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *item,bits256 txid,int32_t vout,int32_t height) +{ + struct iguana_block *block; bits256 hash2; uint64_t amount = 0; char coinaddr[64],account[512]; + /*{ + "category": "receive", + "amount": 0.50000000, + "label": "", + "confirmations": 24466, + "blockhash": "00000000000000000517ce625737579f91162c46ad9eaccad0f52ca13715b156", + "blockindex": 78, + "blocktime": 1448045745, + }*/ + jaddbits256(item,"txid",txid); + if ( vout >= 0 ) + { + jaddnum(item,"vout",vout); + if ( (amount= iguana_txidamount(myinfo,coin,txid,vout)) != 0 ) + jaddnum(item,"amount",dstr(amount)); + } + else if ( vout == -1 ) + jadd(item,"coinbase",jtrue()); + jaddstr(item,"category",iguana_txidcategory(myinfo,coin,account,coinaddr,txid,vout)); + if ( account[0] != 0 && jobj(item,"account") == 0 ) + jaddstr(item,"account",account); + if ( coinaddr[0] != 0 ) + jaddstr(item,"address",coinaddr); + hash2 = iguana_blockhash(coin,height); + jaddbits256(item,"blockhash",hash2); + if ( (block= iguana_blockfind("rawtx",coin,hash2)) != 0 ) + jaddnum(item,"blocktime",block->RO.timestamp); + jaddnum(item,"height",height); + jaddnum(item,"confirmations",coin->blocks.hwmchain.height - height); + return(amount); +} + HASH_AND_INT(bitcoinrpc,getrawtransaction,txid,verbose) { - struct iguana_txid *tx,T; char *txbytes; bits256 checktxid; int32_t len=0,height,extralen=65536; cJSON *retjson,*txobj; uint8_t *extraspace; struct iguana_block *block; bits256 hash2; struct iguana_RTtxid *RTptr; + struct iguana_txid *tx,T; char *txbytes; bits256 checktxid; int32_t len=0,height,extralen=65536; cJSON *retjson,*txobj; uint8_t *extraspace; struct iguana_RTtxid *RTptr; if ( remoteaddr != 0 ) return(clonestr("{\"error\":\"no remote\"}")); HASH_FIND(hh,coin->RTdataset,txid.bytes,sizeof(txid),RTptr); @@ -861,12 +896,7 @@ HASH_AND_INT(bitcoinrpc,getrawtransaction,txid,verbose) free(txbytes); if ( txobj != 0 ) { - hash2 = iguana_blockhash(coin,height); - jaddbits256(txobj,"blockhash",hash2); - if ( (block= iguana_blockfind("rawtx",coin,hash2)) != 0 ) - jaddnum(txobj,"blocktime",block->RO.timestamp); - jaddnum(txobj,"height",height); - jaddnum(txobj,"confirmations",coin->blocks.hwmchain.height - height); + iguana_txdetails(myinfo,coin,txobj,tx->txid,-2,height); return(jprint(txobj,1)); } } @@ -894,14 +924,19 @@ HASH_AND_INT(bitcoinrpc,getrawtransaction,txid,verbose) jaddbits256(retjson,"blockhash",blockhash); jaddnum(retjson,"height",height); free(txbytes); - } else jaddstr(retjson,"error","cant find txid in block"); + } + else if ( coin->RTheight > 0 ) + jaddstr(retjson,"error","cant find txid in block"); + else jaddstr(retjson,"error","not in realtime mode yet"); free(blockstr); free(data); } else jaddstr(retjson,"error","cant find blockhash"); return(jprint(retjson,1)); } } - return(clonestr("{\"error\":\"cant find txid\"}")); + if ( coin->RTheight > 0 ) + return(clonestr("{\"error\":\"cant find txid\"}")); + else return(clonestr("{\"error\":\"not in realtime mode yet\"}")); } int64_t iguana_lockval(int32_t finalized,int64_t locktime) @@ -1005,25 +1040,25 @@ char *iguana_validaterawtx(struct supernet_info *myinfo,struct iguana_info *coin STRING_AND_INT(bitcoinrpc,validaterawtransaction,rawtx,suppress) { - uint8_t *extraspace; int32_t extralen=65536;// char *retstr; struct iguana_msgtx msgtx; + uint8_t *extraspace; int32_t extralen=65536; char *retstr; struct iguana_msgtx msgtx; if ( remoteaddr != 0 ) return(clonestr("{\"error\":\"no remote\"}")); //cJSON *txobj; char *teststr= "{\"version\":1,\"locktime\":0,\"vin\":[{\"userdata\":\"20fe936da3707c8c4cc7eb0352160ec3f50b9454d46425df6347b2fbc5b2ec87ea00\",\"txid\":\"ee12e50b629d5d45438570fff841d1a2ba7d27f356de4aa06900c9a5e38cf141\",\"vout\":0,\"scriptPubKey\":{\"hex\":\"a9145cc47cc123e3f9b7dce0230009b9d3013a9e0c9687\"},\"suppress\":1,\"redeemScript\":\"6304165daa57b1752102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac67a9143805600256ed8498ca1ec426759212e5835e8dc2882103a7b696908f77d69ec89887f8c4a0423b9e80b5974dc43301bd7d8abad07e1211ac68\"}],\"vout\":[{\"satoshis\":\"21821\",\"scriptPubkey\":{\"hex\":\"76a9143ef4734c1141725c095342095f6e0e7748b6c16588ac\"}}]}"; - cJSON *txobj; char *teststr= "{\"version\":1,\"locktime\":0,\"vin\":[{\"userdata\":\"206efad760ee54b9b2e2a038a821ef9f950eb0e248545ac202c3e2074cd14f92cb00\",\"txid\":\"3f4759381a62154f2f0eefed1e4433342548ad7b269f912820383b715a39273c\",\"vout\":0,\"scriptPubKey\":{\"hex\":\"a91446dcccef39c1d8c6da2ccc35dce2bfa7ec0d168887\"},\"suppress\":1,\"redeemScript\":\"63041c60aa57b1752103175cf93574c31637b8c2d8acd5319e3cd23761b5e418d32c6bcb194972ba9273ac67a9142d75daf71325feaa593b8f30989e462892189914882102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac68\"}],\"vout\":[{\"satoshis\":\"18625\",\"scriptPubkey\":{\"hex\":\"76a914b7128d2ee837cf03e30a2c0e3e0181f7b9669bb688ac\"}}]}"; + //cJSON *txobj; char *teststr= "{\"version\":1,\"locktime\":0,\"vin\":[{\"userdata\":\"206efad760ee54b9b2e2a038a821ef9f950eb0e248545ac202c3e2074cd14f92cb00\",\"txid\":\"3f4759381a62154f2f0eefed1e4433342548ad7b269f912820383b715a39273c\",\"vout\":0,\"scriptPubKey\":{\"hex\":\"a91446dcccef39c1d8c6da2ccc35dce2bfa7ec0d168887\"},\"suppress\":1,\"redeemScript\":\"63041c60aa57b1752103175cf93574c31637b8c2d8acd5319e3cd23761b5e418d32c6bcb194972ba9273ac67a9142d75daf71325feaa593b8f30989e462892189914882102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac68\"}],\"vout\":[{\"satoshis\":\"18625\",\"scriptPubkey\":{\"hex\":\"76a914b7128d2ee837cf03e30a2c0e3e0181f7b9669bb688ac\"}}]}"; // 01000000013c27395a713b382028919f267bad48253433441eedef0e2f4f15621a3859473f00000000d147304402207ecd423b55c1aa45a994c4eb4337ff0891692fbb69954a9ba024745a99c5272d02207cea696425feb5388153ab7f2608d66a66e4c95cfda2d44e98bc56e25994d3f701206efad760ee54b9b2e2a038a821ef9f950eb0e248545ac202c3e2074cd14f92cb004c6763041c60aa57b1752103175cf93574c31637b8c2d8acd5319e3cd23761b5e418d32c6bcb194972ba9273ac67a9142d75daf71325feaa593b8f30989e462892189914882102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac68ffffffff01c1480000000000001976a914b7128d2ee837cf03e30a2c0e3e0181f7b9669bb688ac00000000 // 01000000013c27395a713b382028919f267bad48253433441eedef0e2f4f15621a3859473f00000000fd8b00206efad760ee54b9b2e2a038a821ef9f950eb0e248545ac202c3e2074cd14f92cb004c6763041c60aa57b1752103175cf93574c31637b8c2d8acd5319e3cd23761b5e418d32c6bcb194972ba9273ac67a9142d75daf71325feaa593b8f30989e462892189914882102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac68ffffffff01c1480000000000001976a914b7128d2ee837cf03e30a2c0e3e0181f7b9669bb688ac00000000 //cJSON *txobj; char *teststr= "{\"version\":1,\"locktime\":0,\"vin\":[{\"userdata\":\"20ae439d344513eab8e718d8214fe6ae8133b8b5b594afd64da21d0e40b9c37cdd00\",\"txid\":\"2c1320315f4fb519cbf2b4d7b67855013b9a09a85e515df43b41d407a0083b09\",\"vout\":0,\"scriptPubKey\":{\"hex\":\"a9142e7674400d04217f770f2222126dc7fee44b06b487\"},\"suppress\":1,\"redeemScript\":\"63041686a657b1752102a9669e63ef1ab04913615c2f3887ea3584f81e5f08feee9535b19ab3739d8afdac67a914ed74c61c27656abc6c20687c3a9212ffdc6f34cd88210398a4cb9f6ea7c52a4e27455028a95e2e4e397a110fb75f072c2c58a8bdcbf4baac68\"}],\"vout\":[{\"satoshis\":\"16733\",\"scriptPubkey\":{\"hex\":\"76a91454a752f0d71b89d7c014ed0be29ca231c9546f9f88ac\"}}]}"; extraspace = calloc(1,extralen); - if ( (txobj= cJSON_Parse(teststr)) != 0 ) + /*if ( (txobj= cJSON_Parse(teststr)) != 0 ) { bits256 txid; rawtx = bitcoin_json2hex(myinfo,coin,&txid,txobj,0); txobj = bitcoin_hex2json(coin,coin->blocks.hwmchain.height,&txid,0,rawtx,extraspace,extralen,0,0,suppress); printf("RAWTX.(%s) -> (%s)\n",rawtx,jprint(txobj,0)); - } - //retstr = iguana_validaterawtx(myinfo,coin,coin->blocks.hwmchain.height,&msgtx,extraspace,extralen,rawtx,0,suppress); + }*/ + retstr = iguana_validaterawtx(myinfo,coin,coin->blocks.hwmchain.height,&msgtx,extraspace,extralen,rawtx,0,suppress); free(extraspace); return(rawtx); } diff --git a/iguana/iguana_realtime.c b/iguana/iguana_realtime.c index 649e52310..876ef26cc 100755 --- a/iguana/iguana_realtime.c +++ b/iguana/iguana_realtime.c @@ -763,17 +763,23 @@ struct iguana_RTtxid *iguana_RTtxid_create(struct iguana_info *coin,struct iguan return(RTptr); } -int64_t _RTgettxout(struct iguana_info *coin,int32_t *height,int32_t *scriptlen,uint8_t *script,uint8_t *rmd160,char *coinaddr,bits256 txid,int32_t vout,int32_t mempool) +int64_t _RTgettxout(struct iguana_info *coin,int32_t *heightp,int32_t *scriptlenp,uint8_t *script,uint8_t *rmd160,char *coinaddr,bits256 txid,int32_t vout,int32_t mempool) { - int64_t value = 0; struct iguana_RTtxid *RTptr; struct iguana_RTunspent *unspent = 0; + int32_t scriptlen; int64_t value = 0; struct iguana_RTtxid *RTptr; struct iguana_RTunspent *unspent = 0; HASH_FIND(hh,coin->RTdataset,txid.bytes,sizeof(txid),RTptr); + *heightp = -1; + if ( scriptlenp == 0 ) + scriptlenp = &scriptlen; + *scriptlenp = 0; + memset(rmd160,0,20); + coinaddr[0] = 0; if ( RTptr != 0 && (RTptr->height <= coin->blocks.hwmchain.height || mempool != 0) ) { if ( vout >= 0 && vout < RTptr->txn_count && (unspent= RTptr->unspents[vout]) != 0 ) { - *height = RTptr->height; - if ( (*scriptlen= unspent->scriptlen) > 0 ) - memcpy(script,unspent->script,*scriptlen); + *heightp = RTptr->height; + if ( unspent->spend == 0 && (*scriptlenp= unspent->scriptlen) > 0 ) + memcpy(script,unspent->script,*scriptlenp); memcpy(rmd160,unspent->rmd160,sizeof(unspent->rmd160)); bitcoin_address(coinaddr,coin->chain->pubtype,rmd160,sizeof(unspent->rmd160)); value = unspent->value; @@ -782,6 +788,21 @@ int64_t _RTgettxout(struct iguana_info *coin,int32_t *height,int32_t *scriptlen, return(value); } +int32_t _iguana_RTunspentfind(struct supernet_info *myinfo,struct iguana_info *coin,bits256 *txidp,int32_t *voutp,uint8_t *spendscript,struct iguana_outpoint outpt,int64_t value) +{ + int32_t spendlen = 0; struct iguana_RTunspent *unspent; struct iguana_RTtxid *parent; + if ( outpt.isptr != 0 && (unspent= outpt.ptr) != 0 && (parent= unspent->parent) != 0 ) + { + if ( value != unspent->value ) + printf("_iguana_RTunspentfind: mismatched value %.8f != %.8f\n",dstr(value),dstr(unspent->value)); + if ( (spendlen= unspent->scriptlen) > 0 ) + memcpy(spendscript,unspent->script,spendlen); + *txidp = parent->txid; + *voutp = unspent->vout; + } + return(spendlen); +} + int32_t iguana_RTunspentindfind(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,uint8_t *spendscript,int32_t *spendlenp,uint64_t *valuep,int32_t *heightp,bits256 txid,int32_t vout,int32_t lasthdrsi,int32_t mempool) { char _coinaddr[64]; uint8_t rmd160[20]; int64_t value; @@ -796,19 +817,52 @@ int32_t iguana_RTunspentindfind(struct supernet_info *myinfo,struct iguana_info else return(iguana_unspentindfind(myinfo,coin,coinaddr,spendscript,spendlenp,valuep,heightp,txid,vout,lasthdrsi,mempool)); } -int32_t _iguana_RTunspentfind(struct supernet_info *myinfo,struct iguana_info *coin,bits256 *txidp,int32_t *voutp,uint8_t *spendscript,struct iguana_outpoint outpt,int64_t value) +int32_t iguana_txidheight(struct supernet_info *myinfo,struct iguana_info *coin,bits256 txid) { - int32_t spendlen = 0; struct iguana_RTunspent *unspent; struct iguana_RTtxid *parent; - if ( outpt.isptr != 0 && (unspent= outpt.ptr) != 0 && (parent= unspent->parent) != 0 ) + int32_t spendlen,height = 0; uint64_t value; char coinaddr[64]; uint8_t spendscript[IGUANA_MAXSCRIPTSIZE]; + iguana_RTunspentindfind(myinfo,coin,coinaddr,spendscript,&spendlen,&value,&height,txid,0,(coin->firstRTheight/coin->chain->bundlesize) - 1,0); + return(height); +} + +int64_t iguana_txidamount(struct supernet_info *myinfo,struct iguana_info *coin,bits256 txid,int32_t vout) +{ + int32_t spendlen,height = 0; uint64_t value; char coinaddr[64]; uint8_t spendscript[IGUANA_MAXSCRIPTSIZE]; + iguana_RTunspentindfind(myinfo,coin,coinaddr,spendscript,&spendlen,&value,&height,txid,vout,(coin->firstRTheight/coin->chain->bundlesize) - 1,0); + return(value); +} + +char *iguana_txidcategory(struct supernet_info *myinfo,struct iguana_info *coin,char *account,char *coinaddr,bits256 txid,int32_t vout) +{ + struct iguana_waccount *wacct; struct iguana_waddress *waddr; int32_t ismine=1,spendlen,height = 0; uint64_t value; uint8_t spendscript[IGUANA_MAXSCRIPTSIZE]; + iguana_RTunspentindfind(myinfo,coin,coinaddr,spendscript,&spendlen,&value,&height,txid,vout,(coin->firstRTheight/coin->chain->bundlesize) - 1,0); + account[0] = 0; + if ( coinaddr[0] != 0 ) { - if ( value != unspent->value ) - printf("_iguana_RTunspentfind: mismatched value %.8f != %.8f\n",dstr(value),dstr(unspent->value)); - if ( (spendlen= unspent->scriptlen) > 0 ) - memcpy(spendscript,unspent->script,spendlen); - *txidp = parent->txid; - *voutp = unspent->vout; - } - return(spendlen); + if ( (waddr= iguana_waddresssearch(myinfo,&wacct,coinaddr)) != 0 ) + { + if ( waddr->scriptlen != 0 ) + return("isp2sh"); + else if ( waddr->wifstr[0] != 0 ) + ismine = 1; + if ( wacct != 0 ) + strcpy(account,wacct->account); + } + } else account[0] = 0; + if ( value != 0 ) + { + if ( spendlen == 0 ) + { + if ( ismine != 0 ) + return("send"); + else return("spent"); + } + else + { + if ( ismine != 0 ) + return("receive"); + else return("unspent"); + } + } else return("unknown"); } void iguana_RTunmap(uint8_t *ptr,uint32_t len) diff --git a/iguana/iguana_wallet.c b/iguana/iguana_wallet.c index f4eaa7461..34318df97 100755 --- a/iguana/iguana_wallet.c +++ b/iguana/iguana_wallet.c @@ -1576,11 +1576,7 @@ STRING_AND_THREEINTS(bitcoinrpc,listtransactions,account,count,skip,includewatch }*/ item = cJSON_CreateObject(); jaddstr(item,"account",wacct->account); - jaddstr(item,"address",coinaddr); - jaddbits256(item,"txid",txid); - jaddnum(item,"vout",vout); - //return(bitcoinrpc_getrawtransaction(IGUANA_CALLARGS,txid,1)); - + iguana_txdetails(myinfo,coin,item,txid,vout,iguana_txidheight(myinfo,coin,txid)); jaddi(retarray,item); } } diff --git a/includes/iguana_funcs.h b/includes/iguana_funcs.h index 70d51ac90..f1d5f066d 100755 --- a/includes/iguana_funcs.h +++ b/includes/iguana_funcs.h @@ -520,6 +520,10 @@ struct gecko_chain *category_find(bits256 categoryhash,bits256 subhash); void *category_subscribe(struct supernet_info *myinfo,bits256 category,bits256 keyhash); char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); char *SuperNET_JSON(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *json,char *remoteaddr,uint16_t port); +int64_t iguana_txdetails(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *item,bits256 txid,int32_t vout,int32_t height); +int32_t iguana_txidheight(struct supernet_info *myinfo,struct iguana_info *coin,bits256 txid); +int64_t iguana_txidamount(struct supernet_info *myinfo,struct iguana_info *coin,bits256 txid,int32_t vout); +char *iguana_txidcategory(struct supernet_info *myinfo,struct iguana_info *coin,char *account,char *coinaddr,bits256 txid,int32_t vout); struct supernet_info *SuperNET_accountfind(cJSON *json); cJSON *SuperNET_rosettajson(struct supernet_info *myinfo,bits256 privkey,int32_t showprivs); double instantdex_aveprice(struct supernet_info *myinfo,struct exchange_quote *sortbuf,int32_t max,double *totalvolp,char *base,char *rel,double basevolume,cJSON *argjson);