Build Service / BuildPackage (push) Canceled after 0s
Group Actions and Config by category (Space-CLI, Bitcoin, Spaced, Subspaces, Certrelay, Nacho) and build x86 + arm only. Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
4.0 KiB
TypeScript
149 lines
4.0 KiB
TypeScript
import { storeJson } from '../fileModels/storeJson'
|
|
import { actionGroups } from '../actionGroups'
|
|
import { i18n } from '../i18n'
|
|
import { sdk } from '../sdk'
|
|
import { dataDir, SPACED_CHAIN } from '../utils'
|
|
|
|
const { InputSpec, Value } = sdk
|
|
|
|
const BACKUP_DIR = `${dataDir}/${SPACED_CHAIN}/wallets_backup`
|
|
const BACKUP_PATH = `${BACKUP_DIR}/default.json`
|
|
const WALLET_DIR = `${dataDir}/${SPACED_CHAIN}/wallets/default`
|
|
|
|
const inputSpec = InputSpec.of({
|
|
walletJson: Value.textarea({
|
|
name: i18n('Wallet JSON'),
|
|
description: i18n('Paste the JSON contents of a default.json wallet export.'),
|
|
warning: null,
|
|
footnote: null,
|
|
default: null,
|
|
required: true,
|
|
minLength: 1,
|
|
maxLength: null,
|
|
placeholder: '{...}',
|
|
}),
|
|
})
|
|
|
|
export const importWallet = sdk.Action.withInput(
|
|
// id
|
|
'import-wallet',
|
|
|
|
// metadata
|
|
async ({ effects }) => ({
|
|
name: i18n('Import Wallet'),
|
|
description: i18n(
|
|
'Restore a previously-exported default.json wallet into spaced.',
|
|
),
|
|
warning: i18n(
|
|
'Existing /data/mainnet/wallets_backup/default.json and /data/mainnet/wallets/default will be renamed with .bakNNN suffixes before the import. The active spaced daemon will load the imported wallet on success.',
|
|
),
|
|
allowedStatuses: 'only-running',
|
|
group: actionGroups.spaced,
|
|
visibility: 'enabled',
|
|
}),
|
|
|
|
// input
|
|
inputSpec,
|
|
|
|
// prefill
|
|
async ({ effects }) => {},
|
|
|
|
// run
|
|
async ({ effects, input }) => {
|
|
// Validate JSON before touching disk.
|
|
try {
|
|
JSON.parse(input.walletJson)
|
|
} catch (e) {
|
|
return {
|
|
version: '1',
|
|
title: i18n('Failure'),
|
|
message: i18n('Could not parse wallet JSON: ${error}', {
|
|
error: (e as Error).message,
|
|
}),
|
|
result: null,
|
|
}
|
|
}
|
|
|
|
const spacedAuth = await storeJson.read((s) => s.spacedAuth).once()
|
|
if (!spacedAuth) {
|
|
return {
|
|
version: '1',
|
|
title: i18n('Failure'),
|
|
message: i18n('Could not import wallet: ${error}', {
|
|
error: 'spacedAuth missing from store.json',
|
|
}),
|
|
result: null,
|
|
}
|
|
}
|
|
|
|
// base64-encode so shell quoting can never break on user content.
|
|
const b64 = Buffer.from(input.walletJson, 'utf8').toString('base64')
|
|
|
|
const cliAuth = `--rpc-user '${spacedAuth.username}' --rpc-password '${spacedAuth.password}'`
|
|
|
|
const script = `set -e
|
|
mkdir -p '${BACKUP_DIR}'
|
|
|
|
# Rotate /data/mainnet/wallets_backup/default.json -> .bakNNN if exists.
|
|
BACKUP='${BACKUP_PATH}'
|
|
if [ -e "$BACKUP" ]; then
|
|
i=0
|
|
while [ -e "$(printf '%s.bak%03d' "$BACKUP" "$i")" ]; do
|
|
i=$((i+1))
|
|
done
|
|
mv "$BACKUP" "$(printf '%s.bak%03d' "$BACKUP" "$i")"
|
|
fi
|
|
|
|
# Write fresh default.json from base64 payload.
|
|
printf '%s' '${b64}' | base64 -d > "$BACKUP"
|
|
|
|
# Rotate /data/mainnet/wallets/default folder -> .bakNNN if exists.
|
|
WDIR='${WALLET_DIR}'
|
|
if [ -d "$WDIR" ]; then
|
|
j=0
|
|
while [ -d "$(printf '%s.bak%03d' "$WDIR" "$j")" ]; do
|
|
j=$((j+1))
|
|
done
|
|
mv "$WDIR" "$(printf '%s.bak%03d' "$WDIR" "$j")"
|
|
fi
|
|
|
|
# Import + load via spaced RPC.
|
|
/root/.cargo/bin/space-cli --chain ${SPACED_CHAIN} ${cliAuth} importwallet "$BACKUP"
|
|
/root/.cargo/bin/space-cli --chain ${SPACED_CHAIN} ${cliAuth} loadwallet
|
|
`
|
|
|
|
const res = await sdk.SubContainer.withTemp(
|
|
effects,
|
|
{ imageId: 'spaces' },
|
|
sdk.Mounts.of().mountVolume({
|
|
volumeId: 'main',
|
|
subpath: null,
|
|
mountpoint: dataDir,
|
|
readonly: false,
|
|
}),
|
|
'spaces-import-wallet',
|
|
(subc) => subc.exec(['bash', '-c', script], { user: 'root' }),
|
|
)
|
|
|
|
if (res.exitCode !== 0) {
|
|
const stderr = (res.stderr ?? '').toString()
|
|
const stdout = (res.stdout ?? '').toString()
|
|
return {
|
|
version: '1',
|
|
title: i18n('Failure'),
|
|
message: i18n('Could not import wallet: ${error}', {
|
|
error: stderr || stdout || `exit ${res.exitCode}`,
|
|
}),
|
|
result: null,
|
|
}
|
|
}
|
|
|
|
return {
|
|
version: '1',
|
|
title: i18n('Success'),
|
|
message: i18n('Wallet imported and loaded.'),
|
|
result: null,
|
|
}
|
|
},
|
|
)
|