/****************************************************************************** * Copyright © 2014-2016 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. * * * ******************************************************************************/ #include "iguana777.h" #define iguana_blockfind(coin,hash2) iguana_blockhashset(coin,-1,hash2,0) void _iguana_blocklink(struct iguana_info *coin,struct iguana_block *prev,struct iguana_block *block) { char str[65],str2[65]; struct iguana_block *next; if ( memcmp(block->RO.prev_block.bytes,prev->RO.hash2.bytes,sizeof(bits256)) != 0 ) { printf("illegal blocklink mismatched hashes\n"); exit(-1); return; } block->hh.prev = prev; if ( (next= prev->hh.next) != 0 ) { if ( next != block ) { if ( memcmp(next->RO.prev_block.bytes,prev->RO.hash2.bytes,sizeof(bits256)) != 0 ) { printf("illegal blocklink next mismatched hashes\n"); return; } if ( memcmp(next->RO.hash2.bytes,block->RO.hash2.bytes,sizeof(bits256)) != 0 ) printf("blocklink collision: %s vs %s\n",bits256_str(str,block->RO.hash2),bits256_str(str2,next->RO.hash2)); else printf("blocklink corruption: identical hashes with diff ptrs %s %p %p\n",bits256_str(str,block->RO.hash2),block,next); } prev->hh.next = block; // could make a linked list of all at same height for multibranch } else prev->hh.next = block; printf("link.(%s) -> (%s)\n",bits256_str(str,prev->RO.hash2),bits256_str(str,block->RO.hash2)); } struct iguana_block *iguana_blockhashset(struct iguana_info *coin,int32_t height,bits256 hash2,int32_t createflag) { static int depth; struct iguana_block *block,*prev; if ( height > 0 && (height > coin->blocks.maxbits || depth != 0) ) { printf("illegal height.%d when max.%d, or nonz depth.%d\n",height,coin->blocks.maxbits,depth); //getchar(); return(0); } depth++; //portable_mutex_lock(&coin->blocks_mutex); HASH_FIND(hh,coin->blocks.hash,&hash2,sizeof(hash2),block); if ( block != 0 ) { // portable_mutex_unlock(&coin->blocks_mutex); depth--; return(block); } if ( createflag > 0 ) { block = mycalloc('x',1,sizeof(*block)); block->RO.hash2 = hash2; block->hh.itemind = height, block->height = -1; HASH_ADD(hh,coin->blocks.hash,RO.hash2,sizeof(hash2),block); block->hh.next = block->hh.prev = 0; if ( bits256_nonz(block->RO.prev_block) > 0 ) { HASH_FIND(hh,coin->blocks.hash,&block->RO.prev_block,sizeof(block->RO.prev_block),prev); if ( prev != 0 ) _iguana_blocklink(coin,prev,block); } //char str[65]; printf("added.(%s) height.%d (%p %p)\n",bits256_str(str,hash2),height,block->hh.prev,block->hh.next); if ( 0 ) { struct iguana_block *tmp; HASH_FIND(hh,coin->blocks.hash,&hash2,sizeof(hash2),tmp); char str[65]; bits256_str(str,hash2); if ( tmp != block ) printf("%s height.%d search error %p != %p\n",str,height,block,tmp); } } //portable_mutex_unlock(&coin->blocks_mutex); depth--; return(block); } bits256 *iguana_blockhashptr(struct iguana_info *coin,int32_t height) { int32_t i; struct iguana_bundle *bp; bits256 *hashptr; if ( height >= 0 ) { for (i=0; ibundlescount; i++) { if ( (bp= coin->bundles[i]) != 0 ) { if ( height >= bp->bundleheight && height < bp->bundleheight+bp->n ) { hashptr = &bp->hashes[height - bp->bundleheight]; //printf("i.%d hashptr.%p height.%d vs (%d %d)\n",i,hashptr,height,bp->bundleheight,bp->bundleheight+bp->n); return(hashptr); } } } } return(0); } bits256 iguana_blockhash(struct iguana_info *coin,int32_t height) { bits256 *hash2p; static const bits256 zero; if ( (hash2p= iguana_blockhashptr(coin,height)) != 0 ) return(*hash2p); return(zero); } struct iguana_block *iguana_blockptr(struct iguana_info *coin,int32_t height) { static const bits256 zero; bits256 hash2 = iguana_blockhash(coin,height); if ( memcmp(zero.bytes,hash2.bytes,sizeof(zero)) != 0 ) return(iguana_blockfind(coin,hash2)); return(0); } int32_t iguana_blockvalidate(struct iguana_info *coin,int32_t *validp,struct iguana_block *block,int32_t dispflag) { bits256 hash2; uint8_t serialized[sizeof(struct iguana_msgblock) + 4096]; *validp = 0; iguana_serialize_block(&hash2,serialized,block); *validp = (memcmp(hash2.bytes,block->RO.hash2.bytes,sizeof(hash2)) == 0); block->valid = *validp; char str[65]; char str2[65]; if ( *validp == 0 ) { if ( dispflag != 0 ) { printf("iguana_blockvalidate: miscompare (%s) vs (%s)\n",bits256_str(str,hash2),bits256_str(str2,block->RO.hash2)); //getchar(); } return(-1); } return(0); } /*void iguana_mergeprevdep(struct iguana_prevdep *destlp,struct iguana_prevdep *srclp) { if ( srclp->numpkinds > destlp->numpkinds ) destlp->numpkinds = srclp->numpkinds; if ( srclp->numtxids > destlp->numtxids ) destlp->numtxids = srclp->numtxids; if ( srclp->numunspents > destlp->numunspents ) destlp->numunspents = srclp->numunspents; if ( srclp->numspends > destlp->numspends ) destlp->numspends = srclp->numspends; if ( srclp->PoW > destlp->PoW ) destlp->PoW = srclp->PoW; } void iguana_blockmerge(struct iguana_block *dest,struct iguana_prevdep *destlp,struct iguana_block *block,struct iguana_prevdep *srclp) { if ( block->numvins > dest->numvins ) dest->numvins = block->numvins; if ( block->numvouts > dest->numvouts ) dest->numvouts = block->numvouts; if ( block->txn_count > dest->txn_count ) dest->txn_count = block->txn_count; if ( dest->height == 0 ) dest->height = block->height; iguana_mergeprevdep(destlp,srclp); }*/ void iguana_blockconv(struct iguana_block *dest,struct iguana_msgblock *msg,bits256 hash2,int32_t height) //uint32_t numtxids,uint32_t numunspents,uint32_t numspends,double PoW) { memset(dest,0,sizeof(*dest)); dest->RO.version = msg->H.version; dest->RO.prev_block = msg->H.prev_block; dest->RO.merkle_root = msg->H.merkle_root; dest->RO.timestamp = msg->H.timestamp; dest->RO.bits = msg->H.bits; dest->RO.nonce = msg->H.nonce; dest->RO.txn_count = msg->txn_count; dest->height = height; dest->RO.hash2 = hash2; } void iguana_blockcopy(struct iguana_info *coin,struct iguana_block *block,struct iguana_block *origblock) { block->RO.hash2 = origblock->RO.hash2; block->RO.merkle_root = origblock->RO.merkle_root; if ( bits256_nonz(block->RO.prev_block) == 0 ) block->RO.prev_block = origblock->RO.prev_block; if ( block->mainchain == 0 ) block->mainchain = origblock->mainchain; if ( block->fpos < 0 ) block->fpos = origblock->fpos; if ( block->fpipbits == 0 ) block->fpipbits = origblock->fpipbits; if ( block->RO.timestamp == 0 ) block->RO.timestamp = origblock->RO.timestamp; if ( block->RO.nonce == 0 ) block->RO.nonce = origblock->RO.nonce; if ( block->RO.bits == 0 ) block->RO.bits = origblock->RO.bits; if ( block->RO.txn_count == 0 ) block->RO.txn_count = origblock->RO.txn_count; if ( block->RO.version == 0 ) block->RO.version = origblock->RO.version; if ( block->mainchain == 0 ) block->mainchain = origblock->mainchain; if ( block->valid == 0 ) block->valid = origblock->valid; if ( block->RO.recvlen == 0 ) block->RO.recvlen = origblock->RO.recvlen; } double PoW_from_compact(uint32_t nBits,uint8_t unitval) // NOT consensus safe, but most of the time will be correct { uint32_t nbytes,nbits,i,n; double PoW; uint64_t mult; nbytes = (nBits >> 24) & 0xFF; nbits = (8 * (nbytes - 3)); PoW = (nBits & 0xFFFFFF); if ( nbytes > unitval ) { printf("illegal nBits.%x\n",nBits); return(0.); } if ( (n= ((8* (unitval-3)) - nbits)) != 0 ) // 0x1d00ffff is genesis nBits so we map that to 1. { //printf("nbits.%d -> n.%d\n",nbits,n); if ( n < 64 ) PoW /= (1LL << n); else // very rare case efficiency not issue { for (i=0; i %.15f diff %.15f | n.%d unitval.%d nbytes.%d\n",nBits,PoW,1./PoW,n,unitval,nbytes); return(PoW); } int32_t iguana_blockunmain(struct iguana_info *coin,struct iguana_block *block) { struct iguana_block *next; int32_t n = 0; while ( block != 0 ) { //printf("n.%d %p (%p %p) mainchain.%d delink %d\n",n,block,block->hh.prev,block->hh.next,block->mainchain,block->height); block->mainchain = 0; next = block->hh.next; block = next; n++; } if ( n > 1000 ) printf("delinked.%d\n",n); return(n); } struct iguana_block *_iguana_chainlink(struct iguana_info *coin,struct iguana_block *newblock) { int32_t valid,bundlei,height=-1; struct iguana_block *hwmchain,*block = 0,*prev=0,*next; bits256 *hash2p=0; double prevPoW = 0.; if ( newblock == 0 ) return(0); hwmchain = &coin->blocks.hwmchain; if ( (block= iguana_blockfind(coin,newblock->RO.hash2)) != 0 ) { if ( memcmp(coin->chain->genesis_hashdata,block->RO.hash2.bytes,sizeof(bits256)) == 0 ) block->PoW = PoW_from_compact(block->RO.bits,coin->chain->unitval), height = 0; else if ( (prev= iguana_blockfind(coin,block->RO.prev_block)) != 0 ) { if ( memcmp(prev->RO.hash2.bytes,coin->blocks.hwmchain.RO.hash2.bytes,sizeof(bits256)) == 0 ) prev->mainchain = 1; if ( prev->valid != 0 && prev->mainchain != 0 && prev->height >= 0 ) { prevPoW = prev->PoW; block->PoW = PoW_from_compact(block->RO.bits,coin->chain->unitval) + prevPoW; if ( (next= prev->hh.next) != 0 ) { if ( next->mainchain != 0 && block->PoW < next->PoW ) return(0); hwmchain = next; } height = prev->height + 1; } else { //char str[65]; printf("(%s) notready v.%d m.%d h.%d\n",bits256_str(str,prev->RO.hash2),prev->valid,prev->mainchain,prev->height); return(0); } } else { char str[65]; printf("chainlink error: cant find prev.(%s)\n",bits256_str(str,block->RO.prev_block)); //getchar(); return(0); } //char str[65]; printf("extend? %s.h%d: %.15f vs %.15f ht.%d vs %d\n",bits256_str(str,block->RO.hash2),height,block->PoW,coin->blocks.hwmchain.PoW,height,coin->blocks.hwmchain.height); if ( iguana_blockvalidate(coin,&valid,newblock,0) < 0 || valid == 0 ) return(0); block->height = height; block->valid = 1; if ( block->PoW >= hwmchain->PoW ) { block->hh.prev = prev; if ( prev != 0 ) { //if ( prev->hh.next != block ) // iguana_blockunmain(coin,prev->hh.next); prev->hh.next = block; } if ( coin->isRT != 0 || block->height == hwmchain->height ) { coin->blocks.maxblocks = (block->height + 1); coin->blocks.hwmchain = *block; //printf("[%s] <- ht.%d\n",bits256_str(str,block->hash2),coin->blocks.hwmheight); char str[65],str2[65]; bits256 zero; memset(&zero,0,sizeof(zero)); bits256_str(str,newblock->RO.hash2); if ( hash2p != 0 ) bits256_str(str2,*hash2p); else str2[0] = 0; if ( block->height+1 > coin->longestchain ) coin->longestchain = block->height+1; if ( 0 && (block->height % 1000) == 0 ) printf("EXTENDMAIN %s %d <- (%s) n.%u max.%u PoW %f numtx.%d valid.%d\n",str,block->height,str2,hwmchain->height+1,coin->blocks.maxblocks,block->PoW,block->RO.txn_count,block->valid); struct iguana_bundle *bp; if ( (block->height % coin->chain->bundlesize) == 0 ) { bp = iguana_bundlecreate(coin,&bundlei,block->height,block->RO.hash2,zero,0); if ( bp != 0 && bp->hdrsi == coin->bundlescount-1 ) { printf("created last bundle ht.%d\n",bp->bundleheight); iguana_blockreq(coin,block->height,1); } } else { if ( (bp= coin->bundles[block->height / coin->chain->bundlesize]) != 0 ) { if ( memcmp(bp->hashes[block->height % coin->chain->bundlesize].bytes,block->RO.hash2.bytes,sizeof(bits256)) != 0 ) { if ( bits256_nonz(bp->hashes[block->height % coin->chain->bundlesize]) > 0 ) { printf("ERROR: need to fix up bundle for height.%d\n",block->height); //getchar(); } bp->hashes[block->height % coin->chain->bundlesize] = block->RO.hash2; bp->blocks[block->height % coin->chain->bundlesize] = block; } if ( coin->started != 0 && (block->height % coin->chain->bundlesize) == 10 ) { //printf("savehdrs\n"); iguana_savehdrs(coin); //printf("done savehdrs\n"); } } } //if ( block->fpipbits == 0 ) // iguana_blockQ(coin,bp,block->height % coin->chain->bundlesize,block->RO.hash2,1); block->mainchain = 1; //iguana_blockreq(coin,block->height+1,0); return(block); } } } else printf("chainlink error from block.%p\n",block); return(0); } void iguana_blocksetheights(struct iguana_info *coin,struct iguana_block *block) { int32_t height; if ( (height= block->height) < 0 ) return; while ( block != 0 && block->height != height ) { block->height = height; iguana_bundlehash2add(coin,0,coin->bundles[height/coin->chain->bundlesize],height % coin->chain->bundlesize,block->RO.hash2); block = block->hh.next, height++; } } int32_t iguana_chainextend(struct iguana_info *coin,struct iguana_block *newblock) { struct iguana_block *block,*prev; int32_t valid,oldhwm; char str[65]; if ( iguana_blockvalidate(coin,&valid,newblock,0) < 0 || valid == 0 ) { printf("chainextend: newblock.%s didnt validate\n",bits256_str(str,newblock->RO.hash2)); return(-1); } else { block = iguana_blockhashset(coin,-1,newblock->RO.hash2,1); if ( block != newblock ) iguana_blockcopy(coin,block,newblock); block->valid = 1; if ( block->hh.prev == 0 && (prev= iguana_blockfind(coin,block->RO.prev_block)) != 0 ) { if ( prev->hh.next == 0 && block->hh.prev == 0 ) prev->hh.next = block, block->hh.prev = prev; //printf("extend newblock.%s prevm.%d\n",bits256_str(str,block->prev_block),prev->mainchain); if ( prev->mainchain == 0 ) { if ( (block= iguana_blockfind(coin,coin->blocks.hwmchain.RO.hash2)) != 0 && block->mainchain == 0 ) { //printf("c hwmchain is not mainchain anymore?\n"); prev->mainchain = 1; } else return(0); } } if ( memcmp(block->RO.prev_block.bytes,coin->blocks.hwmchain.RO.hash2.bytes,sizeof(bits256)) != 0 ) return(0); } if ( block != 0 ) { oldhwm = coin->blocks.hwmchain.height; //printf("link.%s\n",bits256_str(str,block->hash2)); while ( block != 0 && memcmp(block->RO.hash2.bytes,coin->blocks.hwmchain.RO.hash2.bytes,sizeof(bits256)) != 0 && _iguana_chainlink(coin,block) == block && coin->blocks.hwmchain.height != oldhwm ) { oldhwm = coin->blocks.hwmchain.height; block = block->hh.next; if ( block != 0 ) { if ( bits256_nonz(block->RO.prev_block) == 0 ) break; //printf("next link.%s\n",bits256_str(str,block->hash2)); } } } if ( (block= iguana_blockfind(coin,coin->blocks.hwmchain.RO.hash2)) != 0 && block->mainchain == 0 ) { printf("hwmchain is not mainchain anymore?\n"); block->mainchain = 1; } return(coin->blocks.hwmchain.height); }