diff --git a/cppForSwig/BlockObjRef.cpp b/cppForSwig/BlockObjRef.cpp index 018287e55..84857d4fc 100644 --- a/cppForSwig/BlockObjRef.cpp +++ b/cppForSwig/BlockObjRef.cpp @@ -534,6 +534,9 @@ uint32_t TxRef::getBlockHeight(void) // header and try to match up uint32_t TxRef::getBlockTxIndex(void) { + if(headerPtr_ == NULL) + return UINT32_MAX; + vector txlist = headerPtr_->getTxRefPtrList(); for(uint32_t i=0; igetBlockHeight() << endl; cout << "HeadHash: " << headerPtr_->getThisHash().toHexStr(true) << endl; diff --git a/cppForSwig/BlockUtils.cpp b/cppForSwig/BlockUtils.cpp index 926469c41..30cb72b96 100644 --- a/cppForSwig/BlockUtils.cpp +++ b/cppForSwig/BlockUtils.cpp @@ -27,7 +27,11 @@ TxIOPair::TxIOPair(void) : txPtrOfOutput_(NULL), indexOfOutput_(0), txPtrOfInput_(NULL), - indexOfInput_(0) {} + indexOfInput_(0), + txPtrOfOutputZC_(NULL), + indexOfOutputZC_(0), + txPtrOfInputZC_(NULL), + indexOfInputZC_(0) {} ////////////////////////////////////////////////////////////////////////////// TxIOPair::TxIOPair(uint64_t amount) : @@ -35,13 +39,21 @@ TxIOPair::TxIOPair(uint64_t amount) : txPtrOfOutput_(NULL), indexOfOutput_(0), txPtrOfInput_(NULL), - indexOfInput_(0) {} + indexOfInput_(0), + txPtrOfOutputZC_(NULL), + indexOfOutputZC_(0), + txPtrOfInputZC_(NULL), + indexOfInputZC_(0) {} ////////////////////////////////////////////////////////////////////////////// TxIOPair::TxIOPair(TxRef* txPtrO, uint32_t txoutIndex) : amount_(0), txPtrOfInput_(NULL), - indexOfInput_(0) + indexOfInput_(0) , + txPtrOfOutputZC_(NULL), + indexOfOutputZC_(0), + txPtrOfInputZC_(NULL), + indexOfInputZC_(0) { setTxOutRef(txPtrO, txoutIndex); } @@ -52,6 +64,10 @@ TxIOPair::TxIOPair(TxRef* txPtrO, TxRef* txPtrI, uint32_t txinIndex) : amount_(0) + txPtrOfOutputZC_(NULL), + indexOfOutputZC_(0), + txPtrOfInputZC_(NULL), + indexOfInputZC_(0) { setTxOutRef(txPtrO, txoutIndex); setTxInRef (txPtrI, txinIndex ); @@ -77,19 +93,52 @@ BinaryData TxIOPair::getTxHashOfInput(void) } ////////////////////////////////////////////////////////////////////////////// -void TxIOPair::setTxInRef(TxRef* txref, uint32_t index) +bool TxIOPair::setTxInRef(TxRef* txref, uint32_t index, bool isZeroConf) { - txPtrOfInput_ = txref; - indexOfInput_ = index; + bool success=true; + if(isZeroConf) + { + if(hasTxIn() || hasTxInZC()) + success=false; + else + { + txPtrOfInputZC_ = txref; + indexOfInputZC_ = index; + } + } + else + { + txPtrOfInput_ = txref; + indexOfInput_ = index; + } + + return success; } ////////////////////////////////////////////////////////////////////////////// -void TxIOPair::setTxOutRef(TxRef* txref, uint32_t index) +void TxIOPair::setTxOutRef(TxRef* txref, uint32_t index, bool isZeroConf) { - txPtrOfOutput_ = txref; - indexOfOutput_ = index; - if(hasTxOut()) - amount_ = getTxOutRef().getValue(); + bool success=true; + if(isZeroConf) + { + if(hasTxOut() || hasTxOutZC()) + success=false; + else + { + txPtrOfOutputZC_ = txref; + indexOfOutputZC_ = index; + if(hasTxOutZC()) + amount_ = getTxOutRefZC().getValue(); + } + } + else + { + txPtrOfOutput_ = txref; + indexOfOutput_ = index; + if(hasTxOut()) + amount_ = getTxOutRef().getValue(); + } + return success; } ////////////////////////////////////////////////////////////////////////////// @@ -211,6 +260,13 @@ void BtcAddress::sortLedger(void) sort(ledger_.begin(), ledger_.end()); } +void BtcAddress::addLedgerEntry(LedgerEntry const & le, bool isZeroConf) +{ + if(isZeroConf) + ledger_.push_back(le); + else + ledgerZC_.push_back(le); +} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -291,6 +347,7 @@ void BtcWallet::scanTx(TxRef & tx, int64_t totalLedgerAmt = 0; bool anyTxInIsOurs = false; bool anyTxOutIsOurs = false; + bool isZeroConf = (blktime==UINT32_MAX && blknum==UINT32_MAX); vector thisTxInIsOurs (tx.getNumTxIn(), false); vector thisTxOutIsOurs(tx.getNumTxOut(), false); @@ -357,7 +414,6 @@ void BtcWallet::scanTx(TxRef & tx, if( !anyTxOutIsOurs && !anyTxInIsOurs) return; - //////////////////////////////////////////////////////////////////////////// // END BULK FILTER //////////////////////////////////////////////////////////////////////////// @@ -395,8 +451,13 @@ void BtcWallet::scanTx(TxRef & tx, anyTxInIsOurs = true; thisTxInIsOurs[iin] = true; - unspentOutPoints_.erase(outpt); - txio.setTxInRef(&tx, iin); + //unspentOutPoints_.erase(outpt); + // The legit var only identifies whether this set-call succeeded + // If it didn't, it's because this is from a zero-conf tx but this + // TxIn already exists in the blockchain spending the same output. + bool legit = txio.setTxInRef(&tx, iin, isZeroConf); + if(!legit) + continue; int64_t thisVal = (int64_t)txout.getValue(); LedgerEntry newEntry(addr20, @@ -406,7 +467,7 @@ void BtcWallet::scanTx(TxRef & tx, iin, false, // actually we don't know yet if sent to self false); // "isChangeBack" is meaningless for TxIn - thisAddr.addLedgerEntry(newEntry); + thisAddr.addLedgerEntry(newEntry, isZeroConf); totalLedgerAmt -= thisVal; // Update last seen on the network @@ -421,14 +482,14 @@ void BtcWallet::scanTx(TxRef & tx, // be there if(nonStdTxioMap_.find(outpt) != nonStdTxioMap_.end()) { - nonStdTxioMap_[outpt].setTxInRef(&tx, iin); + nonStdTxioMap_[outpt].setTxInRef(&tx, iin, isZeroConf); nonStdUnspentOutPoints_.erase(outpt); } } } // loop over TxIns - ///// LOOP OVER ALL TXOUT IN BLOCK ///// + ///// LOOP OVER ALL TXOUT IN TX ///// for(uint32_t iout=0; iout::iterator, bool> insResult; - pair toBeInserted(outpt, TxIOPair(&tx, iout)); + TxIOPair newTxio; + bool legit = newTxio.setTxOutRef(&tx, iout, isZeroConf); + if(!legit) + continue; + + if(anyTxInIsOurs) + newTxio.setSentToSelf(); + pair toBeInserted(outpt, newTxio); insResult = txioMap_.insert(toBeInserted); TxIOPair & thisTxio = insResult.first->second; if(insResult.second == true) { - unspentOutPoints_.insert(outpt); + //unspentOutPoints_.insert(outpt); anyTxOutIsOurs = true; thisTxOutIsOurs[iout] = true; @@ -463,7 +531,7 @@ void BtcWallet::scanTx(TxRef & tx, iout, anyTxInIsOurs, false); // we don't actually know - thisAddr.addLedgerEntry(newLedger); + thisAddr.addLedgerEntry(newLedger, isZeroConf); totalLedgerAmt += thisVal; // Check if this is the first time we've seen this if(thisAddr.getFirstTimestamp() == 0) @@ -506,13 +574,18 @@ void BtcWallet::scanTx(TxRef & tx, if(txrefSet_.count(&tx) == 0) { txrefSet_.insert(&tx); - ledgerAllAddr_.push_back(LedgerEntry( BinaryData(0), - totalLedgerAmt, - blknum, - tx.getThisHash(), - txIndex, - isSentToSelf, - isChangeBack)); + LedgerEntry le( BinaryData(0), + totalLedgerAmt, + blknum, + tx.getThisHash(), + txIndex, + isSentToSelf, + isChangeBack); + + if(isZeroConf) + ledgerAllAddrZC_.push_back(le) + else + ledgerAllAddr_.push_back(le) } } @@ -543,7 +616,7 @@ vector BtcWallet::getLedgerEntriesForZeroConfTxList( tempWlt.addAddress( addrPtrVect_[i]->getAddrStr20() ); for(uint32_t i=0; i BtcWallet::getLedgerEntriesForZeroConfTxList( + //////////////////////////////////////////////////////////////////////////////// // Need to copy the TxIOMap (objects) to the new wallet, then update the child // addresses with pointers to the new TxIO objects, not the old ones. This @@ -599,7 +673,7 @@ LedgerEntry BtcWallet::getWalletLedgerEntryForTx(BinaryData const & zcBin) BinaryData txBin(zcBin); TxRef txref(txBin); - tempWlt.scanTx(txref, 0, 0xffffffff, 0xffffffff); + tempWlt.scanTx(txref, 0, UINT32_MAX, UINT32_MAX); if(tempWlt.ledgerAllAddr_.size() > 0) return tempWlt.ledgerAllAddr_[0]; @@ -619,7 +693,7 @@ vector BtcWallet::getAddrLedgerEntriesForTx(BinaryData const & zcBi BinaryData txBin(zcBin); TxRef txref(txBin); - tempWlt.scanTx(txref, 0, 0xffffffff, 0xffffffff); + tempWlt.scanTx(txref, 0, UINT32_MAX, UINT32_MAX); vector leVect(0); for(uint32_t i=0; i::iterator unspentIter; for( unspentIter = unspentOutPoints_.begin(); @@ -699,12 +774,15 @@ uint64_t BtcWallet::getBalance(void) { TxIOPair & txio = txioMap_[*unspentIter]; if(txio.getTxRefOfOutput().isMainBranch() && - !isTxOutLocked(*unspentIter)) + (!isTxOutLocked(*unspentIter) || blockchainOnly)) { balance += txioMap_[*unspentIter].getValue(); } } + + return balance; + */ } @@ -746,20 +824,32 @@ void BtcWallet::sortLedger(void) } //////////////////////////////////////////////////////////////////////////////// -void BtcWallet::lockTxOut(OutPoint const & op) +bool BtcWallet::lockTxOut(OutPoint const & op) { + bool newLocked = false; set & unspentOps = getUnspentOutPoints(); if(unspentOps.find(op)!=unspentOps.end()) + { lockedTxOuts_.insert(op); + newLocked = true; + } + return newLocked; } //////////////////////////////////////////////////////////////////////////////// -void BtcWallet::unlockTxOut(OutPoint const & op) +bool BtcWallet::unlockTxOut(OutPoint const & op) { + bool newUnlocked = false; set & unspentOps = getUnspentOutPoints(); if(unspentOps.find(op)!=unspentOps.end()) + { if(lockedTxOuts_.find(op)!=lockedTxOuts_.end()) + { lockedTxOuts_.erase(op); + newUnlocked = true; + } + } + return newUnlocked; } @@ -2072,22 +2162,45 @@ int64_t BlockDataManager_FullRAM::getSentValue(TxInRef & txin) //////////////////////////////////////////////////////////////////////////////// vector -BlockDataManager_FullRAM::getUnspentTxOutsForWallet( BtcWallet & wlt, int sortType) +BlockDataManager_FullRAM::getUnspentTxOutsForWallet( BtcWallet & wlt, + int sortType, + bool blockchainOnly) { vector result(0); + + // Iterate over all unspent TxOuts in blockchain, maybe ignore zeroconf spent set & unspentOps = wlt.getUnspentOutPoints(); set::iterator opIter; for(opIter = unspentOps.begin(); opIter != unspentOps.end(); opIter++) { - if( !wlt.isTxOutLocked(*opIter) ) + if( !wlt.isTxOutLocked(*opIter) or blockchainOnly) { TxRef & tx = *(getTxByHash(opIter->getTxHash())); uint32_t currBlk = getTopBlockHeader().getBlockHeight(); TxOutRef txout = tx.getTxOutRef(opIter->getTxOutIndex()); UnspentTxOut uto(txout, currBlk); result.push_back(uto); + + } + } + + // If we're considering zero-conf tx, include the ones to self + if( !blockchainOnly ) + { + set & myZcToSelf = wlt.getMyZeroConfOutPointsToSelf(); + set::iterator iter; + for(iter = myZcToSelf.begin(); + iter != myZcToSelf.end(); + iter++) + { + OutPoint & op = *iter; + TxRef & tx = zeroConfMap_[op].txref_; + + TxOutRef txout = tx.getTxOutRef(op.getTxOutIndex()); + UnspentTxOut uto(txout, 0); + result.push_back(uto); } } @@ -2191,6 +2304,7 @@ void BlockDataManager_FullRAM::purgeZeroConfPool(void) { list< map::iterator > mapRmList; + // Find all zero-conf transactions that made it into the blockchain map::iterator iter; for(iter = zeroConfMap_.begin(); iter != zeroConfMap_.end(); @@ -2198,29 +2312,124 @@ void BlockDataManager_FullRAM::purgeZeroConfPool(void) { TxRef* txInBlockchain = getTxByHash(iter->first); if(txInBlockchain != NULL) - mapRmList.push_back(ter); + mapRmList.push_back(iter); } - + // We've made a list of the zc tx to remove, now let's remove them + // I decided this was safer than erasing the data as we were iterating + // over it in the previous loop list< map::iterator >::iterator iter; for(iter = mapRmList.begin(); iter != mapRmList.end(); iter++) { + // Remove from the zero-conf txOuts map + for(uint32_t iin=0; iinsecond.iter_ ); zeroConfMap_.erase( *iter ) } + + // Rewrite the zero-conf pool file + rewriteZeroConfFile(); } + +//////////////////////////////////////////////////////////////////////////////// +void BlockDataManager_FullRAM::rewriteZeroConfFile(void) +{ + ofstream zcFile(zcFilename_, ios::out | ios::binary); + + map::iterator iter; + for(iter = zeroConfMap_.begin(); + iter != zeroConfMap_.end(); + iter++) + { + zcFile.write( (char*)(&zc.txtime_), sizeof(uint64_t) ); + zcFile.write( (char*)(&zc.txref_.getPtr()), zc.txref_.getSize()) + } + + zcFile.close(); +} + + +//////////////////////////////////////////////////////////////////////////////// void BlockDataManager_FullRAM::updateWalletWithZeroConf(BtcWallet & wlt) { - + // Clear the whole list, rebuild + // Inefficient but also irrelevant unless we have millions of + // zero-conf transactions per second... I'll take the risk... + + wlt.clearZeroConfPool(); + map::iterator iter; + for(iter = zeroConfMap_.begin(); + iter != zeroConfMap_.end(); + iter++) + { + TxRef & txref = iter->second.txref_; + + // (Re-)lock any TxOuts spent + bool anyInputsMine = false; + for(uint32_t iin=0; iin BtcWallet::getZeroConfLedger(void) +{ + +} + +vector BtcWallet::getZeroConfLedgerForAddr(BinaryData const & addr160) +{ + +} diff --git a/cppForSwig/BlockUtils.h b/cppForSwig/BlockUtils.h index 058669c92..2654f2831 100644 --- a/cppForSwig/BlockUtils.h +++ b/cppForSwig/BlockUtils.h @@ -77,40 +77,56 @@ class TxIOPair TxIOPair(TxRef* txPtrO, uint32_t txoutIndex, TxRef* txPtrI, uint32_t txinIndex); // Lots of accessors - bool hasTxOut(void) { return (txPtrOfOutput_ != NULL); } - bool hasTxIn(void) { return (txPtrOfInput_ != NULL); } + bool hasTxOut(void) { return (txPtrOfOutput_ != NULL); } + bool hasTxIn(void) { return (txPtrOfInput_ != NULL); } + bool hasTxOutZC(void) { return (txPtrOfOutputZC_ != NULL); } + bool hasTxInZC(void) { return (txPtrOfInputZC_ != NULL); } bool hasValue(void) { return (amount_!=0); } uint64_t getValue(void) { return amount_;} ////////////////////////////////////////////////////////////////////////////// - TxOutRef getTxOutRef(void) const {return txPtrOfOutput_->getTxOutRef(indexOfOutput_);} - TxInRef getTxInRef(void) const {return txPtrOfInput_->getTxInRef(indexOfInput_);} + TxOutRef getTxOutRef(void) const {return txPtrOfOutput_->getTxOutRef(indexOfOutput_);} + TxInRef getTxInRef(void) const {return txPtrOfInput_->getTxInRef(indexOfInput_);} + TxOutRef getTxOutRefZC(void) const {return txPtrOfOutputZC_->getTxOutRef(indexOfOutputZC_);} + TxInRef getTxInRefZC(void) const {return txPtrOfInputZC_->getTxInRef(indexOfInputZC_);} TxRef& getTxRefOfOutput(void) const { return *txPtrOfOutput_; } TxRef& getTxRefOfInput(void) const { return *txPtrOfInput_; } OutPoint getOutPoint(void) { return OutPoint(getTxHashOfOutput(),indexOfOutput_);} pair reassessValidity(void); + bool isSentToSelf(void) { return isSentToSelf_; } + bool setSentToSelf(bool isTrue=true) { isSentToSelf_ = isTrue; } ////////////////////////////////////////////////////////////////////////////// BinaryData getTxHashOfInput(void); BinaryData getTxHashOfOutput(void); - void setTxInRef (TxRef* txref, uint32_t index); - void setTxOutRef(TxRef* txref, uint32_t index); + void setTxInRef (TxRef* txref, uint32_t index, bool isZeroConf=false); + void setTxOutRef (TxRef* txref, uint32_t index, bool isZeroConf=false); ////////////////////////////////////////////////////////////////////////////// - bool isUnspent(void) { return ( hasTxOut() && !hasTxIn() ); } bool isSpent(void) { return ( hasTxOut() && hasTxIn() ); } + bool isUnspent(void) { return ( hasTxOut() && !hasTxIn() ); } bool isSourceUnknown(void) { return ( !hasTxOut() && hasTxIn() ); } bool isStandardTxOutScript(void); + bool isSpentZC(void) { return ( hasTxOut() && hasTxIn() ); } + bool isUnspentZC(void) { return ( hasTxOut() && !hasTxIn() ); } + bool isSpendable(void); + private: uint64_t amount_; TxRef* txPtrOfOutput_; - uint32_t indexOfOutput_;; + uint32_t indexOfOutput_; TxRef* txPtrOfInput_; - uint32_t indexOfInput_;; + uint32_t indexOfInput_; + + TxRef* txPtrOfOutputZC_; + uint32_t indexOfOutputZC_; + TxRef* txPtrOfInputZC_; + uint32_t indexOfInputZC_; + bool isSentToSelf_; }; @@ -261,7 +277,7 @@ class BtcAddress void addTxIO(TxIOPair * txio) { relevantTxIOPtrs_.push_back(txio);} void addTxIO(TxIOPair & txio) { relevantTxIOPtrs_.push_back(&txio);} - void addLedgerEntry(LedgerEntry const & le) { ledger_.push_back(le);} + void addLedgerEntry(LedgerEntry const & le, bool isZeroConf); private: @@ -274,6 +290,7 @@ class BtcAddress // Each address will store a list of pointers to its transactions vector relevantTxIOPtrs_; vector ledger_; + vector ledgerZC_; }; @@ -286,6 +303,8 @@ class BtcAddress //////////////////////////////////////////////////////////////////////////////// class BtcWallet { + + public: BtcWallet(void) {} @@ -362,9 +381,9 @@ class BtcWallet // If we have spent TxOuts but the tx haven't made it into the blockchain // we need to lock them to make sure we have a record of which ones are // available to sign more Txs - void lockTxOut(OutPoint const & op); - void unlockTxOut(OutPoint const & op); - void clearLocked(void) {lockedTxOuts_.clear(); } + bool lockTxOut(OutPoint const & op); + bool unlockTxOut(OutPoint const & op); + void clearZeroConfPool(void); {lockedTxOuts_.clear(); } void lockTxOutSwig(BinaryData const & hash, uint32_t idx); void unlockTxOutSwig(BinaryData const & hash, uint32_t idx); @@ -374,15 +393,21 @@ class BtcWallet bool isOutPointMine(BinaryData const & hsh, uint32_t idx); - // This really shouldn't ever be used except for the zero-conf ops - void makeTempCopyForZcScan(BtcWallet & tempWlt); + + map & getMyZeroConfTxOuts(void) {return myZeroConfTxOuts_;} + set & getMyZeroConfOutPointsToSelf(void) {return myZeroConfOutPointsToSelf_;} private: vector addrPtrVect_; map addrMap_; map txioMap_; + map myZeroConfTxOuts_; + set myZeroConfOutPointsToSelf_; + + vector ledgerAllAddr_; + vector ledgerAllAddrZC_; set unspentOutPoints_; set lockedTxOuts_; @@ -395,16 +420,21 @@ class BtcWallet }; - +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// class ZeroConfData { public: TxRef txref_; uint64_t txtime_; list::iterator iter_; + }; + + + // Some might argue that inheritance would be useful here. I'm not a software // guy, and I have to write all the methods for each class anyway. So I'm // foregoing the inheritance. Just writing each class separately