diff --git a/qortal-ui-core/src/apiKeyUtils.js b/qortal-ui-core/src/apiKeyUtils.js
new file mode 100644
index 00000000..9d2078f1
--- /dev/null
+++ b/qortal-ui-core/src/apiKeyUtils.js
@@ -0,0 +1,76 @@
+import * as api from 'qortal-ui-crypto'
+
+'use strict'
+
+export const checkApiKey = async (nodeConfig) => {
+
+ let selectedNode = nodeConfig.knownNodes[nodeConfig.node];
+ if (selectedNode.enableManagement === false) {
+ console.log("Skipping API key check because enableManagement is false");
+ return;
+ }
+
+ let apiKey = selectedNode.apiKey;
+
+ // Attempt to generate an API key
+ const generateUrl = "/admin/apikey/generate";
+ let generateRes = await api.request(generateUrl, {
+ method: "POST"
+ });
+
+ if (generateRes != null && generateRes.error == null && generateRes.length >= 8) {
+ console.log("Generated API key");
+ apiKey = generateRes;
+ }
+ else {
+ console.log("Unable to generate API key");
+ }
+
+ // Now test the API key
+ let testResult = await testApiKey(apiKey);
+ if (testResult === true) {
+ console.log("API key test passed");
+ }
+ else {
+ console.log("API key test failed");
+
+ let apiKeyValid = false;
+
+ while (apiKeyValid === false) {
+
+ let apiKeyPrompt = prompt("Please enter the API key for this node.\n\nIt can be found in a file called 'apikey.txt' in the directory where the core is installed.\n\nAlternatively, click Cancel to use the core with reduced functionality.", "");
+ if (apiKeyPrompt === null) {
+ // Cancel was pushed - so give up
+ return;
+ }
+
+ let testResult = await testApiKey(apiKeyPrompt);
+ if (testResult === true) {
+ console.log("API key prompt test passed");
+ apiKeyValid = true;
+ apiKey = apiKeyPrompt;
+ break;
+ }
+ else {
+ console.log("API key prompt test failed. Re-prompting...");
+ }
+
+ }
+ }
+
+ // Store API key
+ selectedNode.apiKey = apiKey;
+ nodeConfig.knownNodes[nodeConfig.node] = selectedNode;
+ localStorage.setItem('myQortalNodes', JSON.stringify(nodeConfig.knownNodes));
+}
+
+export const testApiKey = async (apiKey) => {
+ const testUrl = "/admin/apikey/test?apiKey=" + apiKey;
+ let testRes = await api.request(testUrl, {
+ method: "GET"
+ });
+ if (testRes === true) {
+ return true;
+ }
+ return false;
+}
diff --git a/qortal-ui-core/src/components/app-info.js b/qortal-ui-core/src/components/app-info.js
index 83981f94..a5905a0e 100644
--- a/qortal-ui-core/src/components/app-info.js
+++ b/qortal-ui-core/src/components/app-info.js
@@ -97,7 +97,7 @@ class AppInfo extends connect(store)(LitElement) {
return html`
diff --git a/qortal-ui-core/src/components/login-view/create-account-section.js b/qortal-ui-core/src/components/login-view/create-account-section.js
index 546162c7..051bccc7 100644
--- a/qortal-ui-core/src/components/login-view/create-account-section.js
+++ b/qortal-ui-core/src/components/login-view/create-account-section.js
@@ -10,6 +10,7 @@ import FileSaver from 'file-saver'
import { doLogin, doLogout, doSelectAddress } from '../../redux/app/app-actions.js'
import { doStoreWallet } from '../../redux/user/user-actions.js'
+import { checkApiKey } from '../../apiKeyUtils.js'
import snackbar from '../../functional-components/snackbar.js'
@@ -48,7 +49,8 @@ class CreateAccountSection extends connect(store)(LitElement) {
_wallet: { type: Object },
_pass: { type: String },
_name: { type: String },
- isDownloadedBackup: { type: Boolean }
+ isDownloadedBackup: { type: Boolean },
+ nodeConfig: { type: Object }
}
}
@@ -181,6 +183,7 @@ class CreateAccountSection extends connect(store)(LitElement) {
.then(() => {
store.dispatch(doLogin(this._wallet))
store.dispatch(doSelectAddress(this._wallet.addresses[0]))
+ checkApiKey(this.nodeConfig);
this.cleanup()
return ripple.fade()
})
@@ -191,6 +194,7 @@ class CreateAccountSection extends connect(store)(LitElement) {
} else {
store.dispatch(doLogin(this._wallet))
store.dispatch(doSelectAddress(this._wallet.addresses[0]))
+ checkApiKey()
this.cleanup()
}
}
@@ -490,6 +494,7 @@ class CreateAccountSection extends connect(store)(LitElement) {
}
stateChanged(state) {
+ this.nodeConfig = state.app.nodeConfig
// this.loggedIn = state.app.loggedIn
}
diff --git a/qortal-ui-core/src/components/login-view/login-section.js b/qortal-ui-core/src/components/login-view/login-section.js
index b1cc3deb..d96e3785 100644
--- a/qortal-ui-core/src/components/login-view/login-section.js
+++ b/qortal-ui-core/src/components/login-view/login-section.js
@@ -1,6 +1,7 @@
import { LitElement, html, css } from 'lit-element'
import { connect } from 'pwa-helpers'
import { store } from '../../store.js'
+import { checkApiKey } from '../../apiKeyUtils.js'
import '@material/mwc-button'
import '@material/mwc-checkbox'
@@ -45,7 +46,8 @@ class LoginSection extends connect(store)(LitElement) {
hasStoredWallets: { type: Boolean },
saveInBrowser: { type: Boolean },
backedUpWalletJSON: { type: Object },
- backedUpSeedLoading: { type: Boolean }
+ backedUpSeedLoading: { type: Boolean },
+ nodeConfig: { type: Object }
}
}
@@ -280,7 +282,7 @@ class LoginSection extends connect(store)(LitElement) {
@@ -375,6 +377,7 @@ class LoginSection extends connect(store)(LitElement) {
this.loggedIn = state.app.loggedIn
this.wallets = state.user.storedWallets
this.hasStoredWallets = this.wallets.length > 0
+ this.nodeConfig = state.app.nodeConfig
}
keyupEnter(e, action) {
@@ -535,6 +538,7 @@ class LoginSection extends connect(store)(LitElement) {
})).catch(err => console.error(err))
}
}
+ checkApiKey(this.nodeConfig)
this.cleanup()
return this.loadingRipple.fade()
})
diff --git a/qortal-ui-core/src/plugins/routes.js b/qortal-ui-core/src/plugins/routes.js
index f47f1da0..37e68f98 100644
--- a/qortal-ui-core/src/plugins/routes.js
+++ b/qortal-ui-core/src/plugins/routes.js
@@ -11,6 +11,7 @@ import framePasteMenu from '../functional-components/frame-paste-menu.js'
const createTransaction = api.createTransaction
const processTransaction = api.processTransaction
const signChatTransaction = api.signChatTransaction
+const signArbitraryTransaction = api.signArbitraryTransaction
const tradeBotCreateRequest = api.tradeBotCreateRequest
const tradeBotRespondRequest = api.tradeBotRespondRequest
const signTradeBotTxn = api.signTradeBotTxn
@@ -169,7 +170,7 @@ export const routes = {
}
return response
},
-
+
username: async (req) => {
const state = store.getState()
const username = state.user.storedWallets[state.app.wallet.addresses[0].address].name
@@ -206,6 +207,21 @@ export const routes = {
return response
},
+ sign_arbitrary: async (req) => {
+ let response
+ try {
+ const signedArbitraryBytes = await signArbitraryTransaction(req.data.arbitraryBytesBase58, req.data.arbitraryBytesForSigningBase58, req.data.arbitraryNonce, store.getState().app.wallet._addresses[req.data.nonce].keyPair)
+
+ const res = await processTransaction(signedArbitraryBytes)
+ response = res
+ } catch (e) {
+ console.error(e)
+ console.error(e.message)
+ response = false
+ }
+ return response
+ },
+
showNotification: async (req) => {
doNewMessage(req.data)
},