package com.ripple.core.types.shamap; import com.ripple.core.coretypes.hash.Hash256; import com.ripple.core.fields.Field; import com.ripple.core.types.known.sle.LedgerEntry; import com.ripple.core.types.known.sle.ThreadedLedgerEntry; import com.ripple.core.types.known.sle.entries.DirectoryNode; import com.ripple.core.types.known.sle.entries.Offer; import com.ripple.core.types.known.sle.entries.RippleState; import com.ripple.core.types.known.tx.result.AffectedNode; import com.ripple.core.types.known.tx.result.TransactionResult; import java.util.*; public class AccountStateBuilder { private AccountState state; private AccountState previousState = null; private long targetLedgerIndex; public long nextTransactionIndex = 0; private Hash256 targetAccountHash; public long totalTransactions = 0; private TreeSet<Hash256> directoriesModifiedMoreThanOnceByTransaction = new TreeSet<Hash256>(); private TreeSet<Hash256> directoriesModifiedByTransaction = new TreeSet<Hash256>(); public TreeSet<Hash256> modifiedEntries = new TreeSet<Hash256>(); public void resetModified() { modifiedEntries.clear(); directoriesModifiedMoreThanOnceByTransaction.clear(); } public AccountStateBuilder(AccountState state, long targetLedgerIndex) { this.state = state; setStateCheckPoint(); this.targetLedgerIndex = targetLedgerIndex; } public void onLedgerClose(long ledgerIndex, Hash256 accountHash, Hash256 parentHash) { state.updateSkipLists(ledgerIndex, parentHash); targetLedgerIndex = ledgerIndex; targetAccountHash = accountHash; nextTransactionIndex = 0; } public void setStateCheckPoint() { previousState = state.copy(); } public void onTransaction(TransactionResult tr) { if (tr.meta.transactionIndex().longValue() != nextTransactionIndex) throw new AssertionError(); if (tr.ledgerIndex.longValue() != targetLedgerIndex + 1) throw new AssertionError(String.format("%d != %d", tr.ledgerIndex.longValue(), targetLedgerIndex + 1)); nextTransactionIndex++; totalTransactions++; directoriesModifiedByTransaction = new TreeSet<Hash256>(); for (AffectedNode an : sortedAffectedNodes(tr)) { Hash256 id = an.ledgerIndex(); LedgerEntry le = (LedgerEntry) an.nodeAsFinal(); if (an.isCreatedNode()) { modifiedEntries.add(id); le.setDefaults(); state.addLE(le); if (le instanceof Offer) { Offer offer = (Offer) le; offer.setOfferDefaults(); // TODO / TODO for (Hash256 directory : offer.directoryIndexes()) { DirectoryNode dn = getDirectoryForUpdating(directory); Hash256 index = offer.index(); addToDirectoryNode(dn, index); } } else if (le instanceof RippleState) { RippleState state = (RippleState) le; for (Hash256 directory : state.directoryIndexes()) { DirectoryNode dn = getDirectoryForUpdating(directory); addToDirectoryNode(dn, state.index()); } } if (le instanceof ThreadedLedgerEntry) { ThreadedLedgerEntry tle = (ThreadedLedgerEntry) le; tle.previousTxnID(tr.hash); tle.previousTxnLgrSeq(tr.ledgerIndex); } } else if (an.isDeletedNode()) { modifiedEntries.remove(id); directoriesModifiedMoreThanOnceByTransaction.remove(id); state.removeLeaf(id); if (le instanceof Offer) { Offer offer = (Offer) le; for (Hash256 directory : offer.directoryIndexes()) { try { DirectoryNode dn = getDirectoryForUpdating(directory); if (dn != null) { // Hash256 index = offer.index(); if (dn.owner() != null) { deleteFromDirectoryUnstable(offer, dn); } else { deleteFromDirectoryStable(offer, dn); } } } catch (Exception e) { // } } } else if (le instanceof RippleState) { RippleState state = (RippleState) le; for (Hash256 directory : state.directoryIndexes()) { try { DirectoryNode dn = getDirectoryForUpdating(directory); if (dn != null) { deleteFromDirectoryUnstable(le, dn); } } catch (Exception e) { // } } } } else if (an.isModifiedNode()) { modifiedEntries.add(id); ShaMapLeaf leaf = state.getLeafForUpdating(id); LedgerEntryItem item = (LedgerEntryItem) leaf.item; LedgerEntry leModded = item.entry; if (le instanceof ThreadedLedgerEntry) { ThreadedLedgerEntry tle = (ThreadedLedgerEntry) le; tle.previousTxnID(tr.hash); tle.previousTxnLgrSeq(tr.ledgerIndex); } for (Field field : le) { if (field == Field.LedgerIndex) { continue; } leModded.put(field, le.get(field)); } } } } private void deleteFromDirectoryUnstable(LedgerEntry b4, DirectoryNode dn) { boolean b = directoryRemoveUnstable(dn, b4.index()); DirectoryNode cursor = dn; if (!b) { while (cursor.indexNext() != null) { cursor = getDirectoryForUpdating(cursor.nextIndex()); b = directoryRemoveUnstable(cursor, b4.index()); if (b) { break; } } } // if (!b) { cursor = dn; while (cursor.indexPrevious() != null) { cursor = getDirectoryForUpdating(cursor.prevIndex()); b = directoryRemoveUnstable(cursor, b4.index()); if (b) { break; } } } } private void deleteFromDirectoryStable(LedgerEntry b4, DirectoryNode dn) { boolean b = directoryRemoveStable(dn, b4.index()); DirectoryNode cursor = dn; if (!b) { cursor = dn; while (cursor.indexPrevious() != null) { cursor = getDirectoryForUpdating(cursor.prevIndex()); b = directoryRemoveStable(cursor, b4.index()); if (b) { break; } } } if (!b) { while (cursor.indexNext() != null) { cursor = getDirectoryForUpdating(cursor.nextIndex()); b = directoryRemoveStable(cursor, b4.index()); if (b) { break; } } } } public static <E> Collection<E> makeCollection(Iterable<E> iter) { Collection<E> list = new ArrayList<E>(); for (E item : iter) { list.add(item); } return list; } private ArrayList<AffectedNode> sortedAffectedNodes(TransactionResult tr) { ArrayList<AffectedNode> sorted = new ArrayList<AffectedNode>(makeCollection(tr.meta.affectedNodes())); Collections.sort(sorted, new Comparator<AffectedNode>() { @Override public int compare(AffectedNode o1, AffectedNode o2) { return ord(o1) - ord(o2); } private int ord(AffectedNode o1) { switch (o1.ledgerEntryType()) { case DirectoryNode: return 1; case RippleState: return 2; case Offer: return 3; default: return 4; } } }); return sorted; } private void onDirectoryModified(DirectoryNode dn) { Hash256 index = dn.index(); if (directoriesModifiedByTransaction.contains(index)) { directoriesModifiedMoreThanOnceByTransaction.add(index); } else { directoriesModifiedByTransaction.add(index); } } private boolean directoryRemoveStable(DirectoryNode dn, Hash256 index) { onDirectoryModified(dn); return dn.indexes().remove(index); } private boolean directoryRemoveUnstable(DirectoryNode dn, Hash256 index) { onDirectoryModified(dn); return dn.indexes().removeUnstable(index); } private void addToDirectoryNode(DirectoryNode dn, Hash256 index) { onDirectoryModified(dn); dn.indexes().add(index); } private DirectoryNode getDirectoryForUpdating(Hash256 directoryIndex) { ShaMapLeaf leaf = state.getLeafForUpdating(directoryIndex); if (leaf == null) { return null; } LedgerEntryItem lei = (LedgerEntryItem) leaf.item; return (DirectoryNode) lei.entry; } public AccountState state() { return state; } public long currentLedgerIndex() { return targetLedgerIndex; } public String targetAccountHashHex() { return targetAccountHash.toHex(); } public Hash256 targetAccountHash() { return targetAccountHash; } public TreeSet<Hash256> directoriesWithIndexesOutOfOrder() { TreeSet<Hash256> ret = new TreeSet<Hash256>(); for (Hash256 hash256 : directoriesModifiedMoreThanOnceByTransaction) { DirectoryNode dn = state.getDirectoryNode(hash256); if (dn.owner() != null) { ret.add(hash256); } } return ret; } public boolean bad() { return !state.hash().equals(targetAccountHash); } public AccountState previousState() { return previousState; } public void setState(AccountState map) { state = map; } }