#else
int fUseUPnP = false;
#endif
-
+int64 nBalanceReserve = 0;
//////////////////////////////////////////////////////////////////////////////
//
}
// make sure all wallets know about the given transaction, in the given block
-void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false)
+void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true)
{
+ if (!fConnect)
+ {
+ // ppcoin: wallets need to refund inputs when disconnecting coinstake
+ if (tx.IsCoinStake())
+ BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ if (pwallet->IsFromMe(tx))
+ pwallet->DisableTransaction(tx);
+ return;
+ }
+
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
return error("DisconnectBlock() : WriteBlockIndex failed");
}
+ // ppcoin: clean up wallet after disconnecting coinstake
+ BOOST_FOREACH(CTransaction& tx, vtx)
+ SyncWithWallets(tx, this, false, false);
+
return true;
}
if (!CheckBlock())
return false;
+ // ppcoin: coin stake tx must meet target protocol
+ if (IsProofOfStake() && !vtx[1].CheckProofOfStake(txdb, nBits))
+ return error("ConnectBlock() : Block %s unable to meet hash target for coinstake", GetHash().ToString().c_str());
+
//// issue here: it doesn't know the version
- unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size());
+ unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
}
+// ppcoin: coinstake must meet hash target according to the protocol:
+// at least one input must meet the formula
+// hash(nBits + txPrev.block.nTime + txPrev.nTime + nTime) < bnTarget * nCoinDay
+// this ensures that the chance of getting a coinstake is proportional to the
+// amount of coin age one owns.
+// The reason this hash is chosen is the following:
+// nBits: encodes all past block timestamps, prevents computing hash in advance
+// txPrev.block.nTime: prevent nodes from guessing a good timestamp to generate
+// transaction for future advantage
+// txPrev.nTime: prevent nodes from meeting target simultaneously
+// block/tx hash should not be used here as they can be generated in vast quatities
+// so as to generate blocks faster, degrading the system back into a proof-of-work
+// situation.
+//
+bool CTransaction::CheckProofOfStake(CTxDB& txdb, unsigned int nBits) const
+{
+ CBigNum bnTargetPerCoinDay;
+ bnTargetPerCoinDay.SetCompact(nBits);
+
+ if (!IsCoinStake())
+ return true;
+
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ {
+ // First try finding the previous transaction in database
+ CTransaction txPrev;
+ CTxIndex txindex;
+ if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ continue; // previous transaction not in main chain
+ if (nTime < txPrev.nTime)
+ return false; // Transaction timestamp violation
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ return false; // unable to read block of previous transaction
+ if (block.GetBlockTime() + AUTO_CHECKPOINT_TRUST_SPAN > nTime)
+ continue; // only count coins from at least one week ago
+
+ int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, VERSION);
+ ss << nBits << block.nTime << txPrev.nTime << nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ return true;
+ }
+
+ return false;
+}
+
// ppcoin: total coin age spent in transaction, in the unit of coin-days.
// Only those coins last spent at least a week ago count. As those
// transactions not in main chain are not currently indexed so we
return DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
- if (!CheckProofOfWork(GetHash(), nBits))
+ if (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
if (hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
+ // Coin base vout[0] scriptPubKey must be the same as coin stake vout[1]
+ // scriptPubKey
+ if (vtx.size() > 1 && vtx[1].IsCoinStake() &&
+ vtx[0].vout[0].scriptPubKey != vtx[1].vout[1].scriptPubKey)
+ return DoS(100, error("CheckBlock() : block key mismatch"));
+
+
+ // Check block signature
+ if (!CheckBlockSignature())
+ return DoS(100, error("CheckBlock() : bad block signature"));
+
return true;
}
assert(block.hashMerkleRoot == uint256("0xc7311b56de266580cca65be108ae53d7100b5c3b17da8b1106044103abd7a521"));
block.print();
assert(block.GetHash() == hashGenesisBlock);
+ assert(block.CheckBlock());
// Start new block file
unsigned int nFile;
return true;
}
+ // ppcoin: record my external IP reported by peer
+ if (addrFrom.IsRoutable() && addrMe.IsRoutable())
+ addrSeenByPeer = addrMe;
+
// Be shy and don't send version until we hear
if (pfrom->fInbound)
pfrom->PushVersion();
if (!pblock.get())
return NULL;
+ pblock->nBits = GetNextWorkRequired(pindexPrev);
+
// Create coinbase tx
CTransaction txNew;
txNew.vin.resize(1);
// ppcoin: if coinstake available add coinstake tx
CTransaction txCoinStake;
- if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, txCoinStake))
+ if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, pblock->nBits, txCoinStake))
pblock->vtx.push_back(txCoinStake);
// Collect memory pool transactions into the block
}
}
}
- pblock->nBits = GetNextWorkRequired(pindexPrev);
pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
// Fill in header
// Found a solution
pblock->nNonce = ByteReverse(nNonceFound);
assert(hash == pblock->GetHash());
+ // should be able to sign block - assert here for now
+ assert(pblock->SignBlock(*pwalletMain));
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock.get(), *pwalletMain, reservekey);