mirror of
https://github.com/fullstack-cash/fullstack-gatsby-theme-bch-wallet.git
synced 2026-09-21 16:52:06 -07:00
feat(ipfs-coord): Pull out ipfs-coord stuff
This commit is contained in:
Generated
+6339
-46138
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,6 @@
|
|||||||
"gatsby-source-filesystem": "^4.1.0",
|
"gatsby-source-filesystem": "^4.1.0",
|
||||||
"gatsby-transformer-sharp": "^4.1.0",
|
"gatsby-transformer-sharp": "^4.1.0",
|
||||||
"https-browserify": "^1.0.0",
|
"https-browserify": "^1.0.0",
|
||||||
"ipfs-coord": "^6.7.4",
|
|
||||||
"jsonrpc-lite": "^2.2.0",
|
"jsonrpc-lite": "^2.2.0",
|
||||||
"p-queue": "^7.1.0",
|
"p-queue": "^7.1.0",
|
||||||
"p-retry": "^5.0.0",
|
"p-retry": "^5.0.0",
|
||||||
|
|||||||
@@ -1,404 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
import { Content, Row, Col, Inputs, Box } from 'adminlte-2-react'
|
|
||||||
import IpfsControl from '../lib/ipfs-control'
|
|
||||||
import IPFSTabs from './ipfs-tabs'
|
|
||||||
import CommandRouter from '../lib/commands'
|
|
||||||
|
|
||||||
import './ipfs.css'
|
|
||||||
|
|
||||||
import RetryQueue from '../../../../lib/retry-queue.mjs'
|
|
||||||
const queue = new RetryQueue()
|
|
||||||
|
|
||||||
const WalletService = require('../lib/wallet-service')
|
|
||||||
const BchWallet = typeof window !== 'undefined' ? window.SlpWallet : null
|
|
||||||
|
|
||||||
const { Checkbox } = Inputs
|
|
||||||
|
|
||||||
let _this
|
|
||||||
class IPFS extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
this.state = {
|
|
||||||
isStarted: false,
|
|
||||||
ipfsConnection: false,
|
|
||||||
appStatusOutput: '',
|
|
||||||
statusOutput: '',
|
|
||||||
commandOutput: "Enter 'help' to see available commands.",
|
|
||||||
commandInput: '',
|
|
||||||
peers: [],
|
|
||||||
chatOutputs: {
|
|
||||||
All: {
|
|
||||||
output: '',
|
|
||||||
nickname: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_this = this
|
|
||||||
_this.BchWallet = BchWallet
|
|
||||||
_this.WalletService = WalletService
|
|
||||||
|
|
||||||
this.initIPFSControl()
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { ipfsConnection } = _this.state
|
|
||||||
return (
|
|
||||||
<Content>
|
|
||||||
<Row>
|
|
||||||
<Col sm={12}>
|
|
||||||
<Box className='text-center ipfs-checkbox-container'>
|
|
||||||
<Checkbox
|
|
||||||
id='ipfs-checkbox'
|
|
||||||
className='ipfs-checkbox'
|
|
||||||
value={ipfsConnection} // mark as checked
|
|
||||||
text='Connect to wallet services over IPFS'
|
|
||||||
labelPosition='none'
|
|
||||||
labelXs={0}
|
|
||||||
name='ipfsConnection'
|
|
||||||
onChange={this.handleIpfs}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
{ipfsConnection && (
|
|
||||||
<IPFSTabs
|
|
||||||
ipfsControl={_this.ipfsControl}
|
|
||||||
handleCommandLog={_this.onCommandLog}
|
|
||||||
commandOutput={_this.state.commandOutput}
|
|
||||||
statusOutput={_this.state.statusOutput}
|
|
||||||
appStatusOutput={_this.state.appStatusOutput}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Content>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleIpfs () {
|
|
||||||
const connect = !_this.state.ipfsConnection
|
|
||||||
const { isStarted } = _this.state
|
|
||||||
_this.setState(prevState => ({
|
|
||||||
ipfsConnection: connect
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Save checkbox state into localstorage
|
|
||||||
const { walletInfo } = _this.props
|
|
||||||
walletInfo.ipfsService = connect
|
|
||||||
|
|
||||||
_this.props.setWalletInfo(walletInfo)
|
|
||||||
|
|
||||||
if (!isStarted && connect) {
|
|
||||||
try {
|
|
||||||
await _this.ipfsControl.startIpfs()
|
|
||||||
const nodeInfo = _this.ipfsControl.getNodeInfo()
|
|
||||||
console.log('nodeInfo', nodeInfo)
|
|
||||||
_this.setState({
|
|
||||||
isStarted: true
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentDidMount () {
|
|
||||||
await _this.getLastIpfsCoordInstance()
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentWillUnmount () {
|
|
||||||
try {
|
|
||||||
const data = {
|
|
||||||
ipfsInfo: {
|
|
||||||
ipfsIsStarted: true,
|
|
||||||
savedState: _this.state,
|
|
||||||
ipfsControl: _this.ipfsControl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Save the current state
|
|
||||||
_this.props.setMenuNavigation({ data })
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLastIpfsCoordInstance () {
|
|
||||||
try {
|
|
||||||
const { menuNavigation, walletInfo } = _this.props
|
|
||||||
const data = menuNavigation.data
|
|
||||||
// console.log('menuNavigation', menuNavigation)
|
|
||||||
// console.log('walletInfo', walletInfo)
|
|
||||||
// console.log('data', data)
|
|
||||||
|
|
||||||
// Get local info and
|
|
||||||
// Verify if checkbox has been marked
|
|
||||||
// Return for unmarked checkbox
|
|
||||||
if (!walletInfo.ipfsService) return
|
|
||||||
|
|
||||||
_this.setState(prevState => ({
|
|
||||||
ipfsConnection: true
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Don't start ipfs if it has started already
|
|
||||||
if (!data || !data.ipfsInfo.ipfsIsStarted) {
|
|
||||||
await this.ipfsControl.startIpfs()
|
|
||||||
_this.setState({
|
|
||||||
isStarted: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads the previous information and states
|
|
||||||
if (data && data.ipfsInfo) {
|
|
||||||
const { savedState } = data.ipfsInfo
|
|
||||||
_this.setState(savedState)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initIPFSControl (bchWallet) {
|
|
||||||
try {
|
|
||||||
const ipfsConfig = {
|
|
||||||
statusLog: _this.onStatusLog,
|
|
||||||
// handleChatLog: _this.onCommandLog
|
|
||||||
handleChatLog: _this.incommingChat,
|
|
||||||
bchWallet: bchWallet || _this.props.bchWallet, // bch wallet instance
|
|
||||||
privateLog: _this.privLogChat,
|
|
||||||
pollLog: _this.onAppStatus
|
|
||||||
}
|
|
||||||
// Retrieve last ipfs control
|
|
||||||
const { menuNavigation } = _this.props
|
|
||||||
if (
|
|
||||||
menuNavigation &&
|
|
||||||
menuNavigation.data &&
|
|
||||||
menuNavigation.data.ipfsInfo.ipfsControl
|
|
||||||
) {
|
|
||||||
this.ipfsControl = menuNavigation.data.ipfsInfo.ipfsControl
|
|
||||||
} else {
|
|
||||||
// Instantiate a new ipfs control
|
|
||||||
this.ipfsControl = new IpfsControl(ipfsConfig)
|
|
||||||
}
|
|
||||||
this.commandRouter = new CommandRouter({ ipfsControl: this.ipfsControl })
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a line to the Status terminal
|
|
||||||
onStatusLog (str) {
|
|
||||||
console.log('onStatusLog', str)
|
|
||||||
try {
|
|
||||||
// Update the Status terminal
|
|
||||||
_this.setState({
|
|
||||||
statusOutput: _this.state.statusOutput + ' ' + str + '\n'
|
|
||||||
})
|
|
||||||
|
|
||||||
// If a new peer is found, trigger handleNewPeer()
|
|
||||||
if (str.includes('New peer found:')) {
|
|
||||||
const ipfsId = str.substring(24)
|
|
||||||
_this.handleNewPeer(ipfsId)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle chat messages coming in from the IPFS network.
|
|
||||||
incommingChat (str) {
|
|
||||||
try {
|
|
||||||
const { chatOutputs, connectedPeer } = _this.state
|
|
||||||
// console.log(`connectedPeer: ${JSON.stringify(connectedPeer, null, 2)}`)
|
|
||||||
// console.log(`incommingChat str: ${JSON.stringify(str, null, 2)}`)
|
|
||||||
|
|
||||||
const msg = str.data.data.message
|
|
||||||
const handle = str.data.data.handle
|
|
||||||
const terminalOut = `${handle}: ${msg}`
|
|
||||||
|
|
||||||
if (str.data && str.data.apiName && str.data.apiName.includes('chat')) {
|
|
||||||
// If the message is marked as 'chat' data, then post it to the public
|
|
||||||
// chat terminal.
|
|
||||||
chatOutputs.All.output = chatOutputs.All.output + terminalOut + '\n'
|
|
||||||
} else {
|
|
||||||
// Asigns the output to the corresponding peer
|
|
||||||
chatOutputs[connectedPeer].output =
|
|
||||||
chatOutputs[connectedPeer].output + terminalOut + '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
_this.setState({
|
|
||||||
chatOutputs
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(err)
|
|
||||||
// Don't throw an error as this is a top-level handler.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle decrypted, private messages and send them to the right terminal.
|
|
||||||
privLogChat (str, from) {
|
|
||||||
try {
|
|
||||||
console.log(`privLogChat str: ${str}`)
|
|
||||||
// console.log(`privLogChat from: ${from}`)
|
|
||||||
_this.handleRpcDataQueue(str)
|
|
||||||
|
|
||||||
const { chatOutputs } = _this.state
|
|
||||||
|
|
||||||
const terminalOut = `peer: ${str}`
|
|
||||||
|
|
||||||
// Asigns the output to the corresponding peer
|
|
||||||
chatOutputs[from].output = chatOutputs[from].output + terminalOut + '\n'
|
|
||||||
|
|
||||||
_this.setState({
|
|
||||||
chatOutputs
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Error in privLogChat():', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function is triggered when a new peer is detected.
|
|
||||||
handleNewPeer (ipfsId) {
|
|
||||||
try {
|
|
||||||
console.log(`New IPFS peer discovered. ID: ${ipfsId}`)
|
|
||||||
|
|
||||||
// Use the peer IPFS ID to identify the peers state.
|
|
||||||
const { peers, chatOutputs } = _this.state
|
|
||||||
|
|
||||||
// Add the new peer to the peers array.
|
|
||||||
peers.push(ipfsId)
|
|
||||||
|
|
||||||
// Add a chatOutput entry for the new peer.
|
|
||||||
const obj = {
|
|
||||||
output: '',
|
|
||||||
nickname: ''
|
|
||||||
}
|
|
||||||
// chatOutputs[shortIpfsId] = obj
|
|
||||||
chatOutputs[ipfsId] = obj
|
|
||||||
|
|
||||||
_this.setState({
|
|
||||||
peers,
|
|
||||||
chatOutputs
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Error in handleNewPeer(): ', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a line to the Command terminal
|
|
||||||
onCommandLog (msg) {
|
|
||||||
try {
|
|
||||||
let commandOutput
|
|
||||||
if (!msg) {
|
|
||||||
commandOutput = ''
|
|
||||||
} else {
|
|
||||||
commandOutput = _this.state.commandOutput + ' ' + msg + '\n'
|
|
||||||
}
|
|
||||||
_this.setState({
|
|
||||||
commandOutput
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pollForServices callback
|
|
||||||
// This method is called from lib/ipfs-control.js. It executes when a BCH
|
|
||||||
// wallet service is found.
|
|
||||||
async onAppStatus (msg) {
|
|
||||||
try {
|
|
||||||
let output
|
|
||||||
if (!msg) {
|
|
||||||
output = ''
|
|
||||||
} else {
|
|
||||||
output = _this.state.appStatusOutput + ' ' + msg + '\n'
|
|
||||||
}
|
|
||||||
_this.setState({
|
|
||||||
appStatusOutput: output
|
|
||||||
})
|
|
||||||
|
|
||||||
// Updates the bchWallet instance so it works
|
|
||||||
// under the ipfs services
|
|
||||||
await _this.reInitialize()
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
// Don't throw an error as this is a top-level handler.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the bchWallet instance so it works
|
|
||||||
// under the ipfs services
|
|
||||||
async reInitialize () {
|
|
||||||
try {
|
|
||||||
_this.onStatusLog('waiting for re-initialize...')
|
|
||||||
|
|
||||||
const currentWallet = _this.props.walletInfo
|
|
||||||
const { mnemonic } = currentWallet
|
|
||||||
|
|
||||||
const walletService = new _this.WalletService({
|
|
||||||
ipfsControl: this.ipfsControl
|
|
||||||
})
|
|
||||||
const advancedConfig = {
|
|
||||||
interface: 'json-rpc',
|
|
||||||
jsonRpcWalletService: walletService
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the wallet, using auth-rety on failure.
|
|
||||||
const walletIn = { mnemonic, advancedConfig }
|
|
||||||
await queue.retryWrapper(_this.initWallet, walletIn)
|
|
||||||
|
|
||||||
// Update redux state
|
|
||||||
_this.props.setBchWallet(_this.bchWalletLib)
|
|
||||||
_this.onStatusLog('re-initialize success!')
|
|
||||||
} catch (error) {
|
|
||||||
_this.onStatusLog('Error in reInitialize()')
|
|
||||||
_this.onStatusLog(error.message)
|
|
||||||
// Don't throw an error as this is a top-level handler.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the wallet and retrieve the UTXOs for the wallet.
|
|
||||||
// This function is called by the queue library, to do automatic retry on
|
|
||||||
// network failure.
|
|
||||||
async initWallet (walletIn) {
|
|
||||||
try {
|
|
||||||
const { mnemonic, advancedConfig } = walletIn
|
|
||||||
|
|
||||||
_this.bchWalletLib = new _this.BchWallet(mnemonic, advancedConfig)
|
|
||||||
|
|
||||||
// Wait for wallet to be created.
|
|
||||||
await _this.bchWalletLib.walletInfoPromise
|
|
||||||
|
|
||||||
// If auto UTXO initialization fails, do it manually.
|
|
||||||
let utxos = _this.bchWalletLib.utxos.utxoStore
|
|
||||||
if (!utxos) {
|
|
||||||
utxos = await _this.bchWalletLib.getUtxos()
|
|
||||||
}
|
|
||||||
|
|
||||||
_this.onStatusLog(
|
|
||||||
`utxo initialization succeeded: ${JSON.stringify(utxos, null, 2)}`
|
|
||||||
)
|
|
||||||
|
|
||||||
return _this.bchWalletLib
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in initWallet(): ', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill the queue with the incoming data (private messages) from the petitions
|
|
||||||
// So it will be sweeped by the lib/wallet-service/waitForRPCResponse() function
|
|
||||||
handleRpcDataQueue (data) {
|
|
||||||
try {
|
|
||||||
_this.bchWalletLib.ar.jsonRpcWalletService.rpcHandler(data)
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Error in handleRpcDataQueue', error)
|
|
||||||
// Don't throw an error as this is a top-level handler.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep (ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IPFS.propTypes = {}
|
|
||||||
|
|
||||||
export default IPFS
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
import { Row, Col, Inputs } from 'adminlte-2-react'
|
|
||||||
import { Tabs, Tab } from 'react-bootstrap'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import CommandRouter from '../lib/commands'
|
|
||||||
|
|
||||||
import 'adminlte-2-react/src/adminlte/css/AdminLTE.css'
|
|
||||||
const { Text } = Inputs
|
|
||||||
|
|
||||||
let _this
|
|
||||||
class IPFSTabs extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
this.state = {
|
|
||||||
commandOutput: "Enter 'help' to see available commands."
|
|
||||||
}
|
|
||||||
_this = this
|
|
||||||
// Starts ipfs control if there is a wallet registered already
|
|
||||||
// console.log('props', props)
|
|
||||||
|
|
||||||
if (props && props.ipfsControl) {
|
|
||||||
this.ipfsControl = props.ipfsControl
|
|
||||||
this.commandRouter = new CommandRouter({ ipfsControl: this.ipfsControl })
|
|
||||||
// console.log('this.commandRouter: ', this.commandRouter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { commandOutput } = _this.state
|
|
||||||
const { statusOutput, appStatusOutput } = _this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Row>
|
|
||||||
<Col md={12}>
|
|
||||||
<Tabs
|
|
||||||
defaultActiveKey='status'
|
|
||||||
id='ipfs-coord-tabs'
|
|
||||||
className='mb-3 nav-tabs-custom'
|
|
||||||
>
|
|
||||||
<Tab eventKey='status' title='Status'>
|
|
||||||
<Text
|
|
||||||
id='statusLog'
|
|
||||||
name='statusLog'
|
|
||||||
inputType='textarea'
|
|
||||||
labelPosition='none'
|
|
||||||
rows={20}
|
|
||||||
readOnly
|
|
||||||
value={appStatusOutput}
|
|
||||||
onChange={() => {
|
|
||||||
// Prevents DOM error
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<Tab eventKey='ipfs-coord' title='IPFS Coord'>
|
|
||||||
<Text
|
|
||||||
id='ipfsCoordLog'
|
|
||||||
name='ipfsCoordLog'
|
|
||||||
inputType='textarea'
|
|
||||||
labelPosition='none'
|
|
||||||
rows={20}
|
|
||||||
readOnly
|
|
||||||
value={statusOutput}
|
|
||||||
onChange={() => {
|
|
||||||
// Prevents DOM error
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
<Tab eventKey='command' title='Command'>
|
|
||||||
<Text
|
|
||||||
id='commandLog'
|
|
||||||
name='commandLog'
|
|
||||||
inputType='textarea'
|
|
||||||
labelPosition='none'
|
|
||||||
rows={20}
|
|
||||||
readOnly
|
|
||||||
value={`${commandOutput ? `${commandOutput}>` : '>'}`}
|
|
||||||
onChange={() => {
|
|
||||||
// Prevents DOM error
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
id='commandInput'
|
|
||||||
name='commandInput'
|
|
||||||
inputType='tex'
|
|
||||||
labelPosition='none'
|
|
||||||
value={this.state.commandInput}
|
|
||||||
onChange={this.handleTextInput}
|
|
||||||
onKeyDown={_this.handleCommandKeyDown}
|
|
||||||
/>
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles text typed into the input box.
|
|
||||||
handleTextInput (event) {
|
|
||||||
event.preventDefault()
|
|
||||||
|
|
||||||
const target = event.target
|
|
||||||
const value = target.value
|
|
||||||
const name = target.name
|
|
||||||
// console.log('value: ', value)
|
|
||||||
|
|
||||||
_this.setState({
|
|
||||||
[name]: value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate () {
|
|
||||||
if (_this.state.commandOutput !== _this.props.commandOutput) {
|
|
||||||
_this.setState({
|
|
||||||
commandOutput: _this.props.commandOutput
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* START command handling functions */
|
|
||||||
// Handles when the Enter key is pressed while in the chat input box.
|
|
||||||
async handleCommandKeyDown (e) {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
// _this.submitMsg()
|
|
||||||
// console.log("Enter key");
|
|
||||||
|
|
||||||
// Send a chat message to the chat pubsub room.
|
|
||||||
// const now = new Date();
|
|
||||||
// const msg = `Message from BROWSER at ${now.toLocaleString()}`
|
|
||||||
const msg = _this.state.commandInput
|
|
||||||
// console.log(`Sending this message: ${msg}`);
|
|
||||||
|
|
||||||
// _this.handleCommandLog(`me: ${msg}`);
|
|
||||||
|
|
||||||
// console.log('_this.commandRouter: ', _this.commandRouter)
|
|
||||||
const outMsg = await _this.commandRouter.route(msg, _this.ipfsControl)
|
|
||||||
|
|
||||||
if (outMsg === 'clear') {
|
|
||||||
_this.props.handleCommandLog('')
|
|
||||||
} else {
|
|
||||||
_this.handleCommandLog(`\n${outMsg}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the input text box.
|
|
||||||
_this.setState({
|
|
||||||
commandInput: ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a line to the terminal
|
|
||||||
async handleCommandLog (msg) {
|
|
||||||
try {
|
|
||||||
// console.log("msg: ", msg);
|
|
||||||
|
|
||||||
_this.props.handleCommandLog(msg)
|
|
||||||
// Add a slight delay, to give the browser time to render the DOM.
|
|
||||||
await this.sleep(250)
|
|
||||||
|
|
||||||
// _this.keepScrolled();
|
|
||||||
// _this.keepCommandScrolled()
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep (ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IPFSTabs.propTypes = {}
|
|
||||||
|
|
||||||
IPFSTabs.propTypes = {
|
|
||||||
handleCommandLog: PropTypes.func,
|
|
||||||
commandOutput: PropTypes.string,
|
|
||||||
statusOutput: PropTypes.string,
|
|
||||||
appStatusOutput: PropTypes.string
|
|
||||||
}
|
|
||||||
export default IPFSTabs
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
.ipfs-checkbox-container > div > div > div >input{
|
|
||||||
height: 1em;
|
|
||||||
width: 1em;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.ipfs-checkbox-container > div > div > div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
Handles commands from command terminal.
|
|
||||||
*/
|
|
||||||
|
|
||||||
let _this
|
|
||||||
|
|
||||||
class CommandRouter {
|
|
||||||
constructor (cmdConfig) {
|
|
||||||
if (cmdConfig && cmdConfig.ipfsControl) {
|
|
||||||
this.ipfsControl = cmdConfig.ipfsControl
|
|
||||||
}
|
|
||||||
|
|
||||||
_this = this
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse and route a command to the proper handler.
|
|
||||||
async route (command, appIpfs) {
|
|
||||||
try {
|
|
||||||
// console.log(`command: ${command}`)
|
|
||||||
|
|
||||||
// Split the command into an array of words separated by a space
|
|
||||||
const words = command.toString().split(' ')
|
|
||||||
// console.log(`words: ${JSON.stringify(words, null, 2)}`)
|
|
||||||
|
|
||||||
switch (words[0]) {
|
|
||||||
case 'help':
|
|
||||||
return this.help()
|
|
||||||
case 'list':
|
|
||||||
return await this.list(command, appIpfs)
|
|
||||||
case 'clear':
|
|
||||||
return 'clear'
|
|
||||||
case 'pubsub':
|
|
||||||
return await this.pubsub(command, appIpfs)
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in commandRouter()')
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the help
|
|
||||||
help () {
|
|
||||||
const msg = `
|
|
||||||
Available commands:
|
|
||||||
- help - this help.
|
|
||||||
- clear - clear the command terminal.
|
|
||||||
- list peers - list all known ipfs-coord peers.
|
|
||||||
- list relays - list all known circuit relays and their state.
|
|
||||||
- pubsub list - list all subscribed pubsub channels.
|
|
||||||
`
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
async list (command, appIpfs) {
|
|
||||||
const words = command.toString().split(' ')
|
|
||||||
|
|
||||||
switch (words[1]) {
|
|
||||||
case 'relays':
|
|
||||||
return this.listRelays(_this.ipfsControl)
|
|
||||||
case 'peers':
|
|
||||||
return this.listPeers(_this.ipfsControl)
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async pubsub (command, appIpfs) {
|
|
||||||
const words = command.toString().split(' ')
|
|
||||||
|
|
||||||
switch (words[1]) {
|
|
||||||
case 'list':
|
|
||||||
return this.listPubsubChannels(appIpfs)
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List known ipfs-coord peers.
|
|
||||||
async listPeers (appIpfs) {
|
|
||||||
try {
|
|
||||||
// console.log('appIpfs: ', appIpfs)
|
|
||||||
|
|
||||||
const relays = `Known ipfs-coord peers:\n${JSON.stringify(
|
|
||||||
appIpfs.ipfsCoord.thisNode.peerData,
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}`
|
|
||||||
return relays
|
|
||||||
|
|
||||||
// return "test"
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in listPeers(): ', err)
|
|
||||||
return 'Error in listPeers()'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List the relays connected to this IPFS node.
|
|
||||||
async listRelays (appIpfs) {
|
|
||||||
try {
|
|
||||||
// console.log('appIpfs: ', appIpfs)
|
|
||||||
|
|
||||||
const relays = `Known Circuit Relays:\n${JSON.stringify(
|
|
||||||
appIpfs.ipfsCoord.thisNode.relayData,
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}`
|
|
||||||
return relays
|
|
||||||
|
|
||||||
// return "test"
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in listRelays(): ', err)
|
|
||||||
return 'Error in listRelays()'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async listPubsubChannels (appIpfs) {
|
|
||||||
try {
|
|
||||||
// console.log('appIpfs: ', appIpfs)
|
|
||||||
|
|
||||||
const channels = await appIpfs.ipfs.pubsub.ls()
|
|
||||||
|
|
||||||
const outStr = `Pubsub Subscriptions:\n${JSON.stringify(
|
|
||||||
channels,
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}`
|
|
||||||
|
|
||||||
return outStr
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in listPubsubChannels(): ', err)
|
|
||||||
return 'Error in listPubsubChannels()'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CommandRouter
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
/*
|
|
||||||
This library controls the IPFS interface for the app.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This library contains the logic around the browser-based IPFS full node.
|
|
||||||
*/
|
|
||||||
import IPFS from '@chris.troutner/ipfs'
|
|
||||||
import IpfsCoord from 'ipfs-coord'
|
|
||||||
const semver = require('semver')
|
|
||||||
|
|
||||||
// CHANGE THESE VARIABLES
|
|
||||||
const CHAT_ROOM_NAME = 'psf-ipfs-chat-001'
|
|
||||||
const MIN_BCH_WALLET_VERSION = '1.11.0'
|
|
||||||
const WALLET_PROTOCOL = 'bch-wallet'
|
|
||||||
|
|
||||||
// JSON-LD schema used in announcement.
|
|
||||||
// Customize this data for your own app.
|
|
||||||
const name = 'Browser Chat ' + Math.floor(Math.random() * 1000)
|
|
||||||
const announceJsonLd = {
|
|
||||||
'@context': 'https://schema.org/',
|
|
||||||
'@type': 'WebAPI',
|
|
||||||
name: name,
|
|
||||||
description: 'This is a browser-based IPFS node.',
|
|
||||||
documentation: '',
|
|
||||||
provider: {
|
|
||||||
'@type': 'Organization',
|
|
||||||
name: 'Permissionless Software Foundation',
|
|
||||||
url: 'https://PSFoundation.cash'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _this
|
|
||||||
|
|
||||||
class IpfsControl {
|
|
||||||
constructor (ipfsConfig) {
|
|
||||||
this.statusLog = ipfsConfig.statusLog
|
|
||||||
this.handleChatLog = ipfsConfig.handleChatLog
|
|
||||||
this.wallet = ipfsConfig.bchWallet
|
|
||||||
this.privateLog = ipfsConfig.privateLog
|
|
||||||
this.pollLog = ipfsConfig.pollLog
|
|
||||||
this.serviceProviders = []
|
|
||||||
this.selectedServiceProvider = null
|
|
||||||
|
|
||||||
this.semver = semver
|
|
||||||
_this = this
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top level function for controlling the IPFS node. This funciton is called
|
|
||||||
// by the componentDidMount() function of the page.
|
|
||||||
async startIpfs () {
|
|
||||||
try {
|
|
||||||
console.log('Setting up instance of IPFS...')
|
|
||||||
this.statusLog('Setting up instance of IPFS...')
|
|
||||||
|
|
||||||
// Use DHT routing and ipfs.io delegates.
|
|
||||||
const ipfsOptions = {
|
|
||||||
config: {
|
|
||||||
Bootstrap: [],
|
|
||||||
Swarm: {
|
|
||||||
ConnMgr: {
|
|
||||||
HighWater: 30,
|
|
||||||
LowWater: 10
|
|
||||||
},
|
|
||||||
AddrFilters: []
|
|
||||||
},
|
|
||||||
Routing: {
|
|
||||||
Type: 'dhtclient'
|
|
||||||
},
|
|
||||||
preload: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
offline: true
|
|
||||||
},
|
|
||||||
libp2p: {
|
|
||||||
config: {
|
|
||||||
dht: {
|
|
||||||
enabled: true,
|
|
||||||
clientMode: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// const ipfsOptions = {
|
|
||||||
// Bootstrap: [],
|
|
||||||
// Swarm: {
|
|
||||||
// ConnMgr: {
|
|
||||||
// HighWater: 30,
|
|
||||||
// LowWater: 10
|
|
||||||
// },
|
|
||||||
// AddrFilters: []
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.ipfs = await IPFS.create(ipfsOptions)
|
|
||||||
this.statusLog('IPFS node created.')
|
|
||||||
|
|
||||||
// Set a 'low-power' profile for the IPFS node.
|
|
||||||
await this.ipfs.config.profiles.apply('lowpower')
|
|
||||||
|
|
||||||
// Generate a new wallet.
|
|
||||||
// this.wallet = new BchWallet()
|
|
||||||
// console.log("this.wallet: ", this.wallet);
|
|
||||||
|
|
||||||
if (!this.wallet) {
|
|
||||||
throw new Error('Wallet Not Found.! . Create or import a wallet')
|
|
||||||
}
|
|
||||||
// Wait for the wallet to initialize.
|
|
||||||
await this.wallet.walletInfoPromise
|
|
||||||
|
|
||||||
// Instantiate the IPFS Coordination library.
|
|
||||||
this.ipfsCoord = new IpfsCoord({
|
|
||||||
ipfs: this.ipfs,
|
|
||||||
type: 'browser',
|
|
||||||
statusLog: this.statusLog, // Status log
|
|
||||||
bchjs: this.wallet.bchjs,
|
|
||||||
mnemonic: this.wallet.walletInfo.mnemonic,
|
|
||||||
privateLog: this.privateLog,
|
|
||||||
announceJsonLd
|
|
||||||
})
|
|
||||||
this.statusLog('ipfs-coord library instantiated.')
|
|
||||||
|
|
||||||
// Wait for the coordination stuff to be setup.
|
|
||||||
await this.ipfsCoord.start()
|
|
||||||
|
|
||||||
const nodeConfig = await this.ipfs.config.getAll()
|
|
||||||
console.log(
|
|
||||||
`IPFS node configuration: ${JSON.stringify(nodeConfig, null, 2)}`
|
|
||||||
)
|
|
||||||
|
|
||||||
// subscribe to the 'chat' chatroom.
|
|
||||||
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
|
|
||||||
CHAT_ROOM_NAME,
|
|
||||||
this.handleChatLog,
|
|
||||||
this.ipfsCoord.thisNode
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pass the IPFS instance to the window object. Makes it easy to debug IPFS
|
|
||||||
// issues in the browser console.
|
|
||||||
if (typeof window !== 'undefined') window.ipfs = this.ipfs
|
|
||||||
|
|
||||||
// Get this nodes IPFS ID
|
|
||||||
const id = await this.ipfs.id()
|
|
||||||
this.ipfsId = id.id
|
|
||||||
this.statusLog(`This IPFS node ID: ${this.ipfsId}`)
|
|
||||||
|
|
||||||
console.log('IPFS node setup complete.')
|
|
||||||
this.statusLog('IPFS node setup complete.')
|
|
||||||
_this.statusLog(' ')
|
|
||||||
|
|
||||||
setInterval(this.pollForServices, 10000)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in startIpfs(): ', err)
|
|
||||||
this.statusLog(
|
|
||||||
'Error trying to initialize IPFS node! Have you created a wallet?'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This funciton handles incoming chat messages.
|
|
||||||
handleChatMsg (msg) {
|
|
||||||
try {
|
|
||||||
console.log('handleChatMsg msg: ', msg)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in handleChatMsg(): ', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getNodeInfo () {
|
|
||||||
return {
|
|
||||||
ipfsId: this.ipfsId,
|
|
||||||
announceJsonLd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poll the ipfs-coord coordination channel for available service providers.
|
|
||||||
pollForServices () {
|
|
||||||
try {
|
|
||||||
// An array of IPFS IDs of other nodes in the coordination pubsub channel.
|
|
||||||
const peers = _this.ipfsCoord.thisNode.peerList
|
|
||||||
// console.log(`peers: ${JSON.stringify(peers, null, 2)}`)
|
|
||||||
|
|
||||||
// Array of objects. Each object is the IPFS ID of the peer and contains
|
|
||||||
// data about that peer.
|
|
||||||
const peerData = _this.ipfsCoord.thisNode.peerData
|
|
||||||
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
|
|
||||||
|
|
||||||
for (let i = 0; i < peers.length; i++) {
|
|
||||||
const thisPeer = peers[i]
|
|
||||||
const thisData = peerData.filter(x => x.from === thisPeer)
|
|
||||||
const thisPeerData = thisData[0]
|
|
||||||
|
|
||||||
// Create a 'fingerprint' that defines the wallet service.
|
|
||||||
const protocol = thisPeerData.data.jsonLd.protocol
|
|
||||||
const version = thisPeerData.data.jsonLd.version
|
|
||||||
// console.log(
|
|
||||||
// `debug: peer ${thisPeer} uses protocol: ${protocol} v${version}`,
|
|
||||||
// )
|
|
||||||
|
|
||||||
let versionMatches = false
|
|
||||||
if (version) {
|
|
||||||
versionMatches = _this.semver.gt(version, MIN_BCH_WALLET_VERSION)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore any peers that don't match the fingerprint for a BCH wallet
|
|
||||||
// service.
|
|
||||||
if (protocol && protocol.includes(WALLET_PROTOCOL) && versionMatches) {
|
|
||||||
// console.log('Matching peer: ', thisPeerData)
|
|
||||||
|
|
||||||
// Temporary business logic.
|
|
||||||
// Use the first available wallet service detected.
|
|
||||||
if (_this.serviceProviders.length === 0) {
|
|
||||||
_this.selectedServiceProvider = thisPeer
|
|
||||||
|
|
||||||
// Persist the config setting, so it can be used by other commands.
|
|
||||||
// _this.conf.set('selectedService', thisPeer)
|
|
||||||
const pollLog = `---->BCH wallet service selected: ${thisPeer}`
|
|
||||||
console.log(pollLog)
|
|
||||||
_this.pollLog(pollLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the peer to the list of serviceProviders.
|
|
||||||
_this.serviceProviders.push(thisPeer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in pollForServices(): ', err)
|
|
||||||
// Do not throw error. This is a top-level function.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// module.exports = AppIpfs
|
|
||||||
export default IpfsControl
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
/*
|
|
||||||
This library interacts with the ipfs-bch-wallet-service via the JSON RPC
|
|
||||||
over IPFS.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { v4: uid } = require('uuid')
|
|
||||||
const jsonrpc = require('jsonrpc-lite')
|
|
||||||
|
|
||||||
// Public npm libraries.
|
|
||||||
const axios = require('axios')
|
|
||||||
// const Conf = require('conf')
|
|
||||||
|
|
||||||
class WalletService {
|
|
||||||
constructor (localConfig = {}) {
|
|
||||||
// Encapsulate dependencies
|
|
||||||
this.axios = axios
|
|
||||||
// this.conf = new Conf()
|
|
||||||
// this.ipfsControl = localConfig.ipfsControl
|
|
||||||
this.uid = uid
|
|
||||||
this.jsonrpc = jsonrpc
|
|
||||||
this.ipfsControl = localConfig.ipfsControl
|
|
||||||
// A queue for holding RPC data that has arrived.
|
|
||||||
this.rpcDataQueue = []
|
|
||||||
}
|
|
||||||
|
|
||||||
// This handler is triggered when RPC data comes in over IPFS.
|
|
||||||
// Handle RPC input, and match the input to the RPC queue.
|
|
||||||
|
|
||||||
// NOTE: This function is called when a private message
|
|
||||||
// is sent to this node
|
|
||||||
|
|
||||||
rpcHandler (data) {
|
|
||||||
try {
|
|
||||||
// Convert string input into an object.
|
|
||||||
const jsonData = JSON.parse(data)
|
|
||||||
|
|
||||||
// console.log(
|
|
||||||
// 'rest-api.js/rpcHandler() data: ',
|
|
||||||
// JSON.stringify(jsonData, null, 2),
|
|
||||||
// )
|
|
||||||
console.log(`JSON RPC response for ID ${jsonData.id} received.`)
|
|
||||||
|
|
||||||
this.rpcDataQueue.push(jsonData)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in rest-api.js/rpcHandler(): ', err)
|
|
||||||
// Do not throw error. This is a top-level function.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkServiceId () {
|
|
||||||
try {
|
|
||||||
// this.conf = new Conf()
|
|
||||||
|
|
||||||
const serviceId = this.ipfsControl.selectedServiceProvider
|
|
||||||
console.log(`serviceId : ${serviceId}`)
|
|
||||||
if (!serviceId) {
|
|
||||||
throw new Error('Wallet service ID does not exist')
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceId
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in checkServiceId()')
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get up to 20 addresses.
|
|
||||||
async getBalances (addrs) {
|
|
||||||
try {
|
|
||||||
// Input validation.
|
|
||||||
if (!addrs || !Array.isArray(addrs)) {
|
|
||||||
throw new Error(
|
|
||||||
'addrs input to getBalance() must be an array, of up to 20 addresses.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceId = this.checkServiceId()
|
|
||||||
// console.log(`serviceId: ${serviceId}`)
|
|
||||||
|
|
||||||
const rpcId = this.uid()
|
|
||||||
const rpcData = {
|
|
||||||
endpoint: 'balance',
|
|
||||||
addresses: addrs
|
|
||||||
}
|
|
||||||
// Generate a JSON RPC command.
|
|
||||||
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
|
|
||||||
const cmdStr = JSON.stringify(cmd)
|
|
||||||
const thisNode = this.ipfsControl.ipfsCoord.thisNode
|
|
||||||
|
|
||||||
await this.ipfsControl.ipfsCoord.useCases.peer.sendPrivateMessage(
|
|
||||||
serviceId,
|
|
||||||
cmdStr,
|
|
||||||
thisNode
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = await this.waitForRPCResponse(rpcId)
|
|
||||||
|
|
||||||
return data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in getBalance()')
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get hydrated UTXOs for an address
|
|
||||||
async getUtxos (addr) {
|
|
||||||
try {
|
|
||||||
// Input validation
|
|
||||||
if (!addr || typeof addr !== 'string') {
|
|
||||||
throw new Error('getUtxos() input address must be a string.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceId = this.checkServiceId()
|
|
||||||
// console.log(`serviceId: ${serviceId}`)
|
|
||||||
|
|
||||||
const rpcId = this.uid()
|
|
||||||
const rpcData = {
|
|
||||||
endpoint: 'utxos',
|
|
||||||
address: addr
|
|
||||||
}
|
|
||||||
// Generate a JSON RPC command.
|
|
||||||
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
|
|
||||||
const cmdStr = JSON.stringify(cmd)
|
|
||||||
const thisNode = this.ipfsControl.ipfsCoord.thisNode
|
|
||||||
console.log('cmdStr', cmdStr)
|
|
||||||
await this.ipfsControl.ipfsCoord.useCases.peer.sendPrivateMessage(
|
|
||||||
serviceId,
|
|
||||||
cmdStr,
|
|
||||||
thisNode
|
|
||||||
)
|
|
||||||
// Wait for data to come back from the wallet service.
|
|
||||||
const data = await this.waitForRPCResponse(rpcId)
|
|
||||||
return data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in getUtxos()', err)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Broadcast a transaction to the network.
|
|
||||||
async sendTx (hex) {
|
|
||||||
try {
|
|
||||||
// Input validation
|
|
||||||
if (!hex || typeof hex !== 'string') {
|
|
||||||
throw new Error('sendTx() input hex must be a string.')
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceId = this.checkServiceId()
|
|
||||||
// console.log(`serviceId: ${serviceId}`)
|
|
||||||
const rpcId = this.uid()
|
|
||||||
const rpcData = {
|
|
||||||
endpoint: 'broadcast',
|
|
||||||
hex
|
|
||||||
}
|
|
||||||
// Generate a JSON RPC command.
|
|
||||||
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
|
|
||||||
const cmdStr = JSON.stringify(cmd)
|
|
||||||
console.log('cmdStr', cmdStr)
|
|
||||||
|
|
||||||
const thisNode = this.ipfsControl.ipfsCoord.thisNode
|
|
||||||
|
|
||||||
await this.ipfsControl.ipfsCoord.useCases.peer.sendPrivateMessage(
|
|
||||||
serviceId,
|
|
||||||
cmdStr,
|
|
||||||
thisNode
|
|
||||||
)
|
|
||||||
const data = await this.waitForRPCResponse(rpcId)
|
|
||||||
|
|
||||||
return data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in sendTx()')
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a promise that resolves to data when the RPC response is recieved.
|
|
||||||
async waitForRPCResponse (rpcId) {
|
|
||||||
try {
|
|
||||||
// Initialize variables for tracking the return data.
|
|
||||||
let dataFound = false
|
|
||||||
let cnt = 0
|
|
||||||
let data = {
|
|
||||||
success: false,
|
|
||||||
message: 'request timed out',
|
|
||||||
data: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop that waits for a response from the service provider.
|
|
||||||
do {
|
|
||||||
for (let i = 0; i < this.rpcDataQueue.length; i++) {
|
|
||||||
const rawData = this.rpcDataQueue[i]
|
|
||||||
// console.log(`rawData: ${JSON.stringify(rawData, null, 2)}`)
|
|
||||||
|
|
||||||
if (rawData.id === rpcId) {
|
|
||||||
dataFound = true
|
|
||||||
// console.log('data was found in the queue')
|
|
||||||
|
|
||||||
data = rawData.result.value
|
|
||||||
|
|
||||||
// Remove the data from the queue
|
|
||||||
this.rpcDataQueue.splice(i, 1)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait between loops.
|
|
||||||
// await this.sleep(1000)
|
|
||||||
await this.ipfsControl.wallet.bchjs.Util.sleep(4500)
|
|
||||||
|
|
||||||
cnt++
|
|
||||||
|
|
||||||
// Exit if data was returned, or the window for a response expires.
|
|
||||||
} while (!dataFound && cnt < 10)
|
|
||||||
// console.log(`dataFound: ${dataFound}, cnt: ${cnt}`)
|
|
||||||
console.log('waitForRPCResponse', data)
|
|
||||||
return data
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error in waitForRPCResponse()')
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = WalletService
|
|
||||||
@@ -1,16 +1,5 @@
|
|||||||
import React from 'react'
|
// import React from 'react'
|
||||||
import Ipfs from './ipfs-tab'
|
|
||||||
const MenuComponents = props => {
|
const MenuComponents = props => {
|
||||||
return [
|
return []
|
||||||
{
|
|
||||||
key: 'IPFS',
|
|
||||||
icon: 'fas-message',
|
|
||||||
component: (
|
|
||||||
<>
|
|
||||||
<Ipfs {...props} />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
export default MenuComponents
|
export default MenuComponents
|
||||||
|
|||||||
Reference in New Issue
Block a user