forked from hush/hush3
![jonathan@leto.net](/assets/img/avatar_default.png)
83 changed files with 5260 additions and 799 deletions
@ -0,0 +1,114 @@ |
|||
import sys |
|||
sys.path.append('../../src/tui') |
|||
|
|||
from lib import tuilib |
|||
import unittest |
|||
|
|||
''' |
|||
specify chain ticker (daemon should be up), wif which will be imported and address to which you want to broadcast |
|||
added 1 second sleep after each case to surely not face the nSPV server limitation (1 call/second) |
|||
''' |
|||
|
|||
wif = '' |
|||
dest_address = 'RMjy5VkHFJkXTJDTJ3XX1zVzukP48sKyva' |
|||
amount = '0.1' |
|||
chain = 'ILN' |
|||
|
|||
rpc_proxy = tuilib.def_credentials(chain) |
|||
|
|||
|
|||
class TestNspvClient(unittest.TestCase): |
|||
|
|||
def test_nspv_getinfo(self): |
|||
print("testing nspv_getinfo") |
|||
result = rpc_proxy.nspv_getinfo() |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertGreater(result["height"], 2689) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_notarizations(self): |
|||
print("testing nspv_notarizations") |
|||
result = rpc_proxy.nspv_notarizations("2000") |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["prev"]["notarized_height"], 1998) |
|||
self.assertEqual(result["next"]["notarized_height"], 2002) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_hdrsproof(self): |
|||
print("testing nspv_hdrsproof") |
|||
result = rpc_proxy.nspv_hdrsproof("2000", "2100") |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["numhdrs"], 101) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_login(self): |
|||
print("testing nspv_login") |
|||
result = rpc_proxy.nspv_login(wif) |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["status"], "wif will expire in 777 seconds") |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_listunspent(self): |
|||
print("testing nspv_listunspent") |
|||
result = rpc_proxy.nspv_listunspent() |
|||
self.assertEqual(result["result"], "success") |
|||
time.sleep(1) |
|||
result = rpc_proxy.nspv_listunspent("RQ1mvCUcziWzRwE8Ugtex29VjoFjRzxQJT") |
|||
self.assertEqual(result["result"], "error") |
|||
|
|||
def test_nspv_spend(self): |
|||
print("testing nspv_spend") |
|||
result = rpc_proxy.nspv_login(wif) |
|||
result = rpc_proxy.nspv_spend(dest_address, amount) |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["vout"][0]["valueZat"], 10000000) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_broadcast(self): |
|||
print("testing nspv_broadcast") |
|||
result = rpc_proxy.nspv_login(wif) |
|||
broadcast_hex = rpc_proxy.nspv_spend(dest_address, amount)["hex"] |
|||
time.sleep(1) |
|||
result = rpc_proxy.nspv_broadcast(broadcast_hex) |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["retcode"], 1) |
|||
self.assertEqual(result["expected"], result["broadcast"]) |
|||
print("Broadcast txid: " + result["broadcast"]) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_logout(self): |
|||
print("testing nspv_logout") |
|||
rpc_proxy.nspv_login(wif) |
|||
time.sleep(1) |
|||
rpc_proxy.nspv_logout() |
|||
time.sleep(1) |
|||
result = rpc_proxy.nspv_spend(dest_address, amount) |
|||
self.assertEqual(result["result"], "error") |
|||
self.assertEqual(result["error"], "wif expired") |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_spentinfo(self): |
|||
print("testing nspv_spent_info") |
|||
result = rpc_proxy.nspv_spentinfo("67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418", "1") |
|||
self.assertEqual(result["result"], "success") |
|||
self.assertEqual(result["spentheight"], 2681) |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_txproof(self): |
|||
print("testing nspv_txproof") |
|||
result = rpc_proxy.nspv_txproof("67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418", "2673") |
|||
self.assertEqual(result["txid"], "67ffe0eaecd6081de04675c492a59090b573ee78955c4e8a85b8ac0be0e8e418") |
|||
time.sleep(1) |
|||
|
|||
def test_nspv_login_timout(self): |
|||
print("testing auto-logout in 777 seconds") |
|||
rpc_proxy.nspv_login(wif) |
|||
time.sleep(777) |
|||
result = rpc_proxy.nspv_spend(dest_address, amount) |
|||
self.assertEqual(result["result"], "error") |
|||
self.assertEqual(result["error"], "wif expired") |
|||
time.sleep(1) |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
unittest.main() |
File diff suppressed because it is too large
@ -0,0 +1,7 @@ |
|||
#!/bin/bash |
|||
|
|||
#set working directory to the location of this script |
|||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" |
|||
cd $DIR |
|||
|
|||
../komodo-cli -ac_name=HUSH3 "$@" |
@ -0,0 +1,476 @@ |
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
// todo:
|
|||
|
|||
// myprivkey, scrub all destination buffers
|
|||
// oversized tx
|
|||
|
|||
// headers "sync" make sure it connects to prior blocks to notarization. use getinfo hdrht to get missing hdrs
|
|||
|
|||
|
|||
// make sure to sanity check all vector lengths on receipt
|
|||
// make sure no files are updated (this is to allow nSPV=1 and later nSPV=0 without affecting database)
|
|||
// bug: under load, fullnode was returning all 0 nServices
|
|||
|
|||
#ifndef KOMODO_NSPV_H |
|||
#define KOMODO_NSPV_H |
|||
|
|||
int32_t iguana_rwbuf(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *buf) |
|||
{ |
|||
if ( rwflag != 0 ) |
|||
memcpy(serialized,buf,len); |
|||
else memcpy(buf,serialized,len); |
|||
return(len); |
|||
} |
|||
|
|||
int32_t NSPV_rwequihdr(int32_t rwflag,uint8_t *serialized,struct NSPV_equihdr *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nVersion),&ptr->nVersion); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashPrevBlock),(uint8_t *)&ptr->hashPrevBlock); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashMerkleRoot),(uint8_t *)&ptr->hashMerkleRoot); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashFinalSaplingRoot),(uint8_t *)&ptr->hashFinalSaplingRoot); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nTime),&ptr->nTime); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nBits),&ptr->nBits); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nNonce),(uint8_t *)&ptr->nNonce); |
|||
len += iguana_rwbuf(rwflag,&serialized[len],sizeof(ptr->nSolution),ptr->nSolution); |
|||
return(len); |
|||
} |
|||
|
|||
int32_t iguana_rwequihdrvec(int32_t rwflag,uint8_t *serialized,uint16_t *vecsizep,struct NSPV_equihdr **ptrp) |
|||
{ |
|||
int32_t i,vsize,len = 0; |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(*vecsizep),vecsizep); |
|||
if ( (vsize= *vecsizep) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vsize.%d ptrp.%p alloc %ld\n",vsize,*ptrp,sizeof(struct NSPV_equihdr)*vsize);
|
|||
if ( *ptrp == 0 ) |
|||
*ptrp = (struct NSPV_equihdr *)calloc(sizeof(struct NSPV_equihdr),vsize); // relies on uint16_t being "small" to prevent mem exhaustion
|
|||
for (i=0; i<vsize; i++) |
|||
len += NSPV_rwequihdr(rwflag,&serialized[len],&(*ptrp)[i]); |
|||
} |
|||
return(len); |
|||
} |
|||
|
|||
int32_t iguana_rwuint8vec(int32_t rwflag,uint8_t *serialized,int32_t *biglenp,uint8_t **ptrp) |
|||
{ |
|||
int32_t vsize,len = 0; |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(*biglenp),biglenp); |
|||
if ( (vsize= *biglenp) > 0 && vsize < MAX_TX_SIZE_AFTER_SAPLING ) |
|||
{ |
|||
if ( *ptrp == 0 ) |
|||
*ptrp = (uint8_t *)calloc(1,vsize); |
|||
len += iguana_rwbuf(rwflag,&serialized[len],vsize,*ptrp); |
|||
} |
|||
return(len); |
|||
} |
|||
|
|||
int32_t NSPV_rwutxoresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxoresp *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->extradata),&ptr->extradata); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); |
|||
return(len); |
|||
} |
|||
|
|||
int32_t NSPV_rwutxosresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxosresp *ptr) // check mempool
|
|||
{ |
|||
int32_t i,len = 0; |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numutxos),&ptr->numutxos); |
|||
if ( ptr->numutxos != 0 ) |
|||
{ |
|||
if ( ptr->utxos == 0 ) |
|||
ptr->utxos = (struct NSPV_utxoresp *)calloc(sizeof(*ptr->utxos),ptr->numutxos); // relies on uint16_t being "small" to prevent mem exhaustion
|
|||
for (i=0; i<ptr->numutxos; i++) |
|||
len += NSPV_rwutxoresp(rwflag,&serialized[len],&ptr->utxos[i]); |
|||
} |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->total),&ptr->total); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->interest),&ptr->interest); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nodeheight),&ptr->nodeheight); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->CCflag),&ptr->CCflag); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad8),&ptr->pad8); |
|||
if ( rwflag != 0 ) |
|||
{ |
|||
memcpy(&serialized[len],ptr->coinaddr,sizeof(ptr->coinaddr)); |
|||
len += sizeof(ptr->coinaddr); |
|||
} |
|||
else |
|||
{ |
|||
memcpy(ptr->coinaddr,&serialized[len],sizeof(ptr->coinaddr)); |
|||
len += sizeof(ptr->coinaddr); |
|||
} |
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_utxosresp_purge(struct NSPV_utxosresp *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
{ |
|||
if ( ptr->utxos != 0 ) |
|||
free(ptr->utxos); |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
} |
|||
|
|||
void NSPV_utxosresp_copy(struct NSPV_utxosresp *dest,struct NSPV_utxosresp *ptr) |
|||
{ |
|||
*dest = *ptr; |
|||
if ( ptr->utxos != 0 ) |
|||
{ |
|||
dest->utxos = (struct NSPV_utxoresp *)malloc(ptr->numutxos * sizeof(*ptr->utxos)); |
|||
memcpy(dest->utxos,ptr->utxos,ptr->numutxos * sizeof(*ptr->utxos)); |
|||
} |
|||
} |
|||
|
|||
int32_t NSPV_rwntz(int32_t rwflag,uint8_t *serialized,struct NSPV_ntz *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->othertxid),(uint8_t *)&ptr->othertxid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->txidheight),&ptr->txidheight); |
|||
return(len); |
|||
} |
|||
|
|||
int32_t NSPV_rwntzsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsresp *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += NSPV_rwntz(rwflag,&serialized[len],&ptr->prevntz); |
|||
len += NSPV_rwntz(rwflag,&serialized[len],&ptr->nextntz); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->reqheight),&ptr->reqheight); |
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_ntzsresp_copy(struct NSPV_ntzsresp *dest,struct NSPV_ntzsresp *ptr) |
|||
{ |
|||
*dest = *ptr; |
|||
} |
|||
|
|||
void NSPV_ntzsresp_purge(struct NSPV_ntzsresp *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
|
|||
int32_t NSPV_rwinforesp(int32_t rwflag,uint8_t *serialized,struct NSPV_inforesp *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += NSPV_rwntz(rwflag,&serialized[len],&ptr->notarization); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->hdrheight),&ptr->hdrheight); |
|||
len += NSPV_rwequihdr(rwflag,&serialized[len],&ptr->H); |
|||
//fprintf(stderr,"hdr rwlen.%d\n",len);
|
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_inforesp_purge(struct NSPV_inforesp *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
|
|||
int32_t NSPV_rwtxproof(int32_t rwflag,uint8_t *serialized,struct NSPV_txproof *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->unspentvalue),&ptr->unspentvalue); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); |
|||
len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txlen,&ptr->tx); |
|||
len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txprooflen,&ptr->txproof); |
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_txproof_copy(struct NSPV_txproof *dest,struct NSPV_txproof *ptr) |
|||
{ |
|||
*dest = *ptr; |
|||
if ( ptr->tx != 0 && ptr->txlen < MAX_TX_SIZE_AFTER_SAPLING ) |
|||
{ |
|||
dest->tx = (uint8_t *)malloc(ptr->txlen); |
|||
memcpy(dest->tx,ptr->tx,ptr->txlen); |
|||
} |
|||
if ( ptr->txproof != 0 ) |
|||
{ |
|||
dest->txproof = (uint8_t *)malloc(ptr->txprooflen); |
|||
memcpy(dest->txproof,ptr->txproof,ptr->txprooflen); |
|||
} |
|||
} |
|||
|
|||
void NSPV_txproof_purge(struct NSPV_txproof *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
{ |
|||
if ( ptr->tx != 0 ) |
|||
free(ptr->tx); |
|||
if ( ptr->txproof != 0 ) |
|||
free(ptr->txproof); |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
} |
|||
|
|||
int32_t NSPV_rwntzproofshared(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzproofshared *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwequihdrvec(rwflag,&serialized[len],&ptr->numhdrs,&ptr->hdrs); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevht),&ptr->prevht); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nextht),&ptr->nextht); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); |
|||
//fprintf(stderr,"rwcommon prev.%d next.%d\n",ptr->prevht,ptr->nextht);
|
|||
return(len); |
|||
} |
|||
|
|||
int32_t NSPV_rwntzsproofresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += NSPV_rwntzproofshared(rwflag,&serialized[len],&ptr->common); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->prevtxid),(uint8_t *)&ptr->prevtxid); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nexttxid),(uint8_t *)&ptr->nexttxid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevtxidht),&ptr->prevtxidht); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nexttxidht),&ptr->nexttxidht); |
|||
len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->prevtxlen,&ptr->prevntz); |
|||
len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->nexttxlen,&ptr->nextntz); |
|||
//fprintf(stderr,"retlen.%d\n",len);
|
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_ntzsproofresp_copy(struct NSPV_ntzsproofresp *dest,struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
*dest = *ptr; |
|||
if ( ptr->common.hdrs != 0 ) |
|||
{ |
|||
dest->common.hdrs = (struct NSPV_equihdr *)malloc(ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); |
|||
memcpy(dest->common.hdrs,ptr->common.hdrs,ptr->common.numhdrs * sizeof(*ptr->common.hdrs)); |
|||
} |
|||
if ( ptr->prevntz != 0 ) |
|||
{ |
|||
dest->prevntz = (uint8_t *)malloc(ptr->prevtxlen); |
|||
memcpy(dest->prevntz,ptr->prevntz,ptr->prevtxlen); |
|||
} |
|||
if ( ptr->nextntz != 0 ) |
|||
{ |
|||
dest->nextntz = (uint8_t *)malloc(ptr->nexttxlen); |
|||
memcpy(dest->nextntz,ptr->nextntz,ptr->nexttxlen); |
|||
} |
|||
} |
|||
|
|||
void NSPV_ntzsproofresp_purge(struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
{ |
|||
if ( ptr->common.hdrs != 0 ) |
|||
free(ptr->common.hdrs); |
|||
if ( ptr->prevntz != 0 ) |
|||
free(ptr->prevntz); |
|||
if ( ptr->nextntz != 0 ) |
|||
free(ptr->nextntz); |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
} |
|||
|
|||
int32_t NSPV_rwspentinfo(int32_t rwflag,uint8_t *serialized,struct NSPV_spentinfo *ptr) // check mempool
|
|||
{ |
|||
int32_t len = 0; |
|||
len += NSPV_rwtxproof(rwflag,&serialized[len],&ptr->spent); |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->spentvini),&ptr->spentvini); |
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_spentinfo_purge(struct NSPV_spentinfo *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
{ |
|||
NSPV_txproof_purge(&ptr->spent); |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
} |
|||
|
|||
int32_t NSPV_rwbroadcastresp(int32_t rwflag,uint8_t *serialized,struct NSPV_broadcastresp *ptr) |
|||
{ |
|||
int32_t len = 0; |
|||
len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); |
|||
len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->retcode),&ptr->retcode); |
|||
return(len); |
|||
} |
|||
|
|||
void NSPV_broadcast_purge(struct NSPV_broadcastresp *ptr) |
|||
{ |
|||
if ( ptr != 0 ) |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
} |
|||
|
|||
// useful utility functions
|
|||
|
|||
uint256 NSPV_doublesha256(uint8_t *data,int32_t datalen) |
|||
{ |
|||
bits256 _hash; uint256 hash; int32_t i; |
|||
_hash = bits256_doublesha256(0,data,datalen); |
|||
for (i=0; i<32; i++) |
|||
((uint8_t *)&hash)[i] = _hash.bytes[31 - i]; |
|||
return(hash); |
|||
} |
|||
|
|||
uint256 NSPV_hdrhash(struct NSPV_equihdr *hdr) |
|||
{ |
|||
CBlockHeader block; |
|||
block.nVersion = hdr->nVersion; |
|||
block.hashPrevBlock = hdr->hashPrevBlock; |
|||
block.hashMerkleRoot = hdr->hashMerkleRoot; |
|||
block.hashFinalSaplingRoot = hdr->hashFinalSaplingRoot; |
|||
block.nTime = hdr->nTime; |
|||
block.nBits = hdr->nBits; |
|||
block.nNonce = hdr->nNonce; |
|||
block.nSolution.resize(sizeof(hdr->nSolution)); |
|||
memcpy(&block.nSolution[0],hdr->nSolution,sizeof(hdr->nSolution)); |
|||
return(block.GetHash()); |
|||
} |
|||
|
|||
int32_t NSPV_txextract(CTransaction &tx,uint8_t *data,int32_t datalen) |
|||
{ |
|||
std::vector<uint8_t> rawdata; |
|||
if ( datalen < MAX_TX_SIZE_AFTER_SAPLING ) |
|||
{ |
|||
rawdata.resize(datalen); |
|||
memcpy(&rawdata[0],data,datalen); |
|||
if ( DecodeHexTx(tx,HexStr(rawdata)) != 0 ) |
|||
return(0); |
|||
} |
|||
return(-1); |
|||
} |
|||
|
|||
bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime); |
|||
|
|||
int32_t NSPV_fastnotariescount(CTransaction tx,uint8_t elected[64][33],uint32_t nTime) |
|||
{ |
|||
CPubKey pubkeys[64]; uint8_t sig[512]; CScript scriptPubKeys[64]; CMutableTransaction mtx(tx); int32_t vini,j,siglen,retval; uint64_t mask = 0; char *str; std::vector<std::vector<unsigned char>> vData; |
|||
for (j=0; j<64; j++) |
|||
{ |
|||
pubkeys[j] = buf2pk(elected[j]); |
|||
scriptPubKeys[j] = (CScript() << ParseHex(HexStr(pubkeys[j])) << OP_CHECKSIG); |
|||
//fprintf(stderr,"%d %s\n",j,HexStr(pubkeys[j]).c_str());
|
|||
} |
|||
fprintf(stderr,"txid %s\n",tx.GetHash().GetHex().c_str()); |
|||
//for (vini=0; vini<tx.vin.size(); vini++)
|
|||
// mtx.vin[vini].scriptSig.resize(0);
|
|||
for (vini=0; vini<tx.vin.size(); vini++) |
|||
{ |
|||
CScript::const_iterator pc = tx.vin[vini].scriptSig.begin(); |
|||
if ( tx.vin[vini].scriptSig.GetPushedData(pc,vData) != 0 ) |
|||
{ |
|||
vData[0].pop_back(); |
|||
for (j=0; j<64; j++) |
|||
{ |
|||
if ( ((1LL << j) & mask) != 0 ) |
|||
continue; |
|||
char coinaddr[64]; Getscriptaddress(coinaddr,scriptPubKeys[j]); |
|||
NSPV_SignTx(mtx,vini,10000,scriptPubKeys[j],nTime); // sets SIG_TXHASH
|
|||
if ( (retval= pubkeys[j].Verify(SIG_TXHASH,vData[0])) != 0 ) |
|||
{ |
|||
fprintf(stderr,"(vini.%d %s.%d) ",vini,coinaddr,retval); |
|||
mask |= (1LL << j); |
|||
break; |
|||
} |
|||
} |
|||
fprintf(stderr," verified %llx\n",(long long)mask); |
|||
} |
|||
} |
|||
return(bitweight(mask)); |
|||
} |
|||
|
|||
/*
|
|||
NSPV_notariescount is the slowest process during full validation as it requires looking up 13 transactions. |
|||
one way that would be 10000x faster would be to bruteforce validate the signatures in each vin, against all 64 pubkeys! for a valid tx, that is on average 13*32 secp256k1/sapling verify operations, which is much faster than even a single network request. |
|||
Unfortunately, due to the complexity of calculating the hash to sign for a tx, this bruteforcing would require determining what type of signature method and having sapling vs legacy methods of calculating the txhash. |
|||
It could be that the fullnode side could calculate this and send it back to the superlite side as any hash that would validate 13 different ways has to be the valid txhash. |
|||
However, since the vouts being spent by the notaries are highly constrained p2pk vouts, the txhash can be deduced if a specific notary pubkey is indeed the signer |
|||
*/ |
|||
int32_t NSPV_notariescount(CTransaction tx,uint8_t elected[64][33]) |
|||
{ |
|||
uint8_t *script; CTransaction vintx; int64_t rewardsum = 0; int32_t i,j,utxovout,scriptlen,numsigs = 0; |
|||
for (i=0; i<tx.vin.size(); i++) |
|||
{ |
|||
utxovout = tx.vin[i].prevout.n; |
|||
if ( NSPV_gettransaction(1,utxovout,tx.vin[i].prevout.hash,0,vintx,-1,0,rewardsum) != 0 ) |
|||
{ |
|||
fprintf(stderr,"error getting %s/v%d\n",tx.vin[i].prevout.hash.GetHex().c_str(),utxovout); |
|||
return(numsigs); |
|||
} |
|||
if ( utxovout < vintx.vout.size() ) |
|||
{ |
|||
script = (uint8_t *)&vintx.vout[utxovout].scriptPubKey[0]; |
|||
if ( (scriptlen= vintx.vout[utxovout].scriptPubKey.size()) == 35 ) |
|||
{ |
|||
for (j=0; j<64; j++) |
|||
if ( memcmp(&script[1],elected[j],33) == 0 ) |
|||
{ |
|||
numsigs++; |
|||
break; |
|||
} |
|||
} else fprintf(stderr,"invalid scriptlen.%d\n",scriptlen); |
|||
} else fprintf(stderr,"invalid utxovout.%d vs %d\n",utxovout,(int32_t)vintx.vout.size()); |
|||
} |
|||
return(numsigs); |
|||
} |
|||
|
|||
uint256 NSPV_opretextract(int32_t *heightp,uint256 *blockhashp,char *symbol,std::vector<uint8_t> opret,uint256 txid) |
|||
{ |
|||
uint256 desttxid; int32_t i; |
|||
iguana_rwnum(0,&opret[32],sizeof(*heightp),heightp); |
|||
for (i=0; i<32; i++) |
|||
((uint8_t *)blockhashp)[i] = opret[i]; |
|||
for (i=0; i<32; i++) |
|||
((uint8_t *)&desttxid)[i] = opret[4 + 32 + i]; |
|||
if ( 0 && *heightp != 2690 ) |
|||
fprintf(stderr," ntzht.%d %s <- txid.%s size.%d\n",*heightp,(*blockhashp).GetHex().c_str(),(txid).GetHex().c_str(),(int32_t)opret.size()); |
|||
return(desttxid); |
|||
} |
|||
|
|||
int32_t NSPV_notarizationextract(int32_t verifyntz,int32_t *ntzheightp,uint256 *blockhashp,uint256 *desttxidp,CTransaction tx) |
|||
{ |
|||
int32_t numsigs=0; uint8_t elected[64][33]; char *symbol; std::vector<uint8_t> opret; uint32_t nTime; |
|||
if ( tx.vout.size() >= 2 ) |
|||
{ |
|||
symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; |
|||
GetOpReturnData(tx.vout[1].scriptPubKey,opret); |
|||
if ( opret.size() >= 32*2+4 ) |
|||
{ |
|||
//sleep(1); // needed to avoid no pnodes error
|
|||
*desttxidp = NSPV_opretextract(ntzheightp,blockhashp,symbol,opret,tx.GetHash()); |
|||
nTime = NSPV_blocktime(*ntzheightp); |
|||
komodo_notaries(elected,*ntzheightp,nTime); |
|||
if ( verifyntz != 0 && (numsigs= NSPV_fastnotariescount(tx,elected,nTime)) < 12 ) |
|||
{ |
|||
fprintf(stderr,"numsigs.%d error\n",numsigs); |
|||
return(-3); |
|||
} |
|||
return(0); |
|||
} |
|||
else |
|||
{ |
|||
fprintf(stderr,"opretsize.%d error\n",(int32_t)opret.size()); |
|||
return(-2); |
|||
} |
|||
} else return(-1); |
|||
} |
|||
#endif // KOMODO_NSPV_H
|
@ -0,0 +1,144 @@ |
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef KOMODO_NSPV_DEFSH |
|||
#define KOMODO_NSPV_DEFSH |
|||
|
|||
#define NSPV_POLLITERS 100 |
|||
#define NSPV_POLLMICROS 50000 |
|||
#define NSPV_MAXVINS 64 |
|||
#define NSPV_AUTOLOGOUT 777 |
|||
#define NSPV_BRANCHID 0x76b809bb |
|||
|
|||
// nSPV defines and struct definitions with serialization and purge functions
|
|||
|
|||
#define NSPV_INFO 0x00 |
|||
#define NSPV_INFORESP 0x01 |
|||
#define NSPV_UTXOS 0x02 |
|||
#define NSPV_UTXOSRESP 0x03 |
|||
#define NSPV_NTZS 0x04 |
|||
#define NSPV_NTZSRESP 0x05 |
|||
#define NSPV_NTZSPROOF 0x06 |
|||
#define NSPV_NTZSPROOFRESP 0x07 |
|||
#define NSPV_TXPROOF 0x08 |
|||
#define NSPV_TXPROOFRESP 0x09 |
|||
#define NSPV_SPENTINFO 0x0a |
|||
#define NSPV_SPENTINFORESP 0x0b |
|||
#define NSPV_BROADCAST 0x0c |
|||
#define NSPV_BROADCASTRESP 0x0d |
|||
|
|||
int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx,int64_t extradata,uint32_t tiptime,int64_t &rewardsum); |
|||
UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis); |
|||
extern uint256 SIG_TXHASH; |
|||
uint32_t NSPV_blocktime(int32_t hdrheight); |
|||
|
|||
struct NSPV_equihdr |
|||
{ |
|||
int32_t nVersion; |
|||
uint256 hashPrevBlock; |
|||
uint256 hashMerkleRoot; |
|||
uint256 hashFinalSaplingRoot; |
|||
uint32_t nTime; |
|||
uint32_t nBits; |
|||
uint256 nNonce; |
|||
uint8_t nSolution[1344]; |
|||
}; |
|||
|
|||
struct NSPV_utxoresp |
|||
{ |
|||
uint256 txid; |
|||
int64_t satoshis,extradata; |
|||
int32_t vout,height; |
|||
}; |
|||
|
|||
struct NSPV_utxosresp |
|||
{ |
|||
struct NSPV_utxoresp *utxos; |
|||
char coinaddr[64]; |
|||
int64_t total,interest; |
|||
int32_t nodeheight; |
|||
uint16_t numutxos; uint8_t CCflag,pad8; |
|||
}; |
|||
|
|||
struct NSPV_ntz |
|||
{ |
|||
uint256 blockhash,txid,othertxid; |
|||
int32_t height,txidheight; |
|||
}; |
|||
|
|||
struct NSPV_ntzsresp |
|||
{ |
|||
struct NSPV_ntz prevntz,nextntz; |
|||
int32_t reqheight; |
|||
}; |
|||
|
|||
struct NSPV_inforesp |
|||
{ |
|||
struct NSPV_ntz notarization; |
|||
uint256 blockhash; |
|||
int32_t height,hdrheight; |
|||
struct NSPV_equihdr H; |
|||
}; |
|||
|
|||
struct NSPV_txproof |
|||
{ |
|||
uint256 txid; |
|||
int64_t unspentvalue; |
|||
int32_t height,vout,txlen,txprooflen; |
|||
uint8_t *tx,*txproof; |
|||
}; |
|||
|
|||
struct NSPV_ntzproofshared |
|||
{ |
|||
struct NSPV_equihdr *hdrs; |
|||
int32_t prevht,nextht,pad32; |
|||
uint16_t numhdrs,pad16; |
|||
}; |
|||
|
|||
struct NSPV_ntzsproofresp |
|||
{ |
|||
struct NSPV_ntzproofshared common; |
|||
uint256 prevtxid,nexttxid; |
|||
int32_t prevtxidht,nexttxidht,prevtxlen,nexttxlen; |
|||
uint8_t *prevntz,*nextntz; |
|||
}; |
|||
|
|||
struct NSPV_MMRproof |
|||
{ |
|||
struct NSPV_ntzproofshared common; |
|||
// tbd
|
|||
}; |
|||
|
|||
struct NSPV_spentinfo |
|||
{ |
|||
struct NSPV_txproof spent; |
|||
uint256 txid; |
|||
int32_t vout,spentvini; |
|||
}; |
|||
|
|||
struct NSPV_broadcastresp |
|||
{ |
|||
uint256 txid; |
|||
int32_t retcode; |
|||
}; |
|||
|
|||
struct NSPV_CCmtxinfo |
|||
{ |
|||
struct NSPV_utxosresp U; |
|||
struct NSPV_utxoresp used[NSPV_MAXVINS]; |
|||
}; |
|||
|
|||
#endif // KOMODO_NSPV_DEFSH
|
@ -0,0 +1,523 @@ |
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef KOMODO_NSPVFULLNODE_H |
|||
#define KOMODO_NSPVFULLNODE_H |
|||
|
|||
// NSPV_get... functions need to return the exact serialized length, which is the size of the structure minus size of pointers, plus size of allocated data
|
|||
|
|||
#include "notarisationdb.h" |
|||
|
|||
struct NSPV_ntzargs |
|||
{ |
|||
uint256 txid,desttxid,blockhash; |
|||
int32_t txidht,ntzheight; |
|||
}; |
|||
|
|||
int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir) |
|||
{ |
|||
int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarisation nota; char *symbol; std::vector<uint8_t> opret; |
|||
symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; |
|||
memset(args,0,sizeof(*args)); |
|||
if ( dir > 0 ) |
|||
height += 10; |
|||
if ( (args->txidht= ScanNotarisationsDB(height,symbol,1440,nota)) == 0 ) |
|||
return(-1); |
|||
args->txid = nota.first; |
|||
if ( !GetTransaction(args->txid,tx,hashBlock,false) || tx.vout.size() < 2 ) |
|||
return(-2); |
|||
GetOpReturnData(tx.vout[1].scriptPubKey,opret); |
|||
if ( opret.size() >= 32*2+4 ) |
|||
args->desttxid = NSPV_opretextract(&args->ntzheight,&args->blockhash,symbol,opret,args->txid); |
|||
return(args->ntzheight); |
|||
} |
|||
|
|||
int32_t NSPV_notarized_bracket(struct NSPV_ntzargs *prev,struct NSPV_ntzargs *next,int32_t height) |
|||
{ |
|||
uint256 bhash; int32_t txidht,ntzht,nextht,i=0; |
|||
memset(prev,0,sizeof(*prev)); |
|||
memset(next,0,sizeof(*next)); |
|||
if ( (ntzht= NSPV_notarization_find(prev,height,-1)) < 0 || ntzht > height || ntzht == 0 ) |
|||
return(-1); |
|||
txidht = height+1; |
|||
while ( (ntzht= NSPV_notarization_find(next,txidht,1)) < height ) |
|||
{ |
|||
nextht = next->txidht + 10*i; |
|||
//fprintf(stderr,"found forward ntz, but ntzht.%d vs height.%d, txidht.%d -> nextht.%d\n",next->ntzheight,height,txidht,nextht);
|
|||
memset(next,0,sizeof(*next)); |
|||
txidht = nextht; |
|||
if ( ntzht <= 0 ) |
|||
break; |
|||
if ( i++ > 10 ) |
|||
break; |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight) |
|||
{ |
|||
ptr->blockhash = *chainActive[ntzheight]->phashBlock; |
|||
ptr->height = ntzheight; |
|||
ptr->txidheight = txidht; |
|||
ptr->othertxid = desttxid; |
|||
ptr->txid = ntztxid; |
|||
return(0); |
|||
} |
|||
|
|||
int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t origreqheight) |
|||
{ |
|||
struct NSPV_ntzargs prev,next; int32_t reqheight = origreqheight; |
|||
if ( reqheight < chainActive.LastTip()->GetHeight() ) |
|||
reqheight++; |
|||
if ( NSPV_notarized_bracket(&prev,&next,reqheight) == 0 ) |
|||
{ |
|||
if ( prev.ntzheight != 0 ) |
|||
{ |
|||
ptr->reqheight = origreqheight; |
|||
if ( NSPV_ntzextract(&ptr->prevntz,prev.txid,prev.txidht,prev.desttxid,prev.ntzheight) < 0 ) |
|||
return(-1); |
|||
} |
|||
if ( next.ntzheight != 0 ) |
|||
{ |
|||
if ( NSPV_ntzextract(&ptr->nextntz,next.txid,next.txidht,next.desttxid,next.ntzheight) < 0 ) |
|||
return(-1); |
|||
} |
|||
} |
|||
return(sizeof(*ptr)); |
|||
} |
|||
|
|||
int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) |
|||
{ |
|||
CBlockIndex *pindex; |
|||
if ( (pindex= komodo_chainactive(height)) != 0 ) |
|||
{ |
|||
hdr->nVersion = pindex->nVersion; |
|||
if ( pindex->pprev == 0 ) |
|||
return(-1); |
|||
hdr->hashPrevBlock = pindex->pprev->GetBlockHash(); |
|||
hdr->hashMerkleRoot = pindex->hashMerkleRoot; |
|||
hdr->hashFinalSaplingRoot = pindex->hashFinalSaplingRoot; |
|||
hdr->nTime = pindex->nTime; |
|||
hdr->nBits = pindex->nBits; |
|||
hdr->nNonce = pindex->nNonce; |
|||
memcpy(hdr->nSolution,&pindex->nSolution[0],sizeof(hdr->nSolution)); |
|||
return(sizeof(*hdr)); |
|||
} |
|||
return(-1); |
|||
} |
|||
|
|||
int32_t NSPV_getinfo(struct NSPV_inforesp *ptr,int32_t reqheight) |
|||
{ |
|||
int32_t prevMoMheight,len = 0; CBlockIndex *pindex; struct NSPV_ntzsresp pair; |
|||
if ( (pindex= chainActive.LastTip()) != 0 ) |
|||
{ |
|||
ptr->height = pindex->GetHeight(); |
|||
ptr->blockhash = pindex->GetBlockHash(); |
|||
memset(&pair,0,sizeof(pair)); |
|||
if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 ) |
|||
return(-1); |
|||
ptr->notarization = pair.prevntz; |
|||
if ( reqheight == 0 ) |
|||
reqheight = ptr->height; |
|||
ptr->hdrheight = reqheight; |
|||
if ( NSPV_setequihdr(&ptr->H,reqheight) < 0 ) |
|||
return(-1); |
|||
return(sizeof(*ptr)); |
|||
} else return(-1); |
|||
} |
|||
|
|||
int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC) // check mempool
|
|||
{ |
|||
int64_t total = 0,interest=0; uint32_t locktime; int32_t tipheight,maxlen,txheight,n = 0,len = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
SetCCunspents(unspentOutputs,coinaddr,isCC); |
|||
maxlen = MAX_BLOCK_SIZE(tipheight) - 512; |
|||
maxlen /= sizeof(*ptr->utxos); |
|||
strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); |
|||
ptr->CCflag = isCC; |
|||
if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) >= 0 && ptr->numutxos < maxlen ) |
|||
{ |
|||
tipheight = chainActive.LastTip()->GetHeight(); |
|||
ptr->nodeheight = tipheight; |
|||
ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos,sizeof(*ptr->utxos)); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
ptr->utxos[n].txid = it->first.txhash; |
|||
ptr->utxos[n].vout = (int32_t)it->first.index; |
|||
ptr->utxos[n].satoshis = it->second.satoshis; |
|||
ptr->utxos[n].height = it->second.blockHeight; |
|||
if ( ASSETCHAINS_SYMBOL[0] == 0 && it->second.satoshis >= 10*COIN ) |
|||
{ |
|||
ptr->utxos[n].extradata = komodo_accrued_interest(&txheight,&locktime,ptr->utxos[n].txid,ptr->utxos[n].vout,ptr->utxos[n].height,ptr->utxos[n].satoshis,tipheight); |
|||
interest += ptr->utxos[n].extradata; |
|||
} |
|||
total += it->second.satoshis; |
|||
n++; |
|||
} |
|||
if ( len < maxlen ) |
|||
{ |
|||
len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos)); |
|||
//fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len);
|
|||
if ( n == ptr->numutxos ) |
|||
{ |
|||
ptr->total = total; |
|||
ptr->interest = interest; |
|||
return(len); |
|||
} |
|||
} |
|||
} |
|||
if ( ptr->utxos != 0 ) |
|||
free(ptr->utxos); |
|||
memset(ptr,0,sizeof(*ptr)); |
|||
return(0); |
|||
} |
|||
|
|||
uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,int32_t *txlenp,uint256 txid) |
|||
{ |
|||
uint8_t *rawtx = 0; |
|||
*txlenp = 0; |
|||
{ |
|||
if (!GetTransaction(txid, tx, hashBlock, false)) |
|||
return(0); |
|||
string strHex = EncodeHexTx(tx); |
|||
*txlenp = (int32_t)strHex.size() >> 1; |
|||
if ( *txlenp > 0 ) |
|||
{ |
|||
rawtx = (uint8_t *)calloc(1,*txlenp); |
|||
decode_hex(rawtx,*txlenp,(char *)strHex.c_str()); |
|||
} |
|||
} |
|||
return(rawtx); |
|||
} |
|||
|
|||
int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n) |
|||
{ |
|||
CTransaction tx; |
|||
ptr->retcode = 0; |
|||
if ( NSPV_txextract(tx,data,n) == 0 ) |
|||
{ |
|||
//LOCK(cs_main);
|
|||
ptr->txid = tx.GetHash(); |
|||
//fprintf(stderr,"try to addmempool transaction %s\n",ptr->txid.GetHex().c_str());
|
|||
if ( myAddtomempool(tx) != 0 ) |
|||
{ |
|||
ptr->retcode = 1; |
|||
//int32_t i;
|
|||
//for (i=0; i<n; i++)
|
|||
// fprintf(stderr,"%02x",data[i]);
|
|||
fprintf(stderr," relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode); |
|||
RelayTransaction(tx); |
|||
} else ptr->retcode = -3; |
|||
|
|||
} else ptr->retcode = -1; |
|||
return(sizeof(*ptr)); |
|||
} |
|||
|
|||
int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,int32_t vout,uint256 txid,int32_t height) |
|||
{ |
|||
int32_t flag = 0,len = 0; CTransaction _tx; uint256 hashBlock; CBlock block; CBlockIndex *pindex; |
|||
if ( (ptr->tx= NSPV_getrawtx(_tx,hashBlock,&ptr->txlen,txid)) == 0 ) |
|||
return(-1); |
|||
ptr->txid = txid; |
|||
ptr->vout = vout; |
|||
ptr->height = height; |
|||
if ( height != 0 && (pindex= komodo_chainactive(height)) != 0 && komodo_blockload(block,pindex) == 0 ) |
|||
{ |
|||
BOOST_FOREACH(const CTransaction&tx, block.vtx) |
|||
{ |
|||
if ( tx.GetHash() == txid ) |
|||
{ |
|||
flag = 1; |
|||
break; |
|||
} |
|||
} |
|||
if ( flag != 0 ) |
|||
{ |
|||
set<uint256> setTxids; |
|||
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); |
|||
setTxids.insert(txid); |
|||
CMerkleBlock mb(block, setTxids); |
|||
ssMB << mb; |
|||
std::vector<uint8_t> proof(ssMB.begin(), ssMB.end()); |
|||
ptr->txprooflen = (int32_t)proof.size(); |
|||
//fprintf(stderr,"%s txproof.(%s)\n",txid.GetHex().c_str(),HexStr(proof).c_str());
|
|||
if ( ptr->txprooflen > 0 ) |
|||
{ |
|||
ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen); |
|||
memcpy(ptr->txproof,&proof[0],ptr->txprooflen); |
|||
} |
|||
//fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen));
|
|||
} |
|||
} |
|||
ptr->unspentvalue = CCgettxout(txid,vout,1,1); |
|||
return(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen); |
|||
} |
|||
|
|||
int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp *ptr,uint256 prevntztxid,uint256 nextntztxid) |
|||
{ |
|||
int32_t i; uint256 hashBlock,bhash0,bhash1,desttxid0,desttxid1; CTransaction tx; |
|||
ptr->prevtxid = prevntztxid; |
|||
ptr->prevntz = NSPV_getrawtx(tx,hashBlock,&ptr->prevtxlen,ptr->prevtxid); |
|||
ptr->prevtxidht = komodo_blockheight(hashBlock); |
|||
if ( NSPV_notarizationextract(0,&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 ) |
|||
return(-2); |
|||
else if ( komodo_blockheight(bhash0) != ptr->common.prevht ) |
|||
return(-3); |
|||
|
|||
ptr->nexttxid = nextntztxid; |
|||
ptr->nextntz = NSPV_getrawtx(tx,hashBlock,&ptr->nexttxlen,ptr->nexttxid); |
|||
ptr->nexttxidht = komodo_blockheight(hashBlock); |
|||
if ( NSPV_notarizationextract(0,&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 ) |
|||
return(-5); |
|||
else if ( komodo_blockheight(bhash1) != ptr->common.nextht ) |
|||
return(-6); |
|||
|
|||
else if ( ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440 ) |
|||
{ |
|||
fprintf(stderr,"illegal prevht.%d nextht.%d\n",ptr->common.prevht,ptr->common.nextht); |
|||
return(-7); |
|||
} |
|||
//fprintf(stderr,"%s -> prevht.%d, %s -> nexht.%d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht);
|
|||
ptr->common.numhdrs = (ptr->common.nextht - ptr->common.prevht + 1); |
|||
ptr->common.hdrs = (struct NSPV_equihdr *)calloc(ptr->common.numhdrs,sizeof(*ptr->common.hdrs)); |
|||
//fprintf(stderr,"prev.%d next.%d allocate numhdrs.%d\n",prevht,nextht,ptr->common.numhdrs);
|
|||
for (i=0; i<ptr->common.numhdrs; i++) |
|||
{ |
|||
//hashBlock = NSPV_hdrhash(&ptr->common.hdrs[i]);
|
|||
//fprintf(stderr,"hdr[%d] %s\n",prevht+i,hashBlock.GetHex().c_str());
|
|||
if ( NSPV_setequihdr(&ptr->common.hdrs[i],ptr->common.prevht+i) < 0 ) |
|||
{ |
|||
fprintf(stderr,"error setting hdr.%d\n",ptr->common.prevht+i); |
|||
free(ptr->common.hdrs); |
|||
ptr->common.hdrs = 0; |
|||
return(-1); |
|||
} |
|||
} |
|||
//fprintf(stderr,"sizeof ptr %ld, common.%ld lens.%d %d\n",sizeof(*ptr),sizeof(ptr->common),ptr->prevtxlen,ptr->nexttxlen);
|
|||
return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevtxlen + ptr->nexttxlen); |
|||
} |
|||
|
|||
int32_t NSPV_getspentinfo(struct NSPV_spentinfo *ptr,uint256 txid,int32_t vout) |
|||
{ |
|||
int32_t len = 0; |
|||
ptr->txid = txid; |
|||
ptr->vout = vout; |
|||
ptr->spentvini = -1; |
|||
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->spent.tx) - sizeof(ptr->spent.txproof)); |
|||
if ( CCgetspenttxid(ptr->spent.txid,ptr->spentvini,ptr->spent.height,txid,vout) == 0 ) |
|||
{ |
|||
if ( NSPV_gettxproof(&ptr->spent,0,ptr->spent.txid,ptr->spent.height) > 0 ) |
|||
len += ptr->spent.txlen + ptr->spent.txprooflen; |
|||
else |
|||
{ |
|||
NSPV_txproof_purge(&ptr->spent); |
|||
return(-1); |
|||
} |
|||
} |
|||
return(len); |
|||
} |
|||
|
|||
void komodo_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a request
|
|||
{ |
|||
int32_t len,slen,ind,reqheight; std::vector<uint8_t> response; uint32_t timestamp = (uint32_t)time(NULL); |
|||
if ( (len= request.size()) > 0 ) |
|||
{ |
|||
if ( (ind= request[0]>>1) >= sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes) ) |
|||
ind = (int32_t)(sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes)) - 1; |
|||
if ( pfrom->prevtimes[ind] > timestamp ) |
|||
pfrom->prevtimes[ind] = 0; |
|||
if ( request[0] == NSPV_INFO ) // info
|
|||
{ |
|||
//fprintf(stderr,"check info %u vs %u, ind.%d\n",timestamp,pfrom->prevtimes[ind],ind);
|
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_inforesp I; |
|||
if ( len == 1+sizeof(reqheight) ) |
|||
iguana_rwnum(0,&request[1],sizeof(reqheight),&reqheight); |
|||
else reqheight = 0; |
|||
//fprintf(stderr,"request height.%d\n",reqheight);
|
|||
memset(&I,0,sizeof(I)); |
|||
if ( (slen= NSPV_getinfo(&I,reqheight)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_INFORESP; |
|||
//fprintf(stderr,"slen.%d\n",slen);
|
|||
if ( NSPV_rwinforesp(1,&response[1],&I) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_inforesp_purge(&I); |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_UTXOS ) |
|||
{ |
|||
//fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len);
|
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_utxosresp U; char coinaddr[64]; |
|||
if ( len < 64 && (request[1] == len-2 || request[1] == len-3) ) |
|||
{ |
|||
uint8_t isCC = 0; |
|||
memcpy(coinaddr,&request[2],request[1]); |
|||
coinaddr[request[1]] = 0; |
|||
if ( request[1] == len-3 ) |
|||
isCC = (request[len-1] != 0); |
|||
if ( isCC != 0 ) |
|||
fprintf(stderr,"%s isCC.%d\n",coinaddr,isCC); |
|||
memset(&U,0,sizeof(U)); |
|||
if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_UTXOSRESP; |
|||
if ( NSPV_rwutxosresp(1,&response[1],&U) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_utxosresp_purge(&U); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_NTZS ) |
|||
{ |
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_ntzsresp N; int32_t height; |
|||
if ( len == 1+sizeof(height) ) |
|||
{ |
|||
iguana_rwnum(0,&request[1],sizeof(height),&height); |
|||
memset(&N,0,sizeof(N)); |
|||
if ( (slen= NSPV_getntzsresp(&N,height)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_NTZSRESP; |
|||
if ( NSPV_rwntzsresp(1,&response[1],&N) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_ntzsresp_purge(&N); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_NTZSPROOF ) |
|||
{ |
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_ntzsproofresp P; uint256 prevntz,nextntz; |
|||
if ( len == 1+sizeof(prevntz)+sizeof(nextntz) ) |
|||
{ |
|||
iguana_rwbignum(0,&request[1],sizeof(prevntz),(uint8_t *)&prevntz); |
|||
iguana_rwbignum(0,&request[1+sizeof(prevntz)],sizeof(nextntz),(uint8_t *)&nextntz); |
|||
memset(&P,0,sizeof(P)); |
|||
if ( (slen= NSPV_getntzsproofresp(&P,prevntz,nextntz)) > 0 ) |
|||
{ |
|||
// fprintf(stderr,"slen.%d msg prev.%s next.%s\n",slen,prevntz.GetHex().c_str(),nextntz.GetHex().c_str());
|
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_NTZSPROOFRESP; |
|||
if ( NSPV_rwntzsproofresp(1,&response[1],&P) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_ntzsproofresp_purge(&P); |
|||
} else fprintf(stderr,"err.%d\n",slen); |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_TXPROOF ) |
|||
{ |
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_txproof P; uint256 txid; int32_t height,vout; |
|||
if ( len == 1+sizeof(txid)+sizeof(height)+sizeof(vout) ) |
|||
{ |
|||
iguana_rwnum(0,&request[1],sizeof(height),&height); |
|||
iguana_rwnum(0,&request[1+sizeof(height)],sizeof(vout),&vout); |
|||
iguana_rwbignum(0,&request[1+sizeof(height)+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); |
|||
//fprintf(stderr,"got txid %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height);
|
|||
memset(&P,0,sizeof(P)); |
|||
if ( (slen= NSPV_gettxproof(&P,vout,txid,height)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_TXPROOFRESP; |
|||
if ( NSPV_rwtxproof(1,&response[1],&P) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_txproof_purge(&P); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_SPENTINFO ) |
|||
{ |
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_spentinfo S; int32_t vout; uint256 txid; |
|||
if ( len == 1+sizeof(txid)+sizeof(vout) ) |
|||
{ |
|||
iguana_rwnum(0,&request[1],sizeof(vout),&vout); |
|||
iguana_rwbignum(0,&request[1+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); |
|||
memset(&S,0,sizeof(S)); |
|||
if ( (slen= NSPV_getspentinfo(&S,txid,vout)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_SPENTINFORESP; |
|||
if ( NSPV_rwspentinfo(1,&response[1],&S) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_spentinfo_purge(&S); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else if ( request[0] == NSPV_BROADCAST ) |
|||
{ |
|||
if ( timestamp > pfrom->prevtimes[ind] ) |
|||
{ |
|||
struct NSPV_broadcastresp B; uint32_t n,offset; uint256 txid; |
|||
if ( len > 1+sizeof(txid)+sizeof(n) ) |
|||
{ |
|||
iguana_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid); |
|||
iguana_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n); |
|||
memset(&B,0,sizeof(B)); |
|||
offset = 1 + sizeof(txid) + sizeof(n); |
|||
if ( n < MAX_TX_SIZE_AFTER_SAPLING && request.size() == offset+n && (slen= NSPV_sendrawtransaction(&B,&request[offset],n)) > 0 ) |
|||
{ |
|||
response.resize(1 + slen); |
|||
response[0] = NSPV_BROADCASTRESP; |
|||
if ( NSPV_rwbroadcastresp(1,&response[1],&B) == slen ) |
|||
{ |
|||
pfrom->PushMessage("nSPV",response); |
|||
pfrom->prevtimes[ind] = timestamp; |
|||
} |
|||
NSPV_broadcast_purge(&B); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#endif // KOMODO_NSPVFULLNODE_H
|
@ -0,0 +1,706 @@ |
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef KOMODO_NSPVSUPERLITE_H |
|||
#define KOMODO_NSPVSUPERLITE_H |
|||
|
|||
// nSPV client. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc.
|
|||
// no caching, no optimizations, no reducing the number of ntzsproofs needed by detecting overlaps, etc.
|
|||
// advantage is that it is simpler to implement and understand to create a design for a more performant version
|
|||
|
|||
|
|||
CAmount AmountFromValue(const UniValue& value); |
|||
int32_t bitcoin_base58decode(uint8_t *data,char *coinaddr); |
|||
|
|||
uint32_t NSPV_lastinfo,NSPV_logintime,NSPV_tiptime; |
|||
CKey NSPV_key; |
|||
char NSPV_wifstr[64],NSPV_pubkeystr[67],NSPV_lastpeer[128]; |
|||
std::string NSPV_address; |
|||
struct NSPV_inforesp NSPV_inforesult; |
|||
struct NSPV_utxosresp NSPV_utxosresult; |
|||
struct NSPV_spentinfo NSPV_spentresult; |
|||
struct NSPV_ntzsresp NSPV_ntzsresult; |
|||
struct NSPV_ntzsproofresp NSPV_ntzsproofresult; |
|||
struct NSPV_txproof NSPV_txproofresult; |
|||
struct NSPV_broadcastresp NSPV_broadcastresult; |
|||
|
|||
struct NSPV_ntzsresp NSPV_ntzsresp_cache[NSPV_MAXVINS]; |
|||
struct NSPV_ntzsproofresp NSPV_ntzsproofresp_cache[NSPV_MAXVINS * 2]; |
|||
struct NSPV_txproof NSPV_txproof_cache[NSPV_MAXVINS * 4]; |
|||
|
|||
struct NSPV_ntzsresp *NSPV_ntzsresp_find(int32_t reqheight) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; i<sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache); i++) |
|||
if ( NSPV_ntzsresp_cache[i].reqheight == reqheight ) |
|||
return(&NSPV_ntzsresp_cache[i]); |
|||
return(0); |
|||
} |
|||
|
|||
struct NSPV_ntzsresp *NSPV_ntzsresp_add(struct NSPV_ntzsresp *ptr) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; i<sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache); i++) |
|||
if ( NSPV_ntzsresp_cache[i].reqheight == 0 ) |
|||
break; |
|||
if ( i == sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache) ) |
|||
i == (rand() % (sizeof(NSPV_ntzsresp_cache)/sizeof(*NSPV_ntzsresp_cache))); |
|||
NSPV_ntzsresp_purge(&NSPV_ntzsresp_cache[i]); |
|||
NSPV_ntzsresp_copy(&NSPV_ntzsresp_cache[i],ptr); |
|||
fprintf(stderr,"ADD CACHE ntzsresp req.%d\n",ptr->reqheight); |
|||
return(&NSPV_ntzsresp_cache[i]); |
|||
} |
|||
|
|||
struct NSPV_txproof *NSPV_txproof_find(uint256 txid) |
|||
{ |
|||
int32_t i; struct NSPV_txproof *backup = 0; |
|||
for (i=0; i<sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache); i++) |
|||
if ( NSPV_txproof_cache[i].txid == txid ) |
|||
{ |
|||
if ( NSPV_txproof_cache[i].txprooflen != 0 ) |
|||
return(&NSPV_txproof_cache[i]); |
|||
else backup = &NSPV_txproof_cache[i]; |
|||
} |
|||
return(backup); |
|||
} |
|||
|
|||
struct NSPV_txproof *NSPV_txproof_add(struct NSPV_txproof *ptr) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; i<sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache); i++) |
|||
if ( NSPV_txproof_cache[i].txid == ptr->txid ) |
|||
{ |
|||
if ( NSPV_txproof_cache[i].txprooflen == 0 && ptr->txprooflen != 0 ) |
|||
{ |
|||
NSPV_txproof_purge(&NSPV_txproof_cache[i]); |
|||
NSPV_txproof_copy(&NSPV_txproof_cache[i],ptr); |
|||
return(&NSPV_txproof_cache[i]); |
|||
} |
|||
else if ( NSPV_txproof_cache[i].txprooflen != 0 || ptr->txprooflen == 0 ) |
|||
return(&NSPV_txproof_cache[i]); |
|||
} |
|||
for (i=0; i<sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache); i++) |
|||
if ( NSPV_txproof_cache[i].txlen == 0 ) |
|||
break; |
|||
if ( i == sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache) ) |
|||
i == (rand() % (sizeof(NSPV_txproof_cache)/sizeof(*NSPV_txproof_cache))); |
|||
NSPV_txproof_purge(&NSPV_txproof_cache[i]); |
|||
NSPV_txproof_copy(&NSPV_txproof_cache[i],ptr); |
|||
fprintf(stderr,"ADD CACHE txproof %s\n",ptr->txid.GetHex().c_str()); |
|||
return(&NSPV_txproof_cache[i]); |
|||
} |
|||
|
|||
struct NSPV_ntzsproofresp *NSPV_ntzsproof_find(uint256 prevtxid,uint256 nexttxid) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; i<sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache); i++) |
|||
if ( NSPV_ntzsproofresp_cache[i].prevtxid == prevtxid && NSPV_ntzsproofresp_cache[i].nexttxid == nexttxid ) |
|||
return(&NSPV_ntzsproofresp_cache[i]); |
|||
return(0); |
|||
} |
|||
|
|||
struct NSPV_ntzsproofresp *NSPV_ntzsproof_add(struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
int32_t i; |
|||
for (i=0; i<sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache); i++) |
|||
if ( NSPV_ntzsproofresp_cache[i].common.hdrs == 0 ) |
|||
break; |
|||
if ( i == sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache) ) |
|||
i == (rand() % (sizeof(NSPV_ntzsproofresp_cache)/sizeof(*NSPV_ntzsproofresp_cache))); |
|||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresp_cache[i]); |
|||
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresp_cache[i],ptr); |
|||
fprintf(stderr,"ADD CACHE ntzsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); |
|||
return(&NSPV_ntzsproofresp_cache[i]); |
|||
} |
|||
|
|||
// komodo_nSPVresp is called from async message processing
|
|||
|
|||
void komodo_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a response
|
|||
{ |
|||
struct NSPV_inforesp I; int32_t len; uint32_t timestamp = (uint32_t)time(NULL); |
|||
strncpy(NSPV_lastpeer,pfrom->addr.ToString().c_str(),sizeof(NSPV_lastpeer)-1); |
|||
if ( (len= response.size()) > 0 ) |
|||
{ |
|||
switch ( response[0] ) |
|||
{ |
|||
case NSPV_INFORESP: |
|||
//fprintf(stderr,"got info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
|||
I = NSPV_inforesult; |
|||
NSPV_inforesp_purge(&NSPV_inforesult); |
|||
NSPV_rwinforesp(0,&response[1],&NSPV_inforesult); |
|||
if ( NSPV_inforesult.height < I.height ) |
|||
{ |
|||
fprintf(stderr,"got old info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status
|
|||
NSPV_inforesp_purge(&NSPV_inforesult); |
|||
NSPV_inforesult = I; |
|||
} |
|||
else if ( NSPV_inforesult.height > I.height ) |
|||
{ |
|||
NSPV_lastinfo = timestamp - ASSETCHAINS_BLOCKTIME/4; |
|||
// need to validate new header to make sure it is valid mainchain
|
|||
if ( NSPV_inforesult.height == NSPV_inforesult.hdrheight ) |
|||
NSPV_tiptime = NSPV_inforesult.H.nTime; |
|||
} |
|||
break; |
|||
case NSPV_UTXOSRESP: |
|||
NSPV_utxosresp_purge(&NSPV_utxosresult); |
|||
NSPV_rwutxosresp(0,&response[1],&NSPV_utxosresult); |
|||
fprintf(stderr,"got utxos response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos list
|
|||
break; |
|||
case NSPV_NTZSRESP: |
|||
NSPV_ntzsresp_purge(&NSPV_ntzsresult); |
|||
NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult); |
|||
if ( NSPV_ntzsresp_find(NSPV_ntzsresult.reqheight) == 0 ) |
|||
NSPV_ntzsresp_add(&NSPV_ntzsresult); |
|||
fprintf(stderr,"got ntzs response %u size.%d %s prev.%d, %s next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.prevntz.height,NSPV_ntzsresult.nextntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.height); |
|||
break; |
|||
case NSPV_NTZSPROOFRESP: |
|||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); |
|||
NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult); |
|||
if ( NSPV_ntzsproof_find(NSPV_ntzsproofresult.prevtxid,NSPV_ntzsproofresult.nexttxid) == 0 ) |
|||
NSPV_ntzsproof_add(&NSPV_ntzsproofresult); |
|||
fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht); |
|||
break; |
|||
case NSPV_TXPROOFRESP: |
|||
NSPV_txproof_purge(&NSPV_txproofresult); |
|||
NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult); |
|||
if ( NSPV_txproof_find(NSPV_txproofresult.txid) == 0 ) |
|||
NSPV_txproof_add(&NSPV_txproofresult); |
|||
fprintf(stderr,"got txproof response %u size.%d %s ht.%d\n",timestamp,(int32_t)response.size(),NSPV_txproofresult.txid.GetHex().c_str(),NSPV_txproofresult.height); |
|||
break; |
|||
case NSPV_SPENTINFORESP: |
|||
NSPV_spentinfo_purge(&NSPV_spentresult); |
|||
NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult); |
|||
fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size()); |
|||
break; |
|||
case NSPV_BROADCASTRESP: |
|||
NSPV_broadcast_purge(&NSPV_broadcastresult); |
|||
NSPV_rwbroadcastresp(0,&response[1],&NSPV_broadcastresult); |
|||
fprintf(stderr,"got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode); |
|||
break; |
|||
default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// superlite message issuing
|
|||
|
|||
CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind) |
|||
{ |
|||
int32_t n,flag = 0; CNode *pnodes[64]; uint32_t timestamp = (uint32_t)time(NULL); |
|||
if ( KOMODO_NSPV == 0 ) |
|||
return(0); |
|||
if ( pnode == 0 ) |
|||
{ |
|||
memset(pnodes,0,sizeof(pnodes)); |
|||
//LOCK(cs_vNodes);
|
|||
n = 0; |
|||
BOOST_FOREACH(CNode *ptr,vNodes) |
|||
{ |
|||
if ( ptr->prevtimes[ind] > timestamp ) |
|||
ptr->prevtimes[ind] = 0; |
|||
if ( ptr->hSocket == INVALID_SOCKET ) |
|||
continue; |
|||
if ( (ptr->nServices & mask) == mask && timestamp > ptr->prevtimes[ind] ) |
|||
{ |
|||
flag = 1; |
|||
pnodes[n++] = ptr; |
|||
if ( n == sizeof(pnodes)/sizeof(*pnodes) ) |
|||
break; |
|||
} // else fprintf(stderr,"nServices %llx vs mask %llx, t%u vs %u, ind.%d\n",(long long)ptr->nServices,(long long)mask,timestamp,ptr->prevtimes[ind],ind);
|
|||
} |
|||
if ( n > 0 ) |
|||
pnode = pnodes[rand() % n]; |
|||
} else flag = 1; |
|||
if ( pnode != 0 ) |
|||
{ |
|||
std::vector<uint8_t> request; |
|||
request.resize(len); |
|||
memcpy(&request[0],msg,len); |
|||
if ( (0) && KOMODO_NSPV != 0 ) |
|||
fprintf(stderr,"pushmessage [%d] len.%d\n",msg[0],len); |
|||
pnode->PushMessage("getnSPV",request); |
|||
pnode->prevtimes[ind] = timestamp; |
|||
return(pnode); |
|||
} else fprintf(stderr,"no pnodes\n"); |
|||
return(0); |
|||
} |
|||
|
|||
UniValue NSPV_logout() |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
if ( NSPV_logintime != 0 ) |
|||
fprintf(stderr,"scrub wif and privkey from NSPV memory\n"); |
|||
else result.push_back(Pair("status","wasnt logged in")); |
|||
memset(NSPV_ntzsproofresp_cache,0,sizeof(NSPV_ntzsproofresp_cache)); |
|||
memset(NSPV_txproof_cache,0,sizeof(NSPV_txproof_cache)); |
|||
memset(NSPV_ntzsresp_cache,0,sizeof(NSPV_ntzsresp_cache)); |
|||
memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); |
|||
memset(&NSPV_key,0,sizeof(NSPV_key)); |
|||
NSPV_logintime = 0; |
|||
return(result); |
|||
} |
|||
|
|||
// komodo_nSPV from main polling loop (really this belongs in its own file, but it is so small, it ended up here)
|
|||
|
|||
void komodo_nSPV(CNode *pto) // polling loop from SendMessages
|
|||
{ |
|||
uint8_t msg[256]; int32_t i,len=0; uint32_t timestamp = (uint32_t)time(NULL); |
|||
if ( NSPV_logintime != 0 && timestamp > NSPV_logintime+NSPV_AUTOLOGOUT ) |
|||
NSPV_logout(); |
|||
if ( (pto->nServices & NODE_NSPV) == 0 ) |
|||
return; |
|||
if ( pto->prevtimes[NSPV_INFO>>1] > timestamp ) |
|||
pto->prevtimes[NSPV_INFO>>1] = 0; |
|||
if ( KOMODO_NSPV != 0 ) |
|||
{ |
|||
if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + 2*ASSETCHAINS_BLOCKTIME/3 ) |
|||
{ |
|||
int32_t reqht; |
|||
reqht = 0; |
|||
len = 0; |
|||
msg[len++] = NSPV_INFO; |
|||
len += iguana_rwnum(1,&msg[len],sizeof(reqht),&reqht); |
|||
//fprintf(stderr,"issue getinfo\n");
|
|||
NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
UniValue NSPV_txproof_json(struct NSPV_txproof *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("txid",ptr->txid.GetHex())); |
|||
result.push_back(Pair("height",(int64_t)ptr->height)); |
|||
result.push_back(Pair("txlen",(int64_t)ptr->txlen)); |
|||
result.push_back(Pair("txprooflen",(int64_t)ptr->txprooflen)); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_spentinfo_json(struct NSPV_spentinfo *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("txid",ptr->txid.GetHex())); |
|||
result.push_back(Pair("vout",(int64_t)ptr->vout)); |
|||
result.push_back(Pair("spentheight",(int64_t)ptr->spent.height)); |
|||
result.push_back(Pair("spenttxid",ptr->spent.txid.GetHex())); |
|||
result.push_back(Pair("spentvini",(int64_t)ptr->spentvini)); |
|||
result.push_back(Pair("spenttxlen",(int64_t)ptr->spent.txlen)); |
|||
result.push_back(Pair("spenttxprooflen",(int64_t)ptr->spent.txprooflen)); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_ntz_json(struct NSPV_ntz *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("notarized_height",(int64_t)ptr->height)); |
|||
result.push_back(Pair("notarized_blockhash",ptr->blockhash.GetHex())); |
|||
result.push_back(Pair("notarization_txid",ptr->txid.GetHex())); |
|||
result.push_back(Pair("notarization_txidheight",(int64_t)ptr->txidheight)); |
|||
result.push_back(Pair("notarization_desttxid",ptr->othertxid.GetHex())); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_header_json(struct NSPV_equihdr *hdr,int32_t height) |
|||
{ |
|||
UniValue item(UniValue::VOBJ); |
|||
item.push_back(Pair("height",(int64_t)height)); |
|||
item.push_back(Pair("blockhash",NSPV_hdrhash(hdr).GetHex())); |
|||
item.push_back(Pair("hashPrevBlock",hdr->hashPrevBlock.GetHex())); |
|||
item.push_back(Pair("hashMerkleRoot",hdr->hashMerkleRoot.GetHex())); |
|||
item.push_back(Pair("nTime",(int64_t)hdr->nTime)); |
|||
item.push_back(Pair("nBits",(int64_t)hdr->nBits)); |
|||
return(item); |
|||
} |
|||
|
|||
UniValue NSPV_headers_json(struct NSPV_equihdr *hdrs,int32_t numhdrs,int32_t height) |
|||
{ |
|||
UniValue array(UniValue::VARR); int32_t i; |
|||
for (i=0; i<numhdrs; i++) |
|||
array.push_back(NSPV_header_json(&hdrs[i],height+i)); |
|||
return(array); |
|||
} |
|||
|
|||
UniValue NSPV_getinfo_json(struct NSPV_inforesp *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); int32_t expiration; uint32_t timestamp = (uint32_t)time(NULL); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("nSPV",KOMODO_NSPV!=0?"superlite":"fullnode")); |
|||
if ( NSPV_address.size() != 0 ) |
|||
{ |
|||
result.push_back(Pair("address",NSPV_address)); |
|||
result.push_back(Pair("pubkey",NSPV_pubkeystr)); |
|||
} |
|||
if ( NSPV_logintime != 0 ) |
|||
{ |
|||
expiration = (NSPV_logintime + NSPV_AUTOLOGOUT - timestamp); |
|||
result.push_back(Pair("wifexpires",expiration)); |
|||
} |
|||
result.push_back(Pair("height",(int64_t)ptr->height)); |
|||
result.push_back(Pair("chaintip",ptr->blockhash.GetHex())); |
|||
result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->notarization))); |
|||
result.push_back(Pair("header",NSPV_header_json(&ptr->H,ptr->hdrheight))); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_utxoresp_json(struct NSPV_utxoresp *utxos,int32_t numutxos) |
|||
{ |
|||
UniValue array(UniValue::VARR); int32_t i; |
|||
for (i=0; i<numutxos; i++) |
|||
{ |
|||
UniValue item(UniValue::VOBJ); |
|||
item.push_back(Pair("height",(int64_t)utxos[i].height)); |
|||
item.push_back(Pair("txid",utxos[i].txid.GetHex())); |
|||
item.push_back(Pair("vout",(int64_t)utxos[i].vout)); |
|||
item.push_back(Pair("value",(double)utxos[i].satoshis/COIN)); |
|||
if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
|||
item.push_back(Pair("interest",(double)utxos[i].extradata/COIN)); |
|||
array.push_back(item); |
|||
} |
|||
return(array); |
|||
} |
|||
|
|||
UniValue NSPV_utxosresp_json(struct NSPV_utxosresp *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("utxos",NSPV_utxoresp_json(ptr->utxos,ptr->numutxos))); |
|||
result.push_back(Pair("address",ptr->coinaddr)); |
|||
result.push_back(Pair("isCC",ptr->CCflag)); |
|||
result.push_back(Pair("height",(int64_t)ptr->nodeheight)); |
|||
result.push_back(Pair("numutxos",(int64_t)ptr->numutxos)); |
|||
result.push_back(Pair("balance",(double)ptr->total/COIN)); |
|||
if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
|||
result.push_back(Pair("interest",(double)ptr->interest/COIN)); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_ntzsresp_json(struct NSPV_ntzsresp *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("prev",NSPV_ntz_json(&ptr->prevntz))); |
|||
result.push_back(Pair("next",NSPV_ntz_json(&ptr->nextntz))); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_ntzsproof_json(struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("prevht",(int64_t)ptr->common.prevht)); |
|||
result.push_back(Pair("nextht",(int64_t)ptr->common.nextht)); |
|||
result.push_back(Pair("prevtxid",ptr->prevtxid.GetHex())); |
|||
result.push_back(Pair("prevtxidht",(int64_t)ptr->prevtxidht)); |
|||
result.push_back(Pair("prevtxlen",(int64_t)ptr->prevtxlen)); |
|||
result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); |
|||
result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); |
|||
result.push_back(Pair("nexttxlen",(int64_t)ptr->prevtxlen)); |
|||
result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs)); |
|||
result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs,ptr->common.prevht))); |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
//fprintf(stderr,"ntzs_proof %s %d, %s %d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht);
|
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_broadcast_json(struct NSPV_broadcastresp *ptr,uint256 txid) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("expected",txid.GetHex())); |
|||
result.push_back(Pair("broadcast",ptr->txid.GetHex())); |
|||
result.push_back(Pair("retcode",(int64_t)ptr->retcode)); |
|||
switch ( ptr->retcode ) |
|||
{ |
|||
case 1: result.push_back(Pair("type","broadcast and mempool")); break; |
|||
case 0: result.push_back(Pair("type","broadcast")); break; |
|||
case -1: result.push_back(Pair("type","decode error")); break; |
|||
case -2: result.push_back(Pair("type","timeout")); break; |
|||
case -3: result.push_back(Pair("type","error adding to mempool")); break; |
|||
default: result.push_back(Pair("type","unknown")); break; |
|||
} |
|||
result.push_back(Pair("lastpeer",NSPV_lastpeer)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_login(char *wifstr) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); char coinaddr[64]; uint8_t data[128]; int32_t len,valid = 0; |
|||
NSPV_logout(); |
|||
len = bitcoin_base58decode(data,wifstr); |
|||
if ( strlen(wifstr) < 64 && (len == 38 && data[len-5] == 1) || (len == 37 && data[len-5] != 1) ) |
|||
valid = 1; |
|||
if ( valid == 0 || data[0] != 188 ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","invalid wif")); |
|||
result.push_back(Pair("len",(int64_t)len)); |
|||
result.push_back(Pair("prefix",(int64_t)data[0])); |
|||
return(result); |
|||
} |
|||
memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); |
|||
NSPV_logintime = (uint32_t)time(NULL); |
|||
if ( strcmp(NSPV_wifstr,wifstr) != 0 ) |
|||
{ |
|||
strncpy(NSPV_wifstr,wifstr,sizeof(NSPV_wifstr)-1); |
|||
NSPV_key = DecodeSecret(wifstr); |
|||
} |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("status","wif will expire in 777 seconds")); |
|||
CPubKey pubkey = NSPV_key.GetPubKey(); |
|||
CKeyID vchAddress = pubkey.GetID(); |
|||
NSPV_address = EncodeDestination(vchAddress); |
|||
result.push_back(Pair("address",NSPV_address)); |
|||
result.push_back(Pair("pubkey",HexStr(pubkey))); |
|||
strcpy(NSPV_pubkeystr,HexStr(pubkey).c_str()); |
|||
if ( KOMODO_NSPV != 0 ) |
|||
decode_hex(NOTARY_PUBKEY33,33,NSPV_pubkeystr); |
|||
result.push_back(Pair("wifprefix",(int64_t)data[0])); |
|||
result.push_back(Pair("compressed",(int64_t)(data[len-5] == 1))); |
|||
memset(data,0,sizeof(data)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_getinfo_req(int32_t reqht) |
|||
{ |
|||
uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_inforesp I; |
|||
NSPV_inforesp_purge(&NSPV_inforesult); |
|||
msg[len++] = NSPV_INFO; |
|||
len += iguana_rwnum(1,&msg[len],sizeof(reqht),&reqht); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_inforesult.height != 0 ) |
|||
return(NSPV_getinfo_json(&NSPV_inforesult)); |
|||
} |
|||
} else sleep(1); |
|||
memset(&I,0,sizeof(I)); |
|||
return(NSPV_getinfo_json(&NSPV_inforesult)); |
|||
} |
|||
|
|||
uint32_t NSPV_blocktime(int32_t hdrheight) |
|||
{ |
|||
uint32_t timestamp; struct NSPV_inforesp old = NSPV_inforesult; |
|||
if ( hdrheight > 0 ) |
|||
{ |
|||
NSPV_getinfo_req(hdrheight); |
|||
if ( NSPV_inforesult.hdrheight == hdrheight ) |
|||
{ |
|||
timestamp = NSPV_inforesult.H.nTime; |
|||
NSPV_inforesult = old; |
|||
fprintf(stderr,"NSPV_blocktime ht.%d -> t%u\n",hdrheight,timestamp); |
|||
return(timestamp); |
|||
} |
|||
} |
|||
NSPV_inforesult = old; |
|||
return(0); |
|||
} |
|||
|
|||
UniValue NSPV_addressutxos(char *coinaddr,int32_t CCflag) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); uint8_t msg[64]; int32_t i,iter,slen,len = 0; |
|||
//fprintf(stderr,"utxos %s NSPV addr %s\n",coinaddr,NSPV_address.c_str());
|
|||
if ( NSPV_utxosresult.nodeheight >= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag ) |
|||
return(NSPV_utxosresp_json(&NSPV_utxosresult)); |
|||
NSPV_utxosresp_purge(&NSPV_utxosresult); |
|||
if ( bitcoin_base58decode(msg,coinaddr) != 25 ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","invalid address")); |
|||
return(result); |
|||
} |
|||
slen = (int32_t)strlen(coinaddr); |
|||
msg[len++] = NSPV_UTXOS; |
|||
msg[len++] = slen; |
|||
memcpy(&msg[len],coinaddr,slen), len += slen; |
|||
msg[len++] = (CCflag != 0); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_ADDRINDEX,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_utxosresult.nodeheight >= NSPV_inforesult.height && strcmp(coinaddr,NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag ) |
|||
return(NSPV_utxosresp_json(&NSPV_utxosresult)); |
|||
} |
|||
} else sleep(1); |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","no utxos result")); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue NSPV_notarizations(int32_t reqheight) |
|||
{ |
|||
uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_ntzsresp N,*ptr; |
|||
if ( (ptr= NSPV_ntzsresp_find(reqheight)) != 0 ) |
|||
{ |
|||
fprintf(stderr,"FROM CACHE NSPV_notarizations.%d\n",reqheight); |
|||
NSPV_ntzsresp_purge(&NSPV_ntzsresult); |
|||
NSPV_ntzsresp_copy(&NSPV_ntzsresult,ptr); |
|||
return(NSPV_ntzsresp_json(ptr)); |
|||
} |
|||
msg[len++] = NSPV_NTZS; |
|||
len += iguana_rwnum(1,&msg[len],sizeof(reqheight),&reqheight); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_ntzsresult.reqheight == reqheight ) |
|||
return(NSPV_ntzsresp_json(&NSPV_ntzsresult)); |
|||
} |
|||
} else sleep(1); |
|||
memset(&N,0,sizeof(N)); |
|||
return(NSPV_ntzsresp_json(&N)); |
|||
} |
|||
|
|||
UniValue NSPV_txidhdrsproof(uint256 prevtxid,uint256 nexttxid) |
|||
{ |
|||
uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_ntzsproofresp P,*ptr; |
|||
if ( (ptr= NSPV_ntzsproof_find(prevtxid,nexttxid)) != 0 ) |
|||
{ |
|||
fprintf(stderr,"FROM CACHE NSPV_txidhdrsproof %s %s\n",ptr->prevtxid.GetHex().c_str(),ptr->nexttxid.GetHex().c_str()); |
|||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); |
|||
NSPV_ntzsproofresp_copy(&NSPV_ntzsproofresult,ptr); |
|||
return(NSPV_ntzsproof_json(ptr)); |
|||
} |
|||
NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); |
|||
msg[len++] = NSPV_NTZSPROOF; |
|||
len += iguana_rwbignum(1,&msg[len],sizeof(prevtxid),(uint8_t *)&prevtxid); |
|||
len += iguana_rwbignum(1,&msg[len],sizeof(nexttxid),(uint8_t *)&nexttxid); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_ntzsproofresult.prevtxid == prevtxid && NSPV_ntzsproofresult.nexttxid == nexttxid ) |
|||
return(NSPV_ntzsproof_json(&NSPV_ntzsproofresult)); |
|||
} |
|||
} else sleep(1); |
|||
memset(&P,0,sizeof(P)); |
|||
return(NSPV_ntzsproof_json(&P)); |
|||
} |
|||
|
|||
UniValue NSPV_hdrsproof(int32_t prevht,int32_t nextht) |
|||
{ |
|||
uint256 prevtxid,nexttxid; |
|||
NSPV_notarizations(prevht); |
|||
prevtxid = NSPV_ntzsresult.prevntz.txid; |
|||
NSPV_notarizations(nextht); |
|||
nexttxid = NSPV_ntzsresult.nextntz.txid; |
|||
return(NSPV_txidhdrsproof(prevtxid,nexttxid)); |
|||
} |
|||
|
|||
UniValue NSPV_txproof(int32_t vout,uint256 txid,int32_t height) |
|||
{ |
|||
uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_txproof P,*ptr; |
|||
if ( (ptr= NSPV_txproof_find(txid)) != 0 ) |
|||
{ |
|||
fprintf(stderr,"FROM CACHE NSPV_txproof %s\n",txid.GetHex().c_str()); |
|||
NSPV_txproof_purge(&NSPV_txproofresult); |
|||
NSPV_txproof_copy(&NSPV_txproofresult,ptr); |
|||
return(NSPV_txproof_json(ptr)); |
|||
} |
|||
NSPV_txproof_purge(&NSPV_txproofresult); |
|||
msg[len++] = NSPV_TXPROOF; |
|||
len += iguana_rwnum(1,&msg[len],sizeof(height),&height); |
|||
len += iguana_rwnum(1,&msg[len],sizeof(vout),&vout); |
|||
len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); |
|||
fprintf(stderr,"req txproof %s/v%d at height.%d\n",txid.GetHex().c_str(),vout,height); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_txproofresult.txid == txid ) |
|||
return(NSPV_txproof_json(&NSPV_txproofresult)); |
|||
} |
|||
} else sleep(1); |
|||
fprintf(stderr,"txproof timeout\n"); |
|||
memset(&P,0,sizeof(P)); |
|||
return(NSPV_txproof_json(&P)); |
|||
} |
|||
|
|||
UniValue NSPV_spentinfo(uint256 txid,int32_t vout) |
|||
{ |
|||
uint8_t msg[64]; int32_t i,iter,len = 0; struct NSPV_spentinfo I; |
|||
NSPV_spentinfo_purge(&NSPV_spentresult); |
|||
msg[len++] = NSPV_SPENTINFO; |
|||
len += iguana_rwnum(1,&msg[len],sizeof(vout),&vout); |
|||
len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); |
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_SPENTINDEX,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_spentresult.txid == txid && NSPV_spentresult.vout == vout ) |
|||
return(NSPV_spentinfo_json(&NSPV_spentresult)); |
|||
} |
|||
} else sleep(1); |
|||
memset(&I,0,sizeof(I)); |
|||
return(NSPV_spentinfo_json(&I)); |
|||
} |
|||
|
|||
UniValue NSPV_broadcast(char *hex) |
|||
{ |
|||
uint8_t *msg,*data; uint256 txid; int32_t i,n,iter,len = 0; struct NSPV_broadcastresp B; |
|||
NSPV_broadcast_purge(&NSPV_broadcastresult); |
|||
n = (int32_t)strlen(hex) >> 1; |
|||
data = (uint8_t *)malloc(n); |
|||
decode_hex(data,n,hex); |
|||
txid = NSPV_doublesha256(data,n); |
|||
msg = (uint8_t *)malloc(1 + sizeof(txid) + sizeof(n) + n); |
|||
msg[len++] = NSPV_BROADCAST; |
|||
len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); |
|||
len += iguana_rwnum(1,&msg[len],sizeof(n),&n); |
|||
memcpy(&msg[len],data,n), len += n; |
|||
free(data); |
|||
//fprintf(stderr,"send txid.%s\n",txid.GetHex().c_str());
|
|||
for (iter=0; iter<3; iter++); |
|||
if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) |
|||
{ |
|||
for (i=0; i<NSPV_POLLITERS; i++) |
|||
{ |
|||
usleep(NSPV_POLLMICROS); |
|||
if ( NSPV_broadcastresult.txid == txid ) |
|||
{ |
|||
free(msg); |
|||
return(NSPV_broadcast_json(&NSPV_broadcastresult,txid)); |
|||
} |
|||
} |
|||
} else sleep(1); |
|||
free(msg); |
|||
memset(&B,0,sizeof(B)); |
|||
B.retcode = -2; |
|||
return(NSPV_broadcast_json(&B,txid)); |
|||
} |
|||
|
|||
#endif // KOMODO_NSPVSUPERLITE_H
|
@ -0,0 +1,477 @@ |
|||
|
|||
/******************************************************************************
|
|||
* Copyright © 2014-2019 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#ifndef KOMODO_NSPVWALLET_H |
|||
#define KOMODO_NSPVWALLET_H |
|||
|
|||
// nSPV wallet uses superlite functions (and some komodod built in functions) to implement nSPV_spend
|
|||
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); |
|||
|
|||
int32_t NSPV_validatehdrs(struct NSPV_ntzsproofresp *ptr) |
|||
{ |
|||
int32_t i,height,txidht; CTransaction tx; uint256 blockhash,txid,desttxid; |
|||
if ( (ptr->common.nextht-ptr->common.prevht+1) != ptr->common.numhdrs ) |
|||
{ |
|||
fprintf(stderr,"next.%d prev.%d -> %d vs %d\n",ptr->common.nextht,ptr->common.prevht,ptr->common.nextht-ptr->common.prevht+1,ptr->common.numhdrs); |
|||
return(-2); |
|||
} |
|||
else if ( NSPV_txextract(tx,ptr->nextntz,ptr->nexttxlen) < 0 ) |
|||
return(-3); |
|||
else if ( tx.GetHash() != ptr->nexttxid ) |
|||
return(-4); |
|||
else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) |
|||
return(-5); |
|||
else if ( height != ptr->common.nextht ) |
|||
return(-6); |
|||
else if ( NSPV_hdrhash(&ptr->common.hdrs[ptr->common.numhdrs-1]) != blockhash ) |
|||
return(-7); |
|||
for (i=ptr->common.numhdrs-1; i>0; i--) |
|||
{ |
|||
blockhash = NSPV_hdrhash(&ptr->common.hdrs[i-1]); |
|||
if ( blockhash != ptr->common.hdrs[i].hashPrevBlock ) |
|||
return(-i-13); |
|||
} |
|||
sleep(1); // need this to get past the once per second rate limiter per message
|
|||
if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) |
|||
return(-8); |
|||
else if ( tx.GetHash() != ptr->prevtxid ) |
|||
return(-9); |
|||
else if ( NSPV_notarizationextract(1,&height,&blockhash,&desttxid,tx) < 0 ) |
|||
return(-10); |
|||
else if ( height != ptr->common.prevht ) |
|||
return(-11); |
|||
else if ( NSPV_hdrhash(&ptr->common.hdrs[0]) != blockhash ) |
|||
return(-12); |
|||
return(0); |
|||
} |
|||
|
|||
int32_t NSPV_gettransaction(int32_t skipvalidation,int32_t vout,uint256 txid,int32_t height,CTransaction &tx,int64_t extradata,uint32_t tiptime,int64_t &rewardsum) |
|||
{ |
|||
struct NSPV_txproof *ptr; int32_t i,offset,retval = 0; int64_t rewards = 0; uint32_t nLockTime; std::vector<uint8_t> proof; |
|||
//fprintf(stderr,"NSPV_gettx %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height);
|
|||
if ( (ptr= NSPV_txproof_find(txid)) == 0 ) |
|||
{ |
|||
NSPV_txproof(vout,txid,height); |
|||
ptr = &NSPV_txproofresult; |
|||
} |
|||
if ( ptr->txid != txid ) |
|||
{ |
|||
fprintf(stderr,"txproof error %s != %s\n",ptr->txid.GetHex().c_str(),txid.GetHex().c_str()); |
|||
return(-1); |
|||
} |
|||
else if ( NSPV_txextract(tx,ptr->tx,ptr->txlen) < 0 || ptr->txlen <= 0 ) |
|||
retval = -2000; |
|||
else if ( tx.GetHash() != txid ) |
|||
retval = -2001; |
|||
else if ( skipvalidation == 0 && ptr->unspentvalue <= 0 ) |
|||
retval = -2002; |
|||
else if ( ASSETCHAINS_SYMBOL[0] == 0 && extradata >= 0 && tiptime != 0 ) |
|||
{ |
|||
rewards = komodo_interestnew(height,tx.vout[vout].nValue,tx.nLockTime,tiptime); |
|||
if ( rewards != extradata ) |
|||
fprintf(stderr,"extradata %.8f vs rewards %.8f\n",dstr(extradata),dstr(rewards)); |
|||
rewardsum += rewards; |
|||
} |
|||
//char coinaddr[64];
|
|||
//Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); causes crash??
|
|||
//fprintf(stderr,"%s txid.%s vs hash.%s\n",coinaddr,txid.GetHex().c_str(),tx.GetHash().GetHex().c_str());
|
|||
|
|||
if ( skipvalidation == 0 ) |
|||
{ |
|||
if ( ptr->txprooflen > 0 ) |
|||
{ |
|||
proof.resize(ptr->txprooflen); |
|||
memcpy(&proof[0],ptr->txproof,ptr->txprooflen); |
|||
} |
|||
NSPV_notarizations(height); // gets the prev and next notarizations
|
|||
if ( NSPV_inforesult.notarization.height >= height && (NSPV_ntzsresult.prevntz.height == 0 || NSPV_ntzsresult.prevntz.height >= NSPV_ntzsresult.nextntz.height) ) |
|||
{ |
|||
fprintf(stderr,"issue manual bracket\n"); |
|||
NSPV_notarizations(height-1); |
|||
NSPV_notarizations(height+1); |
|||
NSPV_notarizations(height); // gets the prev and next notarizations
|
|||
} |
|||
if ( NSPV_ntzsresult.prevntz.height != 0 && NSPV_ntzsresult.prevntz.height <= NSPV_ntzsresult.nextntz.height ) |
|||
{ |
|||
fprintf(stderr,">>>>> gettx ht.%d prev.%d next.%d\n",height,NSPV_ntzsresult.prevntz.height, NSPV_ntzsresult.nextntz.height); |
|||
offset = (height - NSPV_ntzsresult.prevntz.height); |
|||
if ( offset >= 0 && height <= NSPV_ntzsresult.nextntz.height ) |
|||
{ |
|||
//fprintf(stderr,"call NSPV_txidhdrsproof %s %s\n",NSPV_ntzsresult.prevntz.txid.GetHex().c_str(),NSPV_ntzsresult.nextntz.txid.GetHex().c_str());
|
|||
NSPV_txidhdrsproof(NSPV_ntzsresult.prevntz.txid,NSPV_ntzsresult.nextntz.txid); |
|||
usleep(10000); |
|||
if ( (retval= NSPV_validatehdrs(&NSPV_ntzsproofresult)) == 0 ) |
|||
{ |
|||
std::vector<uint256> txids; uint256 proofroot; |
|||
proofroot = BitcoinGetProofMerkleRoot(proof,txids); |
|||
if ( proofroot != NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot ) |
|||
{ |
|||
fprintf(stderr,"prooflen.%d proofroot.%s vs %s\n",(int32_t)proof.size(),proofroot.GetHex().c_str(),NSPV_ntzsproofresult.common.hdrs[offset].hashMerkleRoot.GetHex().c_str()); |
|||
retval = -2003; |
|||
} |
|||
} |
|||
} else retval = -2005; |
|||
} else retval = -2004; |
|||
} |
|||
return(retval); |
|||
} |
|||
|
|||
int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct NSPV_utxoresp utxos[],int32_t numunspents,int64_t value) |
|||
{ |
|||
int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; |
|||
abovei = belowi = -1; |
|||
for (above=below=i=0; i<numunspents; i++) |
|||
{ |
|||
if ( (atx_value= utxos[i].satoshis) <= 0 ) |
|||
continue; |
|||
if ( atx_value == value ) |
|||
{ |
|||
*aboveip = *belowip = i; |
|||
*abovep = *belowp = 0; |
|||
return(i); |
|||
} |
|||
else if ( atx_value > value ) |
|||
{ |
|||
gap = (atx_value - value); |
|||
if ( above == 0 || gap < above ) |
|||
{ |
|||
above = gap; |
|||
abovei = i; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
gap = (value - atx_value); |
|||
if ( below == 0 || gap < below ) |
|||
{ |
|||
below = gap; |
|||
belowi = i; |
|||
} |
|||
} |
|||
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
|
|||
} |
|||
*aboveip = abovei; |
|||
*abovep = above; |
|||
*belowip = belowi; |
|||
*belowp = below; |
|||
//printf("above.%d below.%d\n",abovei,belowi);
|
|||
if ( abovei >= 0 && belowi >= 0 ) |
|||
{ |
|||
if ( above < (below >> 1) ) |
|||
return(abovei); |
|||
else return(belowi); |
|||
} |
|||
else if ( abovei >= 0 ) |
|||
return(abovei); |
|||
else return(belowi); |
|||
} |
|||
|
|||
int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num) |
|||
{ |
|||
int32_t abovei,belowi,ind,vout,i,n = 0; int64_t threshold,above,below; int64_t remains,totalinputs = 0; CTransaction tx; struct NSPV_utxoresp utxos[NSPV_MAXVINS],*up; |
|||
memset(utxos,0,sizeof(utxos)); |
|||
if ( maxinputs > NSPV_MAXVINS ) |
|||
maxinputs = NSPV_MAXVINS; |
|||
if ( maxinputs > 0 ) |
|||
threshold = total/maxinputs; |
|||
else threshold = total; |
|||
for (i=0; i<num; i++) |
|||
{ |
|||
if ( num < NSPV_MAXVINS || ptr[i].satoshis > threshold ) |
|||
utxos[n++] = ptr[i]; |
|||
} |
|||
remains = total; |
|||
//fprintf(stderr,"threshold %.8f n.%d for total %.8f\n",(double)threshold/COIN,n,(double)total/COIN);
|
|||
for (i=0; i<maxinputs && n>0; i++) |
|||
{ |
|||
below = above = 0; |
|||
abovei = belowi = -1; |
|||
if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) |
|||
{ |
|||
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); |
|||
return(0); |
|||
} |
|||
if ( belowi < 0 || abovei >= 0 ) |
|||
ind = abovei; |
|||
else ind = belowi; |
|||
if ( ind < 0 ) |
|||
{ |
|||
fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); |
|||
return(0); |
|||
} |
|||
//fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n);
|
|||
up = &utxos[ind]; |
|||
mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); |
|||
used[i] = *up; |
|||
totalinputs += up->satoshis; |
|||
remains -= up->satoshis; |
|||
utxos[ind] = utxos[--n]; |
|||
memset(&utxos[n],0,sizeof(utxos[n])); |
|||
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
|||
if ( totalinputs >= total || (i+1) >= maxinputs ) |
|||
break; |
|||
} |
|||
//fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN);
|
|||
if ( totalinputs >= total ) |
|||
return(totalinputs); |
|||
return(0); |
|||
} |
|||
|
|||
bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime) |
|||
{ |
|||
CTransaction txNewConst(mtx); SignatureData sigdata; CBasicKeyStore keystore; int64_t branchid = NSPV_BRANCHID; |
|||
keystore.AddKey(NSPV_key); |
|||
if ( nTime != 0 && nTime < KOMODO_SAPLING_ACTIVATION ) |
|||
{ |
|||
fprintf(stderr,"use legacy sig validation\n"); |
|||
branchid = 0; |
|||
} |
|||
if ( ProduceSignature(TransactionSignatureCreator(&keystore,&txNewConst,vini,utxovalue,SIGHASH_ALL),scriptPubKey,sigdata,branchid) != 0 ) |
|||
{ |
|||
UpdateTransaction(mtx,vini,sigdata); |
|||
//fprintf(stderr,"SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
|
|||
return(true); |
|||
} //else fprintf(stderr,"sigerr SIG_TXHASH %s vini.%d %.8f\n",SIG_TXHASH.GetHex().c_str(),vini,(double)utxovalue/COIN);
|
|||
return(false); |
|||
} |
|||
|
|||
std::string NSPV_signtx(int64_t &rewardsum,int64_t &interestsum,UniValue &retcodes,CMutableTransaction &mtx,uint64_t txfee,CScript opret,struct NSPV_utxoresp used[]) |
|||
{ |
|||
CTransaction vintx; std::string hex; uint256 hashBlock; int64_t interest=0,change,totaloutputs=0,totalinputs=0; int32_t i,utxovout,n,validation; |
|||
n = mtx.vout.size(); |
|||
for (i=0; i<n; i++) |
|||
totaloutputs += mtx.vout[i].nValue; |
|||
n = mtx.vin.size(); |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
totalinputs += used[i].satoshis; |
|||
interest += used[i].extradata; |
|||
} |
|||
interestsum = interest; |
|||
if ( (totalinputs+interest) >= totaloutputs+2*txfee ) |
|||
{ |
|||
change = (totalinputs+interest) - (totaloutputs+txfee); |
|||
mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(NSPV_pubkeystr) << OP_CHECKSIG)); |
|||
} |
|||
if ( opret.size() > 0 ) |
|||
mtx.vout.push_back(CTxOut(0,opret)); |
|||
for (i=0; i<n; i++) |
|||
{ |
|||
utxovout = mtx.vin[i].prevout.n; |
|||
if ( i > 0 ) |
|||
sleep(1); |
|||
validation = NSPV_gettransaction(0,utxovout,mtx.vin[i].prevout.hash,used[i].height,vintx,used[i].extradata,NSPV_tiptime,rewardsum); |
|||
retcodes.push_back(validation); |
|||
if ( validation != -1 ) // most others are degraded security
|
|||
{ |
|||
if ( vintx.vout[utxovout].nValue != used[i].satoshis ) |
|||
{ |
|||
fprintf(stderr,"vintx mismatch %.8f != %.8f\n",(double)vintx.vout[utxovout].nValue/COIN,(double)used[i].satoshis/COIN); |
|||
return(""); |
|||
} |
|||
else if ( utxovout != used[i].vout ) |
|||
{ |
|||
fprintf(stderr,"vintx vout mismatch %d != %d\n",utxovout,used[i].vout); |
|||
return(""); |
|||
} |
|||
else if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey,0) == 0 ) |
|||
{ |
|||
fprintf(stderr,"signing error for vini.%d\n",i); |
|||
return(""); |
|||
} |
|||
} else fprintf(stderr,"couldnt find txid.%s/v%d or it was spent\n",mtx.vin[i].prevout.hash.GetHex().c_str(),utxovout); // of course much better handling is needed
|
|||
} |
|||
fprintf(stderr,"sign %d inputs %.8f + interest %.8f -> %d outputs %.8f change %.8f\n",(int32_t)mtx.vin.size(),(double)totalinputs/COIN,(double)interest/COIN,(int32_t)mtx.vout.size(),(double)totaloutputs/COIN,(double)change/COIN); |
|||
return(EncodeHexTx(mtx)); |
|||
} |
|||
|
|||
UniValue NSPV_spend(char *srcaddr,char *destaddr,int64_t satoshis) // what its all about!
|
|||
{ |
|||
UniValue result(UniValue::VOBJ),retcodes(UniValue::VARR); std::vector<uint8_t> data; CScript scriptPubKey; uint8_t *ptr,rmd160[128]; int32_t len; int64_t txfee = 10000; |
|||
if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","wif expired")); |
|||
return(result); |
|||
} |
|||
if ( strcmp(srcaddr,NSPV_address.c_str()) != 0 ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","invalid address")); |
|||
result.push_back(Pair("mismatched",srcaddr)); |
|||
return(result); |
|||
} |
|||
else if ( bitcoin_base58decode(rmd160,destaddr) != 25 ) |
|||
{ |
|||
if ( (len= is_hexstr(destaddr,0)) > 0 ) |
|||
{ |
|||
len >>= 1; |
|||
data.resize(len); |
|||
decode_hex(&data[0],len,destaddr); |
|||
scriptPubKey = CScript() << data << OP_CHECKCRYPTOCONDITION; |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","invalid destaddr")); |
|||
return(result); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
data.resize(20); |
|||
memcpy(&data[0],&rmd160[1],20); |
|||
scriptPubKey = (CScript() << OP_DUP << OP_HASH160 << ParseHex(HexStr(data)) << OP_EQUALVERIFY << OP_CHECKSIG); |
|||
} |
|||
if ( NSPV_inforesult.height == 0 ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","couldnt getinfo")); |
|||
return(result); |
|||
} |
|||
if ( NSPV_utxosresult.CCflag != 0 || strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) |
|||
NSPV_addressutxos(srcaddr,0); |
|||
if ( NSPV_utxosresult.CCflag != 0 || strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("address",NSPV_utxosresult.coinaddr)); |
|||
result.push_back(Pair("srcaddr",srcaddr)); |
|||
result.push_back(Pair("nodeheight",(int64_t)NSPV_utxosresult.nodeheight)); |
|||
result.push_back(Pair("infoheight",(int64_t)NSPV_inforesult.height)); |
|||
result.push_back(Pair("error","couldnt get addressutxos")); |
|||
return(result); |
|||
} |
|||
if ( NSPV_utxosresult.total < satoshis+txfee ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","not enough funds")); |
|||
result.push_back(Pair("balance",(double)NSPV_utxosresult.total/COIN)); |
|||
result.push_back(Pair("amount",(double)satoshis/COIN)); |
|||
return(result); |
|||
} |
|||
printf("%s numutxos.%d balance %.8f\n",NSPV_utxosresult.coinaddr,NSPV_utxosresult.numutxos,(double)NSPV_utxosresult.total/COIN); |
|||
CScript opret; std::string hex; struct NSPV_utxoresp used[NSPV_MAXVINS]; CMutableTransaction mtx; CTransaction tx; int64_t rewardsum=0,interestsum=0; |
|||
mtx.fOverwintered = true; |
|||
mtx.nExpiryHeight = 0; |
|||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; |
|||
mtx.nVersion = SAPLING_TX_VERSION; |
|||
if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
|||
mtx.nLockTime = (uint32_t)time(NULL) - 777; |
|||
memset(used,0,sizeof(used)); |
|||
|
|||
if ( NSPV_addinputs(used,mtx,satoshis+txfee,64,NSPV_utxosresult.utxos,NSPV_utxosresult.numutxos) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(CTxOut(satoshis,scriptPubKey)); |
|||
if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","wif expired")); |
|||
return(result); |
|||
} |
|||
hex = NSPV_signtx(rewardsum,interestsum,retcodes,mtx,txfee,opret,used); |
|||
if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
|||
{ |
|||
char numstr[64]; |
|||
sprintf(numstr,"%.8f",(double)interestsum/COIN); |
|||
result.push_back(Pair("rewards",numstr)); |
|||
sprintf(numstr,"%.8f",(double)rewardsum/COIN); |
|||
result.push_back(Pair("validated",numstr)); |
|||
} |
|||
if ( hex.size() > 0 ) |
|||
{ |
|||
if ( DecodeHexTx(tx,hex) != 0 ) |
|||
{ |
|||
TxToJSON(tx,uint256(),result); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("hex",hex)); |
|||
result.push_back(Pair("retcodes",retcodes)); |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","couldnt decode")); |
|||
result.push_back(Pair("hex",hex)); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("retcodes",retcodes)); |
|||
result.push_back(Pair("error","signing error")); |
|||
} |
|||
return(result); |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","couldnt create tx")); |
|||
return(result); |
|||
} |
|||
} |
|||
|
|||
int64_t NSPV_AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs,struct NSPV_CCmtxinfo *ptr) |
|||
{ |
|||
char coinaddr[64]; int32_t CCflag = 0; |
|||
if ( ptr != 0 ) |
|||
{ |
|||
mtx.fOverwintered = true; |
|||
mtx.nExpiryHeight = 0; |
|||
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; |
|||
mtx.nVersion = SAPLING_TX_VERSION; |
|||
Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); |
|||
if ( strcmp(ptr->U.coinaddr,coinaddr) != 0 ) |
|||
{ |
|||
NSPV_addressutxos(coinaddr,CCflag); |
|||
NSPV_utxosresp_purge(&ptr->U); |
|||
NSPV_utxosresp_copy(&ptr->U,&NSPV_utxosresult); |
|||
} |
|||
fprintf(stderr,"%s numutxos.%d\n",ptr->U.coinaddr,ptr->U.numutxos); |
|||
memset(ptr->used,0,sizeof(ptr->used)); |
|||
return(NSPV_addinputs(ptr->used,mtx,total,maxinputs,ptr->U.utxos,ptr->U.numutxos)); |
|||
} else return(0); |
|||
} |
|||
|
|||
void NSPV_utxos2CCunspents(struct NSPV_utxosresp *ptr,std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &outputs) |
|||
{ |
|||
CAddressUnspentKey key; CAddressUnspentValue value; int32_t i,type; uint160 hashBytes; std::string addrstr(ptr->coinaddr); |
|||
if ( ptr->utxos != NULL && ptr->numutxos > 0 ) |
|||
{ |
|||
CBitcoinAddress address(addrstr); |
|||
if ( address.GetIndexKey(hashBytes, type, ptr->CCflag) == 0 ) |
|||
{ |
|||
fprintf(stderr,"couldnt get indexkey\n"); |
|||
return; |
|||
} |
|||
for (i = 0; i < ptr->numutxos; i ++) |
|||
{ |
|||
key.type = type; |
|||
key.hashBytes = hashBytes; |
|||
key.txhash = ptr->utxos[i].txid; |
|||
key.index = ptr->utxos[i].vout; |
|||
value.satoshis = ptr->utxos[i].satoshis; |
|||
value.blockHeight = ptr->utxos[i].height; |
|||
outputs.push_back(std::make_pair(key, value)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void NSPV_CCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &outputs,char *coinaddr,bool ccflag) |
|||
{ |
|||
NSPV_addressutxos(coinaddr,ccflag); |
|||
NSPV_utxos2CCunspents(&NSPV_utxosresult,outputs); |
|||
} |
|||
|
|||
#endif // KOMODO_NSPVWALLET_H
|
File diff suppressed because it is too large
Loading…
Reference in new issue