diff --git a/TODO b/TODO index 90a2d733..fcea3716 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,7 @@ pseudocode: getTxIndex( key = "userX" ) => block h contains this tx; while( h > max_h ) getTxIndex( "userX_h" ) => block h contains the previous tx +=> GetTransation: new parameter maxHeight done! - Until old public key is properly used, disable banning torrent peers due to bad piece hashes. note: torrent.cpp line 3286 (function piece_failed), iteration to ban peers is disabled (continue). diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0a03e37d..25c46c10 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -46,20 +46,21 @@ public: // vMerkleTree: 4a5e1e const char* pszTimestamp = "The Times 09/Jul/2013 Globo caught bribing Receita Federal employee to rob R$615M worth tax evasion documents."; CTransaction txNew; - txNew.message = CScript() << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.message = CScript() << string(pszTimestamp); + txNew.userName = CScript() << string("nobody"); txNew.nNonce = 0; // spamMessage is not required to show POW to ease "extranonce" support genesis.vtx.push_back(txNew); genesis.hashPrevBlock = 0; genesis.hashMerkleRoot = genesis.BuildMerkleTree(); genesis.nVersion = 1; genesis.nHeight = 0; - genesis.nTime = 1373331180; + genesis.nTime = 1383612321; //genesis.nBits = 0x1d00ffff; genesis.nBits = 0x1d7fffff; - genesis.nNonce = 636561156; + genesis.nNonce = 1353330432; hashGenesisBlock = genesis.GetHash(); - assert(hashGenesisBlock == uint256("0x000000792bc5674af6fa5c006149d78c47265f7e03d2ca7f5a094b2475ef1328")); + assert(hashGenesisBlock == uint256("0000001d5eb70d639089eb9b31a4175aa906d6824aae78a9b450ab71de5e6e12")); vSeeds.push_back(CDNSSeedData("twister.net.co", "seed.twister.net.co")); diff --git a/src/core.cpp b/src/core.cpp index 21f07582..6885e0a8 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -78,13 +78,22 @@ uint256 CTransaction::GetHash() const return SerializeHash(*this); } +std::string CTransaction::GetUsername() const +{ + if(userName.IsSmallString()) { + return userName.ExtractSmallString(); + } + assert(!"username not small string"); + return std::string(); +} + uint256 CTransaction::GetUsernameHash() const { if(userName.IsSmallString()) { return SerializeHash(userName.ExtractSmallString()); } // [MF] TODO: remove this assert later, it will fail for spammessage. - //assert(!"username not small string"); + assert(!"username not small string"); return uint256(); } diff --git a/src/core.h b/src/core.h index 88288bad..fff039cc 100644 --- a/src/core.h +++ b/src/core.h @@ -215,6 +215,7 @@ public: } uint256 GetHash() const; + std::string GetUsername() const; uint256 GetUsernameHash() const; bool IsSpamMessage() const diff --git a/src/main.cpp b/src/main.cpp index 34993fe2..c9ec0b36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -335,7 +335,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) CTransaction tx2; uint256 hashBlock2; - if( GetTransaction(GetUsernameHash(), tx2, hashBlock2) && hashBlock2 != uint256() ) { + if( GetTransaction(GetUsername(), tx2, hashBlock2) && hashBlock2 != uint256() ) { std::map::iterator mi = mapBlockIndex.find(hashBlock2); if (mi != mapBlockIndex.end()) { CBlockIndex *pindex = (*mi).second; @@ -421,7 +421,7 @@ bool DoTxProofOfWork(CTransaction& tx) } // [MF] check tx consistency and pow, not duplicated id. -bool CheckTransaction(const CTransaction& tx, CValidationState &state) +bool CheckTransaction(const CTransaction& tx, CValidationState &state, int maxHeight) { // Basic checks that don't depend on any context if (tx.IsSpamMessage()) { @@ -442,8 +442,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) CPubKey pubkey; CTransaction txPubKey; uint256 hashBlock; - uint256 userhash = SerializeHash(spamUser); - if( !GetTransaction(userhash, txPubKey, hashBlock) ) + if( !GetTransaction(spamUser, txPubKey, hashBlock, maxHeight) ) return state.DoS(100, error("CheckTransaction() : spam signed by unknown user")); std::vector< std::vector > vData; @@ -518,7 +517,8 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr { // do we already have it? - if( pblocktree->HaveTxIndex(tx.GetUsernameHash()) && + uint256 txid = SerializeHash(make_pair(tx.GetUsername(),-1)); + if( pblocktree->HaveTxIndex(txid) && // duplicate should be discarded but replacement is allowed. !verifyDuplicateOrReplacementTx(tx, false, true) ) { return false; @@ -666,24 +666,21 @@ bool CWalletTx::AcceptWalletTransaction() // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock -bool GetTransaction(const uint256 &userhash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) +bool GetTransaction(const std::string &username, CTransaction &txOut, uint256 &hashBlock, int maxHeight) { + if( maxHeight < 0 ) + maxHeight = nBestHeight; { LOCK(cs_main); - { - /* [MF] don't look in mempool anymore - userhash must be written to some block. - LOCK(mempool.cs); - if (mempool.exists(userhash)) // and btw mempool is not indexed by userhash anymore! - { - txOut = mempool.lookup(userhash); - return true; - } - */ - } - if (fTxIndex) { + assert(fTxIndex); + + int height = -1; + do { + uint256 txid = SerializeHash(make_pair(username,height)); + CDiskTxPos postx; - if (pblocktree->ReadTxIndex(userhash, postx)) { + if (pblocktree->ReadTxIndex(txid, postx)) { CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); CBlockHeader header; try { @@ -694,21 +691,29 @@ bool GetTransaction(const uint256 &userhash, CTransaction &txOut, uint256 &hashB return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); } hashBlock = header.GetHash(); - if (txOut.GetUsernameHash() != userhash) + if (txOut.GetUsername() != username) return error("%s() : txid mismatch", __PRETTY_FUNCTION__); - return true; + if (header.nHeight > maxHeight) { + printf("GetTransaction: %s height: %d blkHeight: %d maxHeight: %d\n", + username.c_str(), height, header.nHeight, maxHeight); + height = header.nHeight-1; + } else { + return true; + } + } else { + break; } - } + } while( height > 0 && height <= maxHeight ); } return false; } -bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement) +bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement, int maxHeight) { CTransaction oldTx; uint256 hashBlock; - if( GetTransaction( tx.GetUsernameHash(), oldTx, hashBlock) ) { + if( GetTransaction( tx.GetUsername(), oldTx, hashBlock, maxHeight) ) { if( checkDuplicate && oldTx.GetHash() == tx.GetHash() ) { return true; } @@ -1072,8 +1077,20 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex for (int i = block.vtx.size() - 1; i >= 1; i--) { const CTransaction &tx = block.vtx[i]; - if( !fJustCheck ) - pblocktree->EraseTxIndex(tx.GetUsernameHash()); + if( !fJustCheck ) { + uint256 oldTxid = SerializeHash(make_pair(tx.GetUsername(),block.nHeight-1)); + CDiskTxPos oldPos; + if( pblocktree->ReadTxIndex(oldTxid, oldPos) ) { + printf("DisconnectBlock: restoring old txid user: %s height: %d\n", + tx.GetUsername().c_str(), block.nHeight); + + uint256 txid = SerializeHash(make_pair(tx.GetUsername(),-1)); + std::vector > vPos; + vPos.push_back(std::make_pair(txid, oldPos)); + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); + } + } } // move best block pointer to prevout block @@ -1129,14 +1146,15 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C for (unsigned int i = 1; i < block.vtx.size(); i++) { CTransaction &tx = block.vtx[i]; - if( pblocktree->HaveTxIndex(block.vtx[i].GetUsernameHash()) ) { + uint256 txid = SerializeHash(make_pair(block.vtx[i].GetUsername(),-1)); + if( pblocktree->HaveTxIndex(txid) ) { /* We have index for this username, which is not allowed, except: * 1) same transaction. this shouldn't happen but it does if twisterd terminates badly. * explanation: TxIndex seems to get out-of-sync with block chain, so it may try to * reconnect blocks which transactions are already written to the tx index. * 2) possibly a key replacement. check if new key is signed by the old one. */ - if( !verifyDuplicateOrReplacementTx(tx, true, true) ) { + if( !verifyDuplicateOrReplacementTx(tx, true, true, block.nHeight) ) { // not the same, not replacement => error! return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction")); } @@ -1156,7 +1174,16 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C CTxUndo txundo; if (!tx.IsSpamMessage()) { blockundo.vtxundo.push_back(txundo); - vPos.push_back(std::make_pair(tx.GetUsernameHash(), pos)); + uint256 txid = SerializeHash(make_pair(tx.GetUsername(),-1)); + vPos.push_back(std::make_pair(txid, pos)); + + CDiskTxPos oldPos; + if( pblocktree->ReadTxIndex(txid, oldPos) && pos != oldPos ) { + printf("ConnectBlock: save old txid user: %s height: %d\n", + tx.GetUsername().c_str(), block.nHeight); + uint256 oldTxid = SerializeHash(make_pair(tx.GetUsername(),block.nHeight-1)); + vPos.push_back(std::make_pair(oldTxid, oldPos)); + } } pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } @@ -1189,10 +1216,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write block index")); } - if (fTxIndex) { - if (!pblocktree->WriteTxIndex(vPos)) - return state.Abort(_("Failed to write transaction index")); - } + assert(fTxIndex); + if (!pblocktree->WriteTxIndex(vPos)) + return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain assert(view.SetBestBlock(pindex)); @@ -1548,7 +1574,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check transactions (consistency, not duplicated id) BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!CheckTransaction(tx, state)) + if (!CheckTransaction(tx, state, block.nHeight)) return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the @@ -2035,7 +2061,7 @@ bool static LoadBlockIndexDB() bool readTxIndex; pblocktree->ReadFlag("txindex", readTxIndex); printf("LoadBlockIndexDB(): transaction index %s\n", readTxIndex ? "enabled" : "disabled"); - //assert( readTxIndex ); // always true in twister + assert( readTxIndex ); // always true in twister // Load hashBestChain pointer to end of best chain pindexBest = pcoinsTip->GetBestBlock(); @@ -3572,7 +3598,7 @@ static bool CreateSpamMsgTx(CTransaction &txNew, std::vector &sal CValidationState state; bool ret = CheckTransaction(txNew, state); - printf("CheckTransaction returned %d\n", ret ); + printf("CreateSpamMsgTx: CheckTransaction returned %d\n", ret ); return true; } @@ -3629,7 +3655,8 @@ CBlockTemplate* CreateNewBlock(std::vector &salt) continue; // This should never happen; all transactions in the memory are new - if( pblocktree->HaveTxIndex(tx.GetUsernameHash()) ) { + uint256 txid = SerializeHash(make_pair(tx.GetUsername(),-1)); + if( pblocktree->HaveTxIndex(txid) ) { if( !verifyDuplicateOrReplacementTx(tx, false, true) ) { printf("ERROR: mempool transaction already exists\n"); if (fDebug) assert("mempool transaction already exists" == 0); diff --git a/src/main.h b/src/main.h index b97be4fb..1c18bbe3 100644 --- a/src/main.h +++ b/src/main.h @@ -182,9 +182,9 @@ bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core */ std::string GetWarnings(std::string strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ -bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); +bool GetTransaction(const std::string &username, CTransaction &tx, uint256 &hashBlock, int maxHeight = -1); /** Verify duplicate or replacement transactions */ -bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement); +bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement, int maxHeight = -1); /** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ @@ -275,7 +275,7 @@ bool CheckUsername(const std::string &userName, CValidationState &state); bool DoTxProofOfWork(CTransaction& tx); // Context-independent validity checks -bool CheckTransaction(const CTransaction& tx, CValidationState& state); +bool CheckTransaction(const CTransaction& tx, CValidationState& state, int maxHeight = -1); /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 9116dc29..4aae8317 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -93,8 +93,7 @@ Value importprivkey(const Array& params, bool fHelp) if( !fAllowNewUser ) { CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(strUsername); - if( !GetTransaction(userhash, txOut, hashBlock) ) { + if( !GetTransaction(strUsername, txOut, hashBlock) ) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "User must exist (or allow_new_user flag must be set)"); } } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index ca529b26..f043f083 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -122,7 +122,7 @@ Value getrawtransaction(const Array& params, bool fHelp) "with information about transaction."); //uint256 hash = ParseHashV(params[0], "parameter 1"); - uint256 hash = SerializeHash(params[0].get_str()); + std::string username = params[0].get_str(); bool fVerbose = false; if (params.size() > 1) @@ -130,7 +130,7 @@ Value getrawtransaction(const Array& params, bool fHelp) CTransaction tx; uint256 hashBlock = 0; - if (!GetTransaction(hash, tx, hashBlock, true)) + if (!GetTransaction(username, tx, hashBlock)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -227,7 +227,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) bool fHave = false; uint256 hashBlock; CTransaction tx2; - fHave = GetTransaction(tx.GetUsernameHash(), tx2, hashBlock); + fHave = GetTransaction(tx.GetUsername(), tx2, hashBlock); // treat replacement as !fHave if( fHave && verifyDuplicateOrReplacementTx(tx, false, true) ) { @@ -283,8 +283,7 @@ Value sendnewusertransaction(const Array& params, bool fHelp) // [MF] prevent redoing POW and resending an existing transaction CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(strUsername); - bool userInTxIndex = GetTransaction(userhash, txOut, hashBlock); + bool userInTxIndex = GetTransaction(strUsername, txOut, hashBlock); if( !replaceKey && userInTxIndex ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username exists in tx database"); if( replaceKey && !userInTxIndex ) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index c611ba02..70d8a597 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -114,12 +114,9 @@ Value createwalletuser(const Array& params, bool fHelp) if( !keyInWallet && replaceKey ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: replacekey given but old key not in wallet"); - uint256 userhash = SerializeHash(strUsername); - printf("createwalletuser: usernamehash(%s) = %s\n", strUsername.c_str(), userhash.GetHex().c_str()); - CTransaction txOut; uint256 hashBlock; - if( GetTransaction(userhash, txOut, hashBlock) && !replaceKey ) + if( GetTransaction(strUsername, txOut, hashBlock) && !replaceKey ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username exists in tx database"); if( replaceKey && !pwalletMain->MoveKeyForReplacement(strUsername) ) @@ -259,8 +256,7 @@ Value verifymessage(const Array& params, bool fHelp) if( !pubkey.IsValid() ) { CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(strUsername); - if( !GetTransaction(userhash, txOut, hashBlock) ) + if( !GetTransaction(strUsername, txOut, hashBlock) ) throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Error: this username does not exist in tx database"); std::vector< std::vector > vData; diff --git a/src/twister.cpp b/src/twister.cpp index 22903200..8784e6f5 100644 --- a/src/twister.cpp +++ b/src/twister.cpp @@ -404,8 +404,7 @@ void ThreadSessionAlerts() // check if user exists CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(n->string()); - if( !GetTransaction(userhash, txOut, hashBlock) ) { + if( !GetTransaction(n->string(), txOut, hashBlock) ) { printf("Special Resource but username is unknown - ignoring\n"); } else { // now we do our own search to make sure we are really close to this target @@ -621,8 +620,7 @@ bool getUserPubKey(std::string const &strUsername, CPubKey &pubkey) if( !pubkey.IsValid() ) { CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(strUsername); - if( !GetTransaction(userhash, txOut, hashBlock) ) { + if( !GetTransaction(strUsername, txOut, hashBlock) ) { //printf("getUserPubKey: user unknown '%s'\n", strUsername.c_str()); return false; } @@ -807,8 +805,7 @@ bool validatePostNumberForUser(std::string const &username, int k) { CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(username); - if( !GetTransaction(userhash, txOut, hashBlock) ) { + if( !GetTransaction(username, txOut, hashBlock) ) { printf("validatePostNumberForUser: username is unknown\n"); return false; } @@ -827,8 +824,7 @@ bool usernameExists(std::string const &username) { CTransaction txOut; uint256 hashBlock; - uint256 userhash = SerializeHash(username); - return GetTransaction(userhash, txOut, hashBlock); + return GetTransaction(username, txOut, hashBlock); }