Skip to content

Commit

Permalink
Add migration for Account.scanningEnabled
Browse files Browse the repository at this point in the history
  • Loading branch information
NullSoldier committed May 22, 2024
1 parent 2807e92 commit 695ac63
Show file tree
Hide file tree
Showing 10 changed files with 487 additions and 0 deletions.
49 changes: 49 additions & 0 deletions ironfish/src/migrations/data/032-add-account-scanning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { Logger } from '../../logger'
import { IDatabase, IDatabaseTransaction } from '../../storage'
import { createDB } from '../../storage/utils'
import { Database, Migration, MigrationContext } from '../migration'
import { GetStores } from './032-add-account-syncing/stores'

export class Migration032 extends Migration {
path = __filename
database = Database.WALLET

prepare(context: MigrationContext): IDatabase {
return createDB({ location: context.config.walletDatabasePath })
}

async forward(
_context: MigrationContext,
db: IDatabase,
tx: IDatabaseTransaction | undefined,
logger: Logger,
): Promise<void> {
const stores = GetStores(db)

for await (const accountValue of stores.old.accounts.getAllValuesIter(tx)) {
logger.debug(` Migrating account ${accountValue.name}`)

const migrated = {
...accountValue,
scanningEnabled: true,
}

await stores.new.accounts.put(accountValue.id, migrated, tx)
}
}

async backward(
_context: MigrationContext,
db: IDatabase,
tx: IDatabaseTransaction | undefined,
): Promise<void> {
const stores = GetStores(db)

for await (const accountValue of stores.new.accounts.getAllValuesIter(tx)) {
await stores.old.accounts.put(accountValue.id, accountValue, tx)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { PUBLIC_ADDRESS_LENGTH } from '@ironfish/rust-nodejs'
import bufio from 'bufio'
import { IDatabaseEncoding } from '../../../../storage'
import { HeadValue, NullableHeadValueEncoding } from './HeadValue'

const KEY_LENGTH = 32
export const VIEW_KEY_LENGTH = 64
const VERSION_LENGTH = 2

export interface AccountValue {
version: number
id: string
name: string
spendingKey: string | null
viewKey: string
incomingViewKey: string
outgoingViewKey: string
publicAddress: string
createdAt: HeadValue | null
scanningEnabled?: boolean
multisigKeys?: {
secret: string
keyPackage: string
}
proofAuthorizingKey: string | null
}

export class AccountValueEncoding implements IDatabaseEncoding<AccountValue> {
serialize(value: AccountValue): Buffer {
const bw = bufio.write(this.getSize(value))
let flags = 0
flags |= Number(!!value.spendingKey) << 0
flags |= Number(!!value.createdAt) << 1
flags |= Number(!!value.multisigKeys) << 2
flags |= Number(!!value.proofAuthorizingKey) << 3
flags |= Number(!!value.scanningEnabled) << 4
bw.writeU8(flags)
bw.writeU16(value.version)
bw.writeVarString(value.id, 'utf8')
bw.writeVarString(value.name, 'utf8')

if (value.spendingKey) {
bw.writeBytes(Buffer.from(value.spendingKey, 'hex'))
}

bw.writeBytes(Buffer.from(value.viewKey, 'hex'))
bw.writeBytes(Buffer.from(value.incomingViewKey, 'hex'))
bw.writeBytes(Buffer.from(value.outgoingViewKey, 'hex'))
bw.writeBytes(Buffer.from(value.publicAddress, 'hex'))

if (value.createdAt) {
const encoding = new NullableHeadValueEncoding()
bw.writeBytes(encoding.serialize(value.createdAt))
}

if (value.multisigKeys) {
bw.writeVarBytes(Buffer.from(value.multisigKeys.secret, 'hex'))
bw.writeVarBytes(Buffer.from(value.multisigKeys.keyPackage, 'hex'))
}

if (value.proofAuthorizingKey) {
bw.writeBytes(Buffer.from(value.proofAuthorizingKey, 'hex'))
}

return bw.render()
}

deserialize(buffer: Buffer): AccountValue {
const reader = bufio.read(buffer, true)
const flags = reader.readU8()
const version = reader.readU16()
const hasSpendingKey = flags & (1 << 0)
const hasCreatedAt = flags & (1 << 1)
const hasMultisigKeys = flags & (1 << 2)
const hasProofAuthorizingKey = flags & (1 << 3)
const scanningEnabled = Boolean(flags & (1 << 4))
const id = reader.readVarString('utf8')
const name = reader.readVarString('utf8')
const spendingKey = hasSpendingKey ? reader.readBytes(KEY_LENGTH).toString('hex') : null
const viewKey = reader.readBytes(VIEW_KEY_LENGTH).toString('hex')
const incomingViewKey = reader.readBytes(KEY_LENGTH).toString('hex')
const outgoingViewKey = reader.readBytes(KEY_LENGTH).toString('hex')
const publicAddress = reader.readBytes(PUBLIC_ADDRESS_LENGTH).toString('hex')

let createdAt = null
if (hasCreatedAt) {
const encoding = new NullableHeadValueEncoding()
createdAt = encoding.deserialize(reader.readBytes(encoding.nonNullSize))
}

let multisigKeys = undefined
if (hasMultisigKeys) {
multisigKeys = {
secret: reader.readVarBytes().toString('hex'),
keyPackage: reader.readVarBytes().toString('hex'),
}
}

const proofAuthorizingKey = hasProofAuthorizingKey
? reader.readBytes(KEY_LENGTH).toString('hex')
: null

return {
version,
id,
name,
viewKey,
incomingViewKey,
outgoingViewKey,
spendingKey,
publicAddress,
createdAt,
scanningEnabled,
multisigKeys,
proofAuthorizingKey,
}
}

getSize(value: AccountValue): number {
let size = 0
size += 1 // flags
size += VERSION_LENGTH
size += bufio.sizeVarString(value.id, 'utf8')
size += bufio.sizeVarString(value.name, 'utf8')
if (value.spendingKey) {
size += KEY_LENGTH
}
size += VIEW_KEY_LENGTH
size += KEY_LENGTH
size += KEY_LENGTH
size += PUBLIC_ADDRESS_LENGTH

if (value.createdAt) {
const encoding = new NullableHeadValueEncoding()
size += encoding.nonNullSize
}

if (value.multisigKeys) {
size += bufio.sizeVarString(value.multisigKeys.secret, 'hex')
size += bufio.sizeVarString(value.multisigKeys.keyPackage, 'hex')
}
if (value.proofAuthorizingKey) {
size += KEY_LENGTH
}

return size
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import bufio from 'bufio'
import { IDatabaseEncoding } from '../../../../storage'

export type HeadValue = {
hash: Buffer
sequence: number
}

export class NullableHeadValueEncoding implements IDatabaseEncoding<HeadValue | null> {
readonly nonNullSize = 32 + 4 // 256-bit block hash + 32-bit integer

serialize(value: HeadValue | null): Buffer {
const bw = bufio.write(this.getSize(value))

if (value) {
bw.writeHash(value.hash)
bw.writeU32(value.sequence)
}

return bw.render()
}

deserialize(buffer: Buffer): HeadValue | null {
const reader = bufio.read(buffer, true)

if (reader.left()) {
const hash = reader.readHash()
const sequence = reader.readU32()
return { hash, sequence }
}

return null
}

getSize(value: HeadValue | null): number {
return value ? this.nonNullSize : 0
}
}
20 changes: 20 additions & 0 deletions ironfish/src/migrations/data/032-add-account-syncing/new/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { IDatabase, IDatabaseStore, StringEncoding } from '../../../../storage'
import { AccountValue, AccountValueEncoding } from './AccountValue'

export function GetNewStores(db: IDatabase): {
accounts: IDatabaseStore<{ key: string; value: AccountValue }>
} {
const accounts: IDatabaseStore<{ key: string; value: AccountValue }> = db.addStore(
{
name: 'a',
keyEncoding: new StringEncoding(),
valueEncoding: new AccountValueEncoding(),
},
false,
)

return { accounts }
}
Loading

0 comments on commit 695ac63

Please sign in to comment.