feat: add accounts using the HWW
This commit is contained in:
parent
73265a9951
commit
2ac8ee95b0
4 changed files with 109 additions and 24 deletions
|
|
@ -29,8 +29,9 @@ async function serialSigner(path) {
|
||||||
showSignedPsbt: false,
|
showSignedPsbt: false,
|
||||||
sendingPsbt: false,
|
sendingPsbt: false,
|
||||||
signingPsbt: false,
|
signingPsbt: false,
|
||||||
psbtSentResolve: null,
|
|
||||||
loginResolve: null,
|
loginResolve: null,
|
||||||
|
psbtSentResolve: null,
|
||||||
|
xpubResolve: null,
|
||||||
confirm: {
|
confirm: {
|
||||||
outputIndex: 0,
|
outputIndex: 0,
|
||||||
showFee: false
|
showFee: false
|
||||||
|
|
@ -105,7 +106,6 @@ async function serialSigner(path) {
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.selectedPort = null
|
this.selectedPort = null
|
||||||
console.log('### error', error)
|
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: 'Cannot close serial port!',
|
message: 'Cannot close serial port!',
|
||||||
|
|
@ -137,6 +137,12 @@ async function serialSigner(path) {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isFetchingXpub: async function () {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.xpubResolve = resolve
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
checkSerialPortSupported: function () {
|
checkSerialPortSupported: function () {
|
||||||
if (!navigator.serial) {
|
if (!navigator.serial) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
|
|
@ -162,7 +168,6 @@ async function serialSigner(path) {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
const {value, done} = await readStringUntil('\n')
|
const {value, done} = await readStringUntil('\n')
|
||||||
console.log('### value', value)
|
|
||||||
if (value) {
|
if (value) {
|
||||||
this.handleSerialPortResponse(value)
|
this.handleSerialPortResponse(value)
|
||||||
this.updateSerialPortConsole(value)
|
this.updateSerialPortConsole(value)
|
||||||
|
|
@ -180,15 +185,31 @@ async function serialSigner(path) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleSerialPortResponse: function (value) {
|
handleSerialPortResponse: function (value) {
|
||||||
const msg = value.split(' ')
|
const command = value.split(' ')[0]
|
||||||
if (msg[0] == COMMAND_SIGN_PSBT) this.handleSignResponse(msg[1])
|
const commandData = value.substring(command.length).trim()
|
||||||
else if (msg[0] == COMMAND_PASSWORD) this.handleLoginResponse(msg[1])
|
|
||||||
else if (msg[0] == COMMAND_PASSWORD_CLEAR)
|
switch (command) {
|
||||||
this.handleLogoutResponse(msg[1])
|
case COMMAND_SIGN_PSBT:
|
||||||
else if (msg[0] == COMMAND_SEND_PSBT)
|
this.handleSignResponse(commandData)
|
||||||
this.handleSendPsbtResponse(msg[1])
|
break
|
||||||
else if (msg[0] == COMMAND_WIPE) this.handleWipeResponse(msg[1])
|
case COMMAND_PASSWORD:
|
||||||
else console.log('### console', value)
|
this.handleLoginResponse(commandData)
|
||||||
|
break
|
||||||
|
case COMMAND_PASSWORD_CLEAR:
|
||||||
|
this.handleLogoutResponse(commandData)
|
||||||
|
break
|
||||||
|
case COMMAND_SEND_PSBT:
|
||||||
|
this.handleSendPsbtResponse(commandData)
|
||||||
|
break
|
||||||
|
case COMMAND_WIPE:
|
||||||
|
this.handleWipeResponse(commandData)
|
||||||
|
break
|
||||||
|
case COMMAND_XPUB:
|
||||||
|
this.handleXpubResponse(commandData)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.log('### console', value)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateSerialPortConsole: function (value) {
|
updateSerialPortConsole: function (value) {
|
||||||
this.receivedData += value + '\n'
|
this.receivedData += value + '\n'
|
||||||
|
|
@ -304,7 +325,6 @@ async function serialSigner(path) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleLogoutResponse: function (res = '') {
|
handleLogoutResponse: function (res = '') {
|
||||||
console.log('###handleLogoutResponse ', res)
|
|
||||||
this.hww.authenticated = !(res.trim() === '1')
|
this.hww.authenticated = !(res.trim() === '1')
|
||||||
if (this.hww.authenticated) {
|
if (this.hww.authenticated) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
|
|
@ -316,7 +336,6 @@ async function serialSigner(path) {
|
||||||
},
|
},
|
||||||
hwwSendPsbt: async function (psbtBase64, tx) {
|
hwwSendPsbt: async function (psbtBase64, tx) {
|
||||||
try {
|
try {
|
||||||
console.log('### hwwSendPsbt tx', tx)
|
|
||||||
this.tx = tx
|
this.tx = tx
|
||||||
this.hww.sendingPsbt = true
|
this.hww.sendingPsbt = true
|
||||||
await this.writer.write(
|
await this.writer.write(
|
||||||
|
|
@ -427,7 +446,6 @@ async function serialSigner(path) {
|
||||||
},
|
},
|
||||||
handleWipeResponse: function (res = '') {
|
handleWipeResponse: function (res = '') {
|
||||||
const wiped = res.trim() === '1'
|
const wiped = res.trim() === '1'
|
||||||
console.log('### wiped', wiped)
|
|
||||||
if (wiped) {
|
if (wiped) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
|
@ -443,6 +461,36 @@ async function serialSigner(path) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hwwXpub: async function (path) {
|
||||||
|
try {
|
||||||
|
await this.writer.write(
|
||||||
|
COMMAND_XPUB + ' ' + this.network + ' ' + path + '\n'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to fetch XPub!',
|
||||||
|
caption: `${error}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleXpubResponse: function (res = '') {
|
||||||
|
const args = res.trim().split(' ')
|
||||||
|
if (args.length < 3 || args[0].trim() !== '1') {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Failed to fetch XPub!',
|
||||||
|
caption: `${res}`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
this.xpubResolve({})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const xpub = args[1].trim()
|
||||||
|
const fingerprint = args[2].trim()
|
||||||
|
this.xpubResolve({xpub, fingerprint})
|
||||||
|
},
|
||||||
hwwShowSeed: async function () {
|
hwwShowSeed: async function () {
|
||||||
try {
|
try {
|
||||||
await this.writer.write(COMMAND_SEED + '\n')
|
await this.writer.write(COMMAND_SEED + '\n')
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,7 @@
|
||||||
v-if="formDialog.useSerialPort"
|
v-if="formDialog.useSerialPort"
|
||||||
filled
|
filled
|
||||||
type="text"
|
type="text"
|
||||||
v-model="formDialog.data.accountPath"
|
v-model="accountPath"
|
||||||
height="50px"
|
height="50px"
|
||||||
autogrow
|
autogrow
|
||||||
label="Account Path"
|
label="Account Path"
|
||||||
|
|
@ -216,7 +216,7 @@
|
||||||
unelevated
|
unelevated
|
||||||
color="primary"
|
color="primary"
|
||||||
:disable="
|
:disable="
|
||||||
(formDialog.data.masterpub == null && formDialog.data.accountPath == null)||
|
(formDialog.data.masterpub == null && accountPath == null)||
|
||||||
formDialog.data.title == null"
|
formDialog.data.title == null"
|
||||||
type="submit"
|
type="submit"
|
||||||
>Add Watch-Only Account</q-btn
|
>Add Watch-Only Account</q-btn
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,15 @@ async function walletList(path) {
|
||||||
address: {},
|
address: {},
|
||||||
formDialog: {
|
formDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
addressType: {
|
|
||||||
label: 'Pay-to-witness-pubkey-hash scripts (P2WPKH)',
|
addressType: 'wpkh',
|
||||||
value: 'wpkh'
|
|
||||||
},
|
|
||||||
useSerialPort: false,
|
useSerialPort: false,
|
||||||
data: {}
|
data: {
|
||||||
|
title: '',
|
||||||
|
masterpub: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
accountPath: '',
|
||||||
filter: '',
|
filter: '',
|
||||||
addressTypeOptions: [
|
addressTypeOptions: [
|
||||||
{
|
{
|
||||||
|
|
@ -103,6 +105,17 @@ async function walletList(path) {
|
||||||
},
|
},
|
||||||
createWalletAccount: async function (data) {
|
createWalletAccount: async function (data) {
|
||||||
try {
|
try {
|
||||||
|
if (this.formDialog.useSerialPort) {
|
||||||
|
const {xpub, fingerprint} = await this.fetchXpubFromHww()
|
||||||
|
if (!xpub) return
|
||||||
|
const path = this.accountPath.substring(2)
|
||||||
|
const outputType = this.formDialog.addressType
|
||||||
|
if (outputType === 'sh') {
|
||||||
|
data.masterpub = `${outputType}(wpkh([${fingerprint}/${path}]${xpub}/{0,1}/*))`
|
||||||
|
} else {
|
||||||
|
data.masterpub = `${outputType}([${fingerprint}/${path}]${xpub}/{0,1}/*)`
|
||||||
|
}
|
||||||
|
}
|
||||||
const response = await LNbits.api.request(
|
const response = await LNbits.api.request(
|
||||||
'POST',
|
'POST',
|
||||||
'/watchonly/api/v1/wallet',
|
'/watchonly/api/v1/wallet',
|
||||||
|
|
@ -117,6 +130,20 @@ async function walletList(path) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fetchXpubFromHww: async function () {
|
||||||
|
const error = findAccountPathIssues(this.accountPath)
|
||||||
|
if (error) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Invalid derivation path.',
|
||||||
|
caption: error,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await this.serialSignerRef.hwwXpub(this.accountPath)
|
||||||
|
return await this.serialSignerRef.isFetchingXpub()
|
||||||
|
},
|
||||||
deleteWalletAccount: function (walletAccountId) {
|
deleteWalletAccount: function (walletAccountId) {
|
||||||
LNbits.utils
|
LNbits.utils
|
||||||
.confirmDialog(
|
.confirmDialog(
|
||||||
|
|
@ -214,7 +241,6 @@ async function walletList(path) {
|
||||||
this.formDialog.useSerialPort = false
|
this.formDialog.useSerialPort = false
|
||||||
},
|
},
|
||||||
getXpubFromDevice: async function () {
|
getXpubFromDevice: async function () {
|
||||||
this.handleAddressTypeChanged('wpkh')
|
|
||||||
try {
|
try {
|
||||||
if (!this.serialSignerRef.isConnected()) {
|
if (!this.serialSignerRef.isConnected()) {
|
||||||
const portOpen = await this.serialSignerRef.openSerialPort()
|
const portOpen = await this.serialSignerRef.openSerialPort()
|
||||||
|
|
@ -239,12 +265,13 @@ async function walletList(path) {
|
||||||
handleAddressTypeChanged: function (value) {
|
handleAddressTypeChanged: function (value) {
|
||||||
const addressType =
|
const addressType =
|
||||||
this.addressTypeOptions.find(t => t.value === value) || {}
|
this.addressTypeOptions.find(t => t.value === value) || {}
|
||||||
this.formDialog.data.accountPath = addressType.path
|
this.accountPath = addressType.path
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
if (this.inkey) {
|
if (this.inkey) {
|
||||||
await this.refreshWalletAccounts()
|
await this.refreshWalletAccounts()
|
||||||
|
this.handleAddressTypeChanged('wpkh')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const COMMAND_SEED = '/seed'
|
||||||
const COMMAND_RESTORE = '/restore'
|
const COMMAND_RESTORE = '/restore'
|
||||||
const COMMAND_CONFIRM_NEXT = '/confirm-next'
|
const COMMAND_CONFIRM_NEXT = '/confirm-next'
|
||||||
const COMMAND_CANCEL = '/cancel'
|
const COMMAND_CANCEL = '/cancel'
|
||||||
|
const COMMAND_XPUB = '/xpub'
|
||||||
|
|
||||||
const DEFAULT_RECEIVE_GAP_LIMIT = 20
|
const DEFAULT_RECEIVE_GAP_LIMIT = 20
|
||||||
|
|
||||||
|
|
@ -171,3 +172,12 @@ function loadTemplateAsync(path) {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findAccountPathIssues(path = '') {
|
||||||
|
const p = path.split('/')
|
||||||
|
if (p[0] !== 'm') return "Path must start with 'm/'"
|
||||||
|
for (let i = 1; i < p.length; i++) {
|
||||||
|
if (p[i].endsWith('')) p[i] = p[i].substring(0, p[i].length - 1)
|
||||||
|
if (isNaN(p[i])) return `${p[i]} is not a valid value`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue