Skip to content

Commit

Permalink
Write to scriptPubkeyByPath index table after addressByScriptPubkey
Browse files Browse the repository at this point in the history
If scriptPubkeyByPath is written to before addressByScriptPubkey, there
is a chance that the write to addressByScriptPubkey fails or is
interrupted, causing the processor data to be incorrect (an index on
data that was never written). This appears to the user as an infamous
"Missing processor address...during initialization" error message.

We hopefully fix the issue and error for good with this change. The
change includes moving the write to scriptPubkeyByPath at the end of
the `saveAddress` processor routine. We do this with a update variable
which we declare at the start of the routine and define it only when
the update to the scriptPubkeyByPath table is necessary.

In addition to this, we reworked some of the control-flow of this
routine and modified the conditions in the control-flow for better
readability. It's important this code is maintainable and easy to
reason about.
  • Loading branch information
samholmes committed Apr 22, 2024
1 parent de40ae7 commit 83c1412
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- fixed: Missing `scriptPubkeyByPath` processor data from wallet data dump returned by `dumpData`
- fixed: Prevent wallet processor data corruption ("Missing processor address" error)

## 2.6.0 (2024-04-09)

Expand Down
71 changes: 50 additions & 21 deletions src/common/utxobased/db/Processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,15 @@ export async function makeProcessor(

async saveAddress(address: IAddress): Promise<void> {
await baselets.address(async tables => {
// This variable is used to update the scriptPubkeyByPath table.
// The path table must be written after the address table because the
// path table is an index of the address table.
// This variable acts as a catch for that update to be done after the
// address table is written.
let indexTableUpdate:
| { path: AddressPath; scriptPubkey: string }
| undefined

const [existingAddress] = await tables.addressByScriptPubkey.query('', [
address.scriptPubkey
])
Expand All @@ -420,14 +429,14 @@ export async function makeProcessor(
'Attempted to save address with an existing path, but different script pubkey'
)

await tables.scriptPubkeyByPath.insert(
addressPathToPrefix(address.path),
address.path.addressIndex,
address.scriptPubkey
)
indexTableUpdate = {
path: address.path,
scriptPubkey: address.scriptPubkey
}

// check if this address is used and if so, whether it has a higher last used index
if (address.used || (existingAddress?.used ?? false)) {
// check if this address is used and if so, whether it has a higher
// last used index
if (address.used || existingAddress?.used === true) {
let [lastUsed] = await tables.lastUsedByFormatPath.query('', [
addressPathToPrefix(address.path)
])
Expand All @@ -443,6 +452,7 @@ export async function makeProcessor(
}
}

// Update routine:
if (existingAddress != null) {
// Only update the lastQueriedBlockHeight on the address if one was given and is greater than the existing value
if (
Expand All @@ -468,16 +478,21 @@ export async function makeProcessor(
existingAddress.balance = address.balance
}

// Only update the path field if one was given and currently does not have one
// NOTE: Addresses can be stored in the db without a path due to the `EdgeCurrencyEngine.addGapLimitAddresses` function
// Once an address path is known, it should never be updated
/*
Only update the path field if one was given and the existing address
currently does not have one. We never update paths for addresses, only
insert paths when they're not present.
NOTE: Addresses can be stored in the db without a path due to the
`EdgeCurrencyEngine.addGapLimitAddresses` function. Once an address
path is known, it should never be updated
*/
if (address.path != null && existingAddress.path == null) {
existingAddress.path = address.path
await tables.scriptPubkeyByPath.insert(
addressPathToPrefix(address.path),
address.path.addressIndex,
address.scriptPubkey
)
indexTableUpdate = {
path: existingAddress.path,
scriptPubkey: existingAddress.scriptPubkey
}
}

// Only update the used flag if one was given and is true
Expand All @@ -500,18 +515,32 @@ export async function makeProcessor(
)
}
}

// Update the address table:
await tables.addressByScriptPubkey.insert(
'',
existingAddress.scriptPubkey,
existingAddress
)
return
}
await tables.addressByScriptPubkey.insert(
'',
address.scriptPubkey,
address
)

// Insert routine:
if (existingAddress == null) {
await tables.addressByScriptPubkey.insert(
'',
address.scriptPubkey,
address
)
}

// Insert the path into the index table:
if (indexTableUpdate != null) {
await tables.scriptPubkeyByPath.insert(
addressPathToPrefix(indexTableUpdate.path),
indexTableUpdate.path.addressIndex,
indexTableUpdate.scriptPubkey
)
}
})
},

Expand Down

0 comments on commit 83c1412

Please sign in to comment.