Logic to 'rebase' a ledger to a different previous ledger.

This code does not assume the new previous ledger is valid
or trusted.
1) Validate sequence numbers and basic information.
2) Create a new ledger based on the new previous ledger.
3) Compare the old previous ledger and the new previous
ledger.
4) Try to include any missing, valid transactions in
the current ledger.
5) Traverse the old ledger based on the old previous ledger,
try to import any transactions into the new ledger.
This commit is contained in:
JoelKatz
2012-01-24 21:30:27 -08:00
parent b5afbf52ec
commit f4e6a72e1c
5 changed files with 77 additions and 12 deletions

View File

@@ -433,3 +433,73 @@ void Ledger::addJson(Json::Value& ret)
else ledger["Closed"]=false; else ledger["Closed"]=false;
ret[boost::lexical_cast<std::string>(mLedgerSeq)]=ledger; ret[boost::lexical_cast<std::string>(mLedgerSeq)]=ledger;
} }
Ledger::pointer Ledger::switchPreviousLedger(Ledger::pointer oldPrevious, Ledger::pointer newPrevious, int limit)
{
// Build a new ledger that can replace this ledger as the active ledger,
// with a different previous ledger. We assume our ledger is trusted, as is its
// previous ledger. We make no assumptions about the new previous ledger.
int count;
// 1) Validate sequences and make sure the specified ledger is a valid prior ledger
if(newPrevious->getLedgerSeq()!=oldPrevious->getLedgerSeq()) return Ledger::pointer();
// 2) Begin building a new ledger with the specified ledger as previous.
Ledger* newLedger=new Ledger(*newPrevious, mTimeStamp);
// 3) For any transactions in our previous ledger but not in the new previous ledger, add them to the set
SHAMap::SHAMapDiff mapDifferences;
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> > TxnDiff;
if(!newPrevious->mTransactionMap->compare(oldPrevious->mTransactionMap, mapDifferences, limit))
return Ledger::pointer();
if(!Transaction::convertToTransactions(oldPrevious->getLedgerSeq(), newPrevious->getLedgerSeq(),
false, true, mapDifferences, TxnDiff))
return Ledger::pointer(); // new previous ledger contains invalid transactions
// 4) Try to add those transactions to the new ledger.
do
{
count=0;
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >::iterator it=TxnDiff.begin();
while(it!=TxnDiff.end())
{
Transaction::pointer& tx=it->second.second;
if(!tx || newLedger->addTransaction(tx))
{
count++;
TxnDiff.erase(++it);
}
else ++it;
}
} while(count!=0);
// WRITEME: Handle rejected transactions left in TxnDiff
// 5) Try to add transactions from this ledger to the new ledger.
// OPTIMIZME: This should use the transaction canonicalizer, rather than creating transactions
std::map<uint256, Transaction::pointer> txnMap;
for(SHAMapItem::pointer mit=peekTransactionMap()->peekFirstItem();
!!mit;
mit=peekTransactionMap()->peekNextItem(mit->getTag()))
txnMap.insert(std::make_pair(mit->getTag(), Transaction::pointer(new Transaction(mit->peekData(), false))));
do
{
count=0;
std::map<uint256, Transaction::pointer>::iterator it=txnMap.begin();
while(it!=txnMap.end())
{
if(newLedger->addTransaction(it->second))
{
count++;
txnMap.erase(++it);
}
else ++it;
}
} while(count!=0);
// WRITEME: Handle rejected transactions left in txnMap
return Ledger::pointer(newLedger);
}

View File

@@ -96,6 +96,7 @@ public:
TransResult applyTransaction(Transaction::pointer trans); TransResult applyTransaction(Transaction::pointer trans);
TransResult removeTransaction(Transaction::pointer trans); TransResult removeTransaction(Transaction::pointer trans);
TransResult hasTransaction(Transaction::pointer trans); TransResult hasTransaction(Transaction::pointer trans);
Ledger::pointer switchPreviousLedger(Ledger::pointer oldPrevious, Ledger::pointer newPrevious, int limit);
// database functions // database functions
static void saveAcceptedLedger(Ledger::pointer); static void saveAcceptedLedger(Ledger::pointer);

View File

