Browse Source

properly implement verification using keys that have been replaced.

the basic idea is replace txIndex key with a pair (username,height).
height = -1 for the most up-to-date key, otherwise height = last block where previous key was valid.
by checking maxHeight and iterating backwards we can easily find the key to validate data from any given block number.
miguelfreitas
Miguel Freitas 11 years ago
parent
commit
f81088be70
  1. 1
      TODO
  2. 9
      src/chainparams.cpp
  3. 11
      src/core.cpp
  4. 1
      src/core.h
  5. 99
      src/main.cpp
  6. 6
      src/main.h
  7. 3
      src/rpcdump.cpp
  8. 9
      src/rpcrawtransaction.cpp
  9. 8
      src/rpcwallet.cpp
  10. 12
      src/twister.cpp

1
TODO

@ -7,6 +7,7 @@ pseudocode: @@ -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).

9
src/chainparams.cpp

@ -46,20 +46,21 @@ public: @@ -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<unsigned char>((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"));

11
src/core.cpp

@ -78,13 +78,22 @@ uint256 CTransaction::GetHash() const @@ -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();
}

1
src/core.h

@ -215,6 +215,7 @@ public: @@ -215,6 +215,7 @@ public:
}
uint256 GetHash() const;
std::string GetUsername() const;
uint256 GetUsernameHash() const;
bool IsSpamMessage() const

99
src/main.cpp

@ -335,7 +335,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -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<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock2);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = (*mi).second;
@ -421,7 +421,7 @@ bool DoTxProofOfWork(CTransaction& tx) @@ -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) @@ -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<unsigned char> > vData;
@ -518,7 +517,8 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr @@ -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() @@ -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 @@ -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 @@ -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<std::pair<uint256, CDiskTxPos> > 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 @@ -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 @@ -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 @@ -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 @@ -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() @@ -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<unsigned char> &sal @@ -3572,7 +3598,7 @@ static bool CreateSpamMsgTx(CTransaction &txNew, std::vector<unsigned char> &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<unsigned char> &salt) @@ -3629,7 +3655,8 @@ CBlockTemplate* CreateNewBlock(std::vector<unsigned char> &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);

6
src/main.h

@ -182,9 +182,9 @@ bool IsInitialBlockDownload(); @@ -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); @@ -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

3
src/rpcdump.cpp

@ -93,8 +93,7 @@ Value importprivkey(const Array& params, bool fHelp) @@ -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)");
}
}

9
src/rpcrawtransaction.cpp

@ -122,7 +122,7 @@ Value getrawtransaction(const Array& params, bool fHelp) @@ -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) @@ -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) @@ -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) @@ -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 )

8
src/rpcwallet.cpp

@ -114,12 +114,9 @@ Value createwalletuser(const Array& params, bool fHelp) @@ -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) @@ -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<unsigned char> > vData;

12
src/twister.cpp

@ -404,8 +404,7 @@ void ThreadSessionAlerts() @@ -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) @@ -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) @@ -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) @@ -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);
}

Loading…
Cancel
Save