Browse Source

add support (untested) for key replacement

miguelfreitas
Miguel Freitas 11 years ago
parent
commit
b6db2327e4
  1. 70
      src/main.cpp
  2. 2
      src/main.h

70
src/main.cpp

@ -483,8 +483,11 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr @@ -483,8 +483,11 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
{
// do we already have it?
if( pblocktree->HaveTxIndex(userhash) )
if( pblocktree->HaveTxIndex(userhash) &&
// duplicate should be discarded but replacement is allowed.
!verifyDuplicateOrReplacementTx(tx, false, true) ) {
return false;
}
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
@ -664,8 +667,40 @@ bool GetTransaction(const uint256 &userhash, CTransaction &txOut, uint256 &hashB @@ -664,8 +667,40 @@ bool GetTransaction(const uint256 &userhash, CTransaction &txOut, uint256 &hashB
return false;
}
bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement)
{
CTransaction oldTx;
uint256 hashBlock;
if( GetTransaction( tx.GetUsernameHash(), oldTx, hashBlock) ) {
if( checkDuplicate && oldTx.GetHash() == tx.GetHash() ) {
return true;
}
vector< vector<unsigned char> > vData;
if( checkReplacement && tx.pubKey.ExtractPushData(vData) && vData.size() >= 2 ) {
// possibly a key replacement. check if new key is signed by the old one.
// vData[0] is the (supposedly) new pub key
string strNewKey( (char *)vData[0].data(), vData[0].size() );
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strNewKey;
uint256 hashNewKey = ss.GetHash();
// vData[1] is (supposedly) the hash of the new key signed with the old one
CPubKey pubkeyRec;
vector< vector<unsigned char> > oldvData;
if (pubkeyRec.RecoverCompact(hashNewKey, vData[1]) &&
oldTx.pubKey.ExtractPushData(oldvData) &&
oldvData.size() >= 1 &&
pubkeyRec.GetID() == CPubKey(oldvData[0]).GetID()) {
// good signature. key replacement allowed.
return true;
}
}
}
return false;
}
@ -1053,9 +1088,22 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C @@ -1053,9 +1088,22 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
}
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// except if they are signed by the older one (key replacement)
for (unsigned int i = 1; i < block.vtx.size(); i++) {
if( pblocktree->HaveTxIndex(block.vtx[i].GetUsernameHash()) )
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
CTransaction &tx = block.vtx[i];
if( pblocktree->HaveTxIndex(block.vtx[i].GetUsernameHash()) ) {
/* 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) ) {
// not the same, not replacement => error!
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
}
}
}
CBlockUndo blockundo;
@ -2335,9 +2383,17 @@ bool static AlreadyHave(const CInv& inv) @@ -2335,9 +2383,17 @@ bool static AlreadyHave(const CInv& inv)
txInMap = mempool.exists(inv.hash);
}
bool txInTxIndex = false;
/* [MF] checkme: because of key replacement, we better not check
* txIndex but rather only memory. "mempool" cmd is replied with
* "inv" cmd of the current txs in memory. if recipient decides he
* already has a given tx in txindex (by calling this function),
* he would never ask/receive the key replacement.
*/
/*
if( !txInMap ) {
txInTxIndex = pblocktree->HaveTxIndex(inv.hash);
}
*/
return txInMap || txInTxIndex;
}
case MSG_BLOCK:
@ -3534,8 +3590,10 @@ CBlockTemplate* CreateNewBlock(std::vector<unsigned char> &salt) @@ -3534,8 +3590,10 @@ CBlockTemplate* CreateNewBlock(std::vector<unsigned char> &salt)
// This should never happen; all transactions in the memory are new
if( pblocktree->HaveTxIndex(tx.GetUsernameHash()) ) {
printf("ERROR: mempool transaction already exists\n");
if (fDebug) assert("mempool transaction already exists" == 0);
if( !verifyDuplicateOrReplacementTx(tx, false, true) ) {
printf("ERROR: mempool transaction already exists\n");
if (fDebug) assert("mempool transaction already exists" == 0);
}
}
// Size limits

2
src/main.h

@ -183,6 +183,8 @@ bool IsInitialBlockDownload(); @@ -183,6 +183,8 @@ bool IsInitialBlockDownload();
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);
/** Verify duplicate or replacement transactions */
bool verifyDuplicateOrReplacementTx(CTransaction &tx, bool checkDuplicate, bool checkReplacement);
/** 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 */

Loading…
Cancel
Save