Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i1722-avoid-wallet-rescan-after-init #1723

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class ErgoWalletActor(settings: ErgoSettings,
}
}

private def emptyWallet: Receive = {
private def expectWalletReading: Receive = {
case ReadWallet(state) =>
val ws = settings.walletSettings
// Try to read wallet from json file or test mnemonic provided in a config file
Expand All @@ -97,7 +97,7 @@ class ErgoWalletActor(settings: ErgoSettings,
ergoWalletService.initWallet(state, settings, pass, mnemonicPassOpt) match {
case Success((mnemonic, newState)) =>
log.info("Wallet is initialized")
context.become(loadedWallet(newState))
context.become(loadedWallet(newState.copy(walletState = WalletPhase.Created)))
self ! UnlockWallet(pass)
sender() ! Success(mnemonic)
case Failure(t) =>
Expand All @@ -111,7 +111,7 @@ class ErgoWalletActor(settings: ErgoSettings,
ergoWalletService.restoreWallet(state, settings, mnemonic, mnemonicPassOpt, walletPass) match {
case Success(newState) =>
log.info("Wallet is restored")
context.become(loadedWallet(newState))
context.become(loadedWallet(newState.copy(walletState = WalletPhase.Restored)))
self ! UnlockWallet(walletPass)
sender() ! Success(())
case Failure(t) =>
Expand Down Expand Up @@ -265,7 +265,8 @@ class ErgoWalletActor(settings: ErgoSettings,
case ScanOnChain(newBlock) =>
if (state.secretIsSet(settings.walletSettings.testMnemonic)) { // scan blocks only if wallet is initialized
val nextBlockHeight = state.expectedNextBlockHeight(newBlock.height, settings.nodeSettings.isFullBlocksPruned)
if (nextBlockHeight == newBlock.height) {
// we want to scan a block either when it is its turn or when wallet is freshly created (no need to load the past)
if (nextBlockHeight == newBlock.height || (state.walletState == WalletPhase.Created && state.getWalletHeight == 0)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kushti I've been through this PR one more time, I once found this solution after many thoughts. We basically want the first block applied to chain to be the first block added to freshly created wallet (out of order in this special case). We cannot manually change the digest of the wallet as that can be done only by applying modifiers to it. So this is the best place to do that.

log.info(s"Wallet is going to scan a block ${newBlock.id} on chain at height ${newBlock.height}")
val newState =
ergoWalletService.scanBlockUpdate(state, newBlock, settings.walletSettings.dustLimit) match {
Expand All @@ -279,7 +280,7 @@ class ErgoWalletActor(settings: ErgoSettings,
context.become(loadedWallet(newState))
} else if (nextBlockHeight < newBlock.height) {
log.warn(s"Wallet: skipped blocks found starting from $nextBlockHeight, going back to scan them")
self ! ScanInThePast(nextBlockHeight, false)
self ! ScanInThePast(nextBlockHeight, rescan = false)
} else {
log.warn(s"Wallet: block in the past reported at ${newBlock.height}, blockId: ${newBlock.id}")
}
Expand Down Expand Up @@ -488,7 +489,7 @@ class ErgoWalletActor(settings: ErgoSettings,
sender() ! txsToSend
}

override def receive: Receive = emptyWallet
override def receive: Receive = expectWalletReading

private def wrapLegalExc[T](e: Throwable): Failure[T] =
if (e.getMessage.startsWith("Illegal key size")) {
Expand Down Expand Up @@ -520,6 +521,17 @@ object ErgoWalletActor extends ScorexLogging {
walletActorRef
}

/** Wallet transitions either from Default -> Initialized or Default -> Restored */
trait WalletPhase
object WalletPhase {
/** Wallet is expecting either Creation or Restoration */
case object UnInitialized extends WalletPhase
/** New wallet initialized with generated mnemonic in this runtime */
case object Created extends WalletPhase
/** Wallet restored from existing mnemonic in this runtime */
case object Restored extends WalletPhase
}

// Private signals the wallet actor sends to itself
/**
* A signal the wallet actor sends to itself to scan a block in the past
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.ergoplatform._
import org.ergoplatform.nodeView.history.ErgoHistory.Height
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.nodeView.state.{ErgoStateContext, ErgoStateReader, UtxoStateReader}
import org.ergoplatform.nodeView.wallet.ErgoWalletActor.WalletPhase
import org.ergoplatform.nodeView.wallet.ErgoWalletState.FilterFn
import org.ergoplatform.nodeView.wallet.persistence.{OffChainRegistry, WalletRegistry, WalletStorage}
import org.ergoplatform.settings.{ErgoSettings, Parameters}
Expand All @@ -27,6 +28,7 @@ case class ErgoWalletState(
utxoStateReaderOpt: Option[UtxoStateReader],
parameters: Parameters,
maxInputsToUse: Int,
walletState: WalletPhase,
error: Option[String] = None,
rescanInProgress: Boolean
) extends ScorexLogging {
Expand Down Expand Up @@ -90,7 +92,11 @@ case class ErgoWalletState(
}
}

// Read a box from UTXO set if the node has it, otherwise, from the wallet
/**
* Read a box from UTXO set if the node has it, otherwise, from the wallet
* @param boxId of the box to read
* @return maybe ErgoBox
*/
def readBoxFromUtxoWithWalletFallback(boxId: BoxId): Option[ErgoBox] = {
utxoStateReaderOpt match {
case Some(utxoReader) =>
Expand All @@ -100,7 +106,10 @@ case class ErgoWalletState(
}
}

// expected height of a next block when the wallet is receiving a new block with the height blockHeight
/** Get expected height of a next block when the wallet is receiving a new block with the height blockHeight
* @param blockHeight height of the block being currently received
* @param isFullBlocksPruned whether node has all the full blocks and applies them sequentially
*/
def expectedNextBlockHeight(blockHeight: Height, isFullBlocksPruned: Boolean): Height = {
val walletHeight = getWalletHeight
if (!isFullBlocksPruned) {
Expand Down Expand Up @@ -153,6 +162,7 @@ object ErgoWalletState {
utxoStateReaderOpt = None,
parameters,
maxInputsToUse,
walletState = WalletPhase.UnInitialized,
rescanInProgress = false
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.ergoplatform._
import org.ergoplatform.db.DBSpec
import org.ergoplatform.modifiers.mempool.{ErgoTransaction, UnconfirmedTransaction}
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.nodeView.wallet.ErgoWalletActor.WalletPhase
import org.ergoplatform.nodeView.wallet.WalletScanLogic.ScanResults
import org.ergoplatform.nodeView.wallet.persistence.{OffChainRegistry, WalletRegistry, WalletStorage}
import org.ergoplatform.nodeView.wallet.requests.{AssetIssueRequest, PaymentRequest}
Expand All @@ -20,6 +21,7 @@ import org.ergoplatform.wallet.boxes.{ErgoBoxSerializer, ReplaceCompactCollectBo
import org.ergoplatform.wallet.crypto.ErgoSignature
import org.ergoplatform.wallet.mnemonic.Mnemonic
import org.ergoplatform.wallet.secrets.ExtendedSecretKey
import org.ergoplatform.wallet.utils.FileUtils
import org.scalacheck.Gen
import org.scalatest.BeforeAndAfterAll
import scorex.db.{LDBKVStore, LDBVersionedStore}
Expand All @@ -37,6 +39,7 @@ class ErgoWalletServiceSpec
with ErgoWalletSupport
with ErgoTransactionGenerators
with DBSpec
with FileUtils
with BeforeAndAfterAll {

override val ergoSettings: ErgoSettings = settings
Expand All @@ -61,6 +64,7 @@ class ErgoWalletServiceSpec
utxoStateReaderOpt = Option.empty,
parameters,
maxInputsToUse = 1000,
walletState = WalletPhase.UnInitialized,
rescanInProgress = false
)
}
Expand Down Expand Up @@ -292,6 +296,8 @@ class ErgoWalletServiceSpec
property("it should lock/unlock wallet") {
withVersionedStore(2) { versionedStore =>
withStore { store =>
deleteRecursive(WalletStorage.storageFolder(settings))
deleteRecursive(WalletRegistry.registryFolder(settings))
val walletState = initialState(store, versionedStore)
val walletService = new ErgoWalletServiceImpl(settings)
val pass = Random.nextString(10)
Expand Down