@@ -389,9 +389,6 @@ SHAMapLeafNode::pointer SHAMap::createLeaf(const SHAMapInnerNode& lowestParent,
for(int depth=lowestParent.getDepth()+1; depth<SHAMapNode::leafDepth; depth++) for(int depth=lowestParent.getDepth()+1; depth<SHAMapNode::leafDepth; depth++)
{ {
SHAMapInnerNode::pointer newNode(new SHAMapInnerNode(SHAMapNode(depth, id), mSeq)); SHAMapInnerNode::pointer newNode(new SHAMapInnerNode(SHAMapNode(depth, id), mSeq));
#ifdef DEBUG
std::cerr << "create node " << newNode->getString() << std::endl;
#endif
mInnerNodeByID[*newNode]=newNode; mInnerNodeByID[*newNode]=newNode;
} }
SHAMapLeafNode::pointer newLeaf(new SHAMapLeafNode(SHAMapNode(SHAMapNode::leafDepth, id), mSeq)); SHAMapLeafNode::pointer newLeaf(new SHAMapLeafNode(SHAMapNode(SHAMapNode::leafDepth, id), mSeq));

View File

@@ -274,12 +274,10 @@ Transaction::pointer Transaction::findFrom(const uint160& fromID, uint32 seq)
} }
bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq, bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq,
bool checkFirstTransactions, bool checkSecondTransactions, bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap,
const std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> >& inMap,
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap) std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap)
{ // convert a straight SHAMap payload difference to a transaction difference table { // convert a straight SHAMap payload difference to a transaction difference table
// return value: true=ledgers are valid, false=a ledger is invalid // return value: true=ledgers are valid, false=a ledger is invalid
bool ret=true;
std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> >::const_iterator it; std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> >::const_iterator it;
for(it=inMap.begin(); it!=inMap.end(); ++it) for(it=inMap.begin(); it!=inMap.end(); ++it)
{ {
@@ -294,7 +292,7 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg
if( (firstTrans->getStatus()==INVALID) || (firstTrans->getID()!=id) ) if( (firstTrans->getStatus()==INVALID) || (firstTrans->getID()!=id) )
{ {
firstTrans->setStatus(INVALID, firstLedgerSeq); firstTrans->setStatus(INVALID, firstLedgerSeq);
ret=false; return false;
} }
else firstTrans->setStatus(INCLUDED, firstLedgerSeq); else firstTrans->setStatus(INCLUDED, firstLedgerSeq);
} }
@@ -305,17 +303,17 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg
if( (secondTrans->getStatus()==INVALID) || (secondTrans->getID()!=id) ) if( (secondTrans->getStatus()==INVALID) || (secondTrans->getID()!=id) )
{ {
secondTrans->setStatus(INVALID, secondLedgerSeq); secondTrans->setStatus(INVALID, secondLedgerSeq);
ret=false; return false;
} }
else secondTrans->setStatus(INCLUDED, secondLedgerSeq); else secondTrans->setStatus(INCLUDED, secondLedgerSeq);
} }
assert(firstTrans || secondTrans); assert(firstTrans || secondTrans);
if(firstTrans && secondTrans && (firstTrans->getStatus()!=INVALID) && (secondTrans->getStatus()!=INVALID)) if(firstTrans && secondTrans && (firstTrans->getStatus()!=INVALID) && (secondTrans->getStatus()!=INVALID))
ret=false; // one or the other SHAMap is structurally invalid or a miracle has happened return false; // one or the other SHAMap is structurally invalid or a miracle has happened
outMap[id]=std::pair<Transaction::pointer, Transaction::pointer>(firstTrans, secondTrans); outMap[id]=std::pair<Transaction::pointer, Transaction::pointer>(firstTrans, secondTrans);
} }
return ret; return true;
} }
static bool isHex(char j) static bool isHex(char j)

View File

@@ -88,8 +88,7 @@ public:
// conversion function // conversion function
static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq, static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq,
bool checkFirstTransactions, bool checkSecondTransactions, bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap,
const std::map<uint256, std::pair<SHAMapItem::pointer,SHAMapItem::pointer> >& inMap,
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap); std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap);
bool operator<(const Transaction&) const; bool operator<(const Transaction&) const;