Compare commits

..
18 Commits
Author SHA1 Message Date
Chris Troutner 87eb804cd1 Merge pull request #93 from Permissionless-Software-Foundation/ct-unstable
fix(util): Adding scripts for creating new users
2025-05-19 13:49:31 -07:00
Chris Troutner 23d5118b52 fix(util): Adding scripts for creating new users 2025-05-19 13:48:15 -07:00
Chris Troutner 94eb342435 Merge pull request #92 from Permissionless-Software-Foundation/ct-unstable
Syncing admin settings with upstream ipfs-service-provider
2025-05-19 13:18:38 -07:00
Chris Troutner ac326b8e34 fix(adapter/admin.js): Adding mnemonic for user 2025-05-19 13:16:32 -07:00
Chris Troutner 164a38ad20 linting 2025-05-19 13:11:23 -07:00
Chris Troutner 3300e0fc80 fix(config): Fixing merge conflict in common.js 2025-05-19 13:11:13 -07:00
Chris Troutner 3a4569c33f Merge pull request #175 from Permissionless-Software-Foundation/dh-env-var-admin
feat(admin): added env var setting so only admin can create new users
2025-05-19 13:06:40 -07:00
Daniel Gonzalez 05a0620663 feat(admin): added env var setting so only admin can create new users 2025-05-19 12:17:09 -04:00
Chris Troutner af83c06b43 Merge pull request #174 from Permissionless-Software-Foundation/ct-unstable
fix(usage): Debugging issue with usage tracking
2025-05-18 15:37:46 -07:00
Chris Troutner e6317c3282 fix(usage): Debugging issue with usage tracking 2025-05-18 15:36:32 -07:00
Chris Troutner ddce40bb52 Merge pull request #91 from Permissionless-Software-Foundation/ct-unstable
Production improvements
2025-05-16 07:54:30 -07:00
Chris Troutner 746f9a3488 fix(production): Adding script to get users in prod env 2025-05-16 07:52:58 -07:00
Chris Troutner a611580f48 Turning off CI option 2025-05-15 19:18:41 -07:00
Chris Troutner 07c671ede4 fix(production): Using v3 UI 2025-05-15 17:58:29 -07:00
Chris Troutner 7c8d73810d linting 2025-05-15 07:43:24 -07:00
Chris Troutner 9829e8581c Adding sweep-funds2 script 2025-05-15 07:43:13 -07:00
Chris Troutner a12aa5609d Merge pull request #90 from Permissionless-Software-Foundation/dh-refactor-counteroffer
feat(order): refactored acceptCounterOffer()
2025-05-15 06:01:47 -07:00
Daniel Gonzalez abf834c5a8 feat(order): refactored acceptCounterOffer() 2025-05-14 20:33:25 -04:00
19 changed files with 563 additions and 119 deletions
+7 -1
View File
@@ -183,5 +183,11 @@ export default {
// Nostr
nostrRelay: process.env.NOSTR_RELAY ? process.env.NOSTR_RELAY : 'wss://nostr-relay.psfoundation.info',
nostrTopic: process.env.NOSTR_TOPIC ? process.env.NOSTR_TOPIC : 'bch-dex-test-topic-01'
nostrTopic: process.env.NOSTR_TOPIC ? process.env.NOSTR_TOPIC : 'bch-dex-test-topic-01',
// Account Configuration
disableNewAccounts: process.env.DISABLE_NEW_ACCOUNTS ? true : false,
// Admin password
adminPassword: process.env.ADMIN_PASSWORD
}
+4 -4
View File
@@ -46,11 +46,11 @@ WORKDIR /home/safeuser
# Clone the rest.bitcoin.com repository
WORKDIR /home/safeuser
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v3
# Switch to the desired branch. `master` is usually stable,
# and `stage` has the most up-to-date changes.
WORKDIR /home/safeuser/bch-dex-ui-v2
WORKDIR /home/safeuser/bch-dex-ui-v3
# For development: switch to unstable branch
RUN git checkout ct-unstable
@@ -61,13 +61,13 @@ RUN git checkout ct-unstable
RUN npm install
# Build the site
RUN CI=true npm run build
RUN npm run build
# Load the NGINX image.
FROM nginx
EXPOSE 80
# Copy the files built in the first container to the new NGINX container.
COPY --from=builder /home/safeuser/bch-dex-ui-v2/build /usr/share/nginx/html
COPY --from=builder /home/safeuser/bch-dex-ui-v3/build /usr/share/nginx/html
#USER safeuser
+27 -58
View File
@@ -51,70 +51,38 @@ class Admin {
// used by the Listing Manager and test scripts, in order access private API
// functions.
async createSystemUser () {
// Create the system user.
try {
context.password = _this._randomString(20)
const options = {
method: 'POST',
url: `${LOCALHOST}/users`,
data: {
user: {
email: 'system@system.com',
password: context.password,
name: 'admin'
}
}
const context = {
email: 'system@system.com',
name: 'admin',
password: _this.config.adminPassword || _this._randomString(20),
type: 'admin',
mnemonic: 'spread ivory oval pioneer flash gap merit nerve simple image entire trouble'
}
const result = await _this.axios.request(options)
// console.log('admin.data: ', result.data)
// Check if the user already exists
let adminUser = await _this.User.findOne({ email: context.email })
context.email = result.data.user.email
context.id = result.data.user._id
context.token = result.data.token
if (adminUser) {
// Update the password
adminUser.password = context.password
} else {
// Create a new admin user
adminUser = new _this.User(context)
}
// Update context with the new user id and token
context.id = adminUser._id
context.token = await adminUser.generateToken()
// Get the mongoDB entry
const user = await _this.User.findById(context.id)
// Save the user
await adminUser.save()
// Change the user type to admin
user.type = 'admin'
// console.log(`user created: ${JSON.stringify(user, null, 2)}`)
// Save the user model.
await user.save()
// console.log(`admin user created: ${JSON.stringify(result.body, null, 2)}`)
// console.log(`with password: ${context.password}`)
// Write out the system user information to a JSON file that external
// applications like the Task Manager and the test scripts can access.
await jsonFiles.writeJSON(context, JSON_PATH)
// console.log('context: ', context)
// console.log('JSON_PATH: ', JSON_PATH)
// Write the user data to the JSON file
await _this.jsonFiles.writeJSON(context, JSON_PATH)
return context
} catch (err) {
// Handle existing system user.
if (err.response.status === 422) {
try {
// Delete the existing user
await _this.deleteExistingSystemUser()
// Call this function again.
return _this.createSystemUser()
} catch (err2) {
console.error(
'Error in admin.js/createSystemUser() while trying generate new system user.'
)
// process.end(1)
throw err2
}
} else {
console.log('Error in admin.js/createSystemUser: ')
// process.end(1)
throw err
}
} catch (error) {
console.log('Error in admin.js/createSystemUser()')
throw error
}
}
@@ -129,6 +97,7 @@ class Admin {
})
await _this.User.deleteOne({ email: 'system@system.com' })
return true
} catch (err) {
console.log('Error in admin.js/deleteExistingSystemUser()')
throw err
@@ -152,7 +121,7 @@ class Admin {
Accept: 'application/json'
},
data: {
email: 'system@system.com',
email: existingUser.email,
password: existingUser.password
}
}
+14 -11
View File
@@ -10,20 +10,20 @@ import mongoose from 'mongoose'
const Order = new mongoose.Schema({
// Token data
tokenId: { type: String },
utxoTxid: { type: String },
utxoVout: { type: Number },
ticker: { type: String },
tokenType: { type: Number },
tokenId: { type: String, required: true },
utxoTxid: { type: String, required: true },
utxoVout: { type: Number, required: true },
ticker: { type: String, required: true },
tokenType: { type: Number, required: true },
// Trade data
buyOrSell: { type: String },
numTokens: { type: Number },
rateInBaseUnit: { type: String },
buyOrSell: { type: String, required: true },
numTokens: { type: Number, required: true },
rateInBaseUnit: { type: String, required: true },
minUnitsToExchange: { type: String },
p2wdbTxid: { type: String },
p2wdbHash: { type: String },
makerAddr: { type: String },
makerAddr: { type: String, required: true },
// Authentication data
signature: { type: String },
@@ -32,13 +32,16 @@ const Order = new mongoose.Schema({
offerPubKey: { type: String },
// Wallet Data
hdIndex: { type: Number }, // HD index address holding the UTXO for this offer.
hdIndex: { type: Number, required: true }, // HD index address holding the UTXO for this offer.
// SWaP Protocol Properties
lokadId: { type: String },
messageType: { type: Number },
messageClass: { type: Number },
nostrEventId: { type: String } // Nostr Event Id.
nostrEventId: { type: String, required: true }, // Nostr Event Id.
// Additional properties found in createOrder
dataType: { type: String, required: true }
})
export default mongoose.model('order', Order)
+20 -4
View File
@@ -362,7 +362,7 @@ class WalletAdapter {
throw err
}
// return true
// return true
}
// Move tokens to an address controlled by the HD wallet, to generate a
@@ -467,6 +467,13 @@ class WalletAdapter {
// then broadcasting the transaction to the network.
async completeTx (hex, hdIndex) {
try {
// Input validation
if (!hex || typeof hex !== 'string') {
throw new Error('hex must be a string!')
}
if (typeof hdIndex !== 'number' || hdIndex < 0) {
throw new Error('hdIndex must be a non-negative number!')
}
// console.log('hex: ', hex)
console.log('completeTx() hdIndex: ', hdIndex)
@@ -583,6 +590,16 @@ class WalletAdapter {
async moveTokensFromCustomWallet (inObj = {}) {
try {
const { tokenId, qty, wallet } = inObj
// Input validation
if (!tokenId || typeof tokenId !== 'string') {
throw new Error('tokenId must be a string!')
}
if (!qty) {
throw new Error('qty must be a number!')
}
if (!wallet) {
throw new Error('wallet is required!')
}
const keyPair = await this.getKeyPair()
console.log('keyPair: ', keyPair)
@@ -607,17 +624,16 @@ class WalletAdapter {
)
const txid = await wallet.sendTokens(receiver, 3)
console.log('txid: ', txid)
const utxoInfo = {
txid,
vout: 1,
hdIndex: wallet.walletInfo.hdIndex,
hdIndex: keyPair.hdIndex,
tokenType: tokenUtxos[0].tokenType
}
return utxoInfo
} catch (err) {
console.error('Error in wallet.js/moveTokens()')
console.error('Error in wallet.js/moveTokensFromCustomWallet()')
throw err
}
}
+12 -1
View File
@@ -10,6 +10,8 @@ import UserRESTControllerLib from './controller.js'
import Validators from '../middleware/validators.js'
import config from '../../../../config/index.js'
let _this
class UserRouter {
@@ -34,6 +36,7 @@ class UserRouter {
}
// Encapsulate dependencies.
this.config = config
this.userRESTController = new UserRESTControllerLib(dependencies)
this.validators = new Validators()
@@ -52,7 +55,7 @@ class UserRouter {
}
// Define the routes and attach the controller.
this.router.post('/', this.userRESTController.createUser)
this.router.post('/', this.createUser)
this.router.get('/', this.getAll)
this.router.get('/:id', this.getById)
this.router.put('/:id', this.updateUser)
@@ -63,6 +66,14 @@ class UserRouter {
app.use(this.router.allowedMethods())
}
async createUser (ctx, next) {
if (process.env.DISABLE_NEW_ACCOUNTS) {
await _this.validators.ensureAdmin(ctx, next)
}
await _this.userRESTController.createUser(ctx, next)
return true
}
async getAll (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.userRESTController.getUsers(ctx, next)
+5 -2
View File
@@ -1,6 +1,6 @@
/*
This Controller library is concerned with timer-based functions that are
kicked off periodicially.
kicked off periodically.
*/
import config from '../../config/index.js'
@@ -60,7 +60,7 @@ class TimerControllers {
clearInterval(this.gcOffersInt)
// clearInterval(this.checkDupOffers)
clearInterval(this.optimizeWalletHandle)
clearInterval(this.cleanusageHandle)
clearInterval(this.cleanUsageHandle)
}
// Garbage Collect the Orders.
@@ -114,6 +114,9 @@ class TimerControllers {
// Clean the usage state so that stats reflect the last 24 hours.
cleanUsage () {
try {
const now = new Date()
console.log(`cleanUsage() Timer Controller executing at ${now.toLocaleString()}`)
this.useCases.usage.cleanUsage()
return true
+4
View File
@@ -36,7 +36,11 @@ class UsageUseCases {
const now = new Date()
const twentyFourHoursAgo = now.getTime() - (60000 * 60 * 24)
console.log('cleanUsage() now: ', now)
console.log('cleanUsage() restCalls.length before filtering: ', restCalls.length)
restCalls = restCalls.filter(x => x.timestamp > twentyFourHoursAgo)
console.log('cleanUsage() restCalls.length after filtering: ', restCalls.length)
return restCalls
} catch (err) {
console.error('Error in usage-use-cases.js/cleanUsage()')
+53
View File
@@ -175,6 +175,59 @@ if (!config.noMongo) {
assert.equal(result.data.user.type, 'user')
assert.property(result.data.user, 'mnemonic')
})
it('should reject signup when DISABLE_NEW_ACCOUNTS is true', async () => {
try {
process.env.DISABLE_NEW_ACCOUNTS = true
const options = {
method: 'POST',
url: `${LOCALHOST}/users`,
data: {
email: 'test2@test.com',
password: 'supersecretpassword',
name: 'test3'
}
}
await axios(options)
assert(false, 'Unexpected result')
} catch (err) {
assert(err.response.status === 401, 'Error code 401 expected.')
}
})
it('admin can create a user when DISABLE_NEW_ACCOUNTS is true', async () => {
process.env.DISABLE_NEW_ACCOUNTS = true
const options = {
method: 'post',
url: `${LOCALHOST}/users`,
headers: {
Authorization: `Bearer ${context.adminJWT}`
},
data: {
user: {
email: 'fromAdmin@test.com',
password: 'supersecretpassword',
name: 'test3'
}
}
}
const result = await axios(options)
context.user = result.data.user
context.token = result.data.token
assert(result.status === 200, 'Status Code 200 expected.')
assert(
result.data.user.email === 'fromAdmin@test.com',
'Email of test expected'
)
assert(
result.data.user.password === undefined,
'Password expected to be omited'
)
assert.property(result.data, 'token', 'Token property exists.')
assert.equal(result.data.user.type, 'user')
})
})
describe('GET /users', () => {
+43 -28
View File
@@ -62,6 +62,7 @@ describe('Admin', () => {
describe('createSystemUser()', () => {
it('should create admin', async () => {
try {
await uut.deleteExistingSystemUser()
const result = await uut.createSystemUser()
assert.property(result, 'email')
@@ -72,44 +73,58 @@ describe('Admin', () => {
assert(false, 'Unexpected result')
}
})
it('should handle axios error', async () => {
it('should update admin password', async () => {
try {
const error1 = new Error('test error')
error1.response = {
status: 422
}
const error2 = new Error('test error')
error1.response = {
status: 500
}
// The loginAdmin() function in some use cases is recursive
// after handling the 422 error, it gets called again
sandbox
.stub(uut.axios, 'request')
.onFirstCall()
.throws(error1)
.onSecondCall()
.throws(error2)
uut.config.adminPassword = 'newpassword'
await uut.createSystemUser()
const fakeUser = {
password: 'oldpassword',
save: () => { return 'token' },
generateToken: () => { return 'token' }
}
sandbox.stub(uut.User, 'findOne').resolves(fakeUser)
const result = await uut.createSystemUser()
assert.property(result, 'email')
assert.property(result, 'password')
assert.property(result, 'id')
assert.property(result, 'token')
assert.equal(fakeUser.password, 'newpassword', 'password should be updated')
} catch (err) {
console.log(err)
assert(false, 'Unexpected result')
}
})
it('should handle error', async () => {
try {
sandbox.stub(uut.User, 'findOne').throws(new Error('test error'))
await uut.createSystemUser()
assert.fail('Unexpected result')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
it('should handle errors when remove user', async () => {
describe('deleteExistingSystemUser()', () => {
it('should delete admin', async () => {
try {
const error1 = new Error('test error')
error1.response = {
status: 422
}
sandbox.stub(uut.axios, 'request').throws(error1)
sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error'))
await uut.createSystemUser()
sandbox.stub(uut.User, 'deleteOne').resolves(true)
const result = await uut.deleteExistingSystemUser()
assert.isTrue(result)
} catch (err) {
assert(false, 'Unexpected result')
}
})
it('should handle error when deleting admin', async () => {
try {
sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error'))
await uut.deleteExistingSystemUser()
assert.fail('Unexpected result')
} catch (err) {
assert.include(err.message, 'test error')
}
+9 -1
View File
@@ -47,7 +47,15 @@ describe('#OrderPagination', () => {
timestamp: 'timestamp',
localTimestamp: 'localTimestamp',
txid: 'txid',
p2wdbHash: 'p2wdbHash'
p2wdbHash: 'p2wdbHash',
hdIndex: 0,
nostrEventId: 'nostrEventId',
dataType: 'dataType',
makerAddr: 'makerAddr',
rateInBaseUnit: 'rateInBaseUnit',
minUnitsToExchange: 'minUnitsToExchange',
ticker: 'ticker',
tokenType: 1
})
await order.save()
})
+138
View File
@@ -0,0 +1,138 @@
/*
Unit tests for the adapter/wallet.js library.
*/
// Global npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
import WalletAdapter from '../../../src/adapters/wallet.js'
import { MockBchWallet } from '../mocks/adapters/wallet.js'
describe('#wallet', () => {
let uut
let sandbox
beforeEach(() => {
uut = new WalletAdapter()
uut.bchWallet = new MockBchWallet()
uut.bitcoinJs = {
Transaction: {
fromHex: () => { return { } },
SIGHASH_ALL: () => { return { } }
},
TransactionBuilder: {
fromTransaction: () => { return { sign: () => { return { } }, build: () => { return { toHex: () => { return 'hex' } } } } }
},
ECPair: {
fromWIF: () => { return { } }
}
}
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#moveTokensFromCustomWallet', () => {
it('should move tokens from a custom wallet', async () => {
sandbox.stub(uut, 'getKeyPair').resolves({
cashAddress: 'cashAddress',
wif: 'wif',
hdIndex: 11
})
const customWallet = new MockBchWallet()
customWallet.sendTokens = () => { return 'move token tx id result' }
customWallet.utxos.utxoStore.slpUtxos.type1.tokens = [
{
tokenId: 'tokenId',
tokenType: 1,
qty: 1
}
]
const inObj = {
qty: 1,
wallet: customWallet,
tokenId: 'tokenId'
}
const result = await uut.moveTokensFromCustomWallet(inObj)
assert.equal(result.txid, 'move token tx id result')
assert.equal(result.hdIndex, 11)
assert.equal(result.tokenType, 1)
assert.equal(result.vout, 1)
})
it('should throw an error if the wallet is not provided', async () => {
try {
const inObj = {
qty: 1,
tokenId: 'tokenId'
}
await uut.moveTokensFromCustomWallet(inObj)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'wallet is required!')
}
})
it('should throw an error if the tokenId is not provided', async () => {
try {
const inObj = {
qty: 1,
wallet: new MockBchWallet()
}
await uut.moveTokensFromCustomWallet(inObj)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'tokenId must be a string!')
}
})
it('should throw an error if the qty is not provided', async () => {
try {
const inObj = {
tokenId: 'tokenId',
wallet: new MockBchWallet()
}
await uut.moveTokensFromCustomWallet(inObj)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'qty must be a number!')
}
})
})
describe('#completeTx', () => {
it('should complete a transaction', async () => {
sandbox.stub(uut, 'getKeyPair').resolves({
cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
wif: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ',
hdIndex: 11
})
sandbox.stub(uut, 'deseralizeTx').resolves({
txid: 'complete tx id result'
})
sandbox.stub(uut.retryQueue, 'addToQueue').resolves('complete tx id result')
const hex = 'hex'
const hdIndex = 11
const txid = await uut.completeTx(hex, hdIndex)
assert.equal(txid, 'complete tx id result')
})
it('should throw an error if the hex is not provided', async () => {
try {
const hdIndex = 11
await uut.completeTx(null, hdIndex)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'hex must be a string!')
}
})
it('should throw an error if the hdIndex is not provided', async () => {
try {
const hex = 'hex'
await uut.completeTx(hex, null)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'hdIndex must be a non-negative number!')
}
})
})
})
+6 -3
View File
@@ -1,6 +1,5 @@
/*
Unit tests for the use-cases/usage-use-cases.js business logic library.
Unit tests for the use-cases/usage-use-cases.js business logic library.
*/
// Public npm libraries
@@ -58,7 +57,7 @@ describe('#usage-use-case', () => {
// set older mock data
restCalls.push({
timestamp: now.getTime() - (60000 * 60 * 24),
timestamp: now.getTime() - (60000 * 60 * 48), // 48 hours ago
ip: '127.0.0.1'
})
@@ -88,6 +87,7 @@ describe('#usage-use-case', () => {
}
})
})
describe('#getRestSummary', () => {
it('should get the number of rest calls', () => {
// Set mock data
@@ -137,6 +137,7 @@ describe('#usage-use-case', () => {
assert.equal(result[0].ip, 'localhost')
assert.equal(result[0].cnt, '2')
})
it('should return a maximum of 20 values', () => {
// Fill Array with 21 values
for (let i = 0; i < 21; i++) {
@@ -154,6 +155,7 @@ describe('#usage-use-case', () => {
assert.equal(result.length, 20)
})
it('should handle error', () => {
try {
// Set mock data
@@ -194,6 +196,7 @@ describe('#usage-use-case', () => {
assert.equal(result[0].endpoint, 'GET /api/v1/users')
assert.equal(result[0].cnt, '2')
})
it('should return a maximum of 20 values', () => {
// Fill Array with 21 values
for (let i = 0; i < 21; i++) {
+3 -4
View File
@@ -1,8 +1,9 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/adapters/localdb/models/users.js'
const EMAIL = 'test@test.com'
const PASSWORD = 'pass'
const EMAIL = process.env.EMAIL || 'test@test3.com'
const PASSWORD = process.env.PASSWORD || 'pass'
async function addUser () {
// Connect to the Mongo Database.
@@ -13,8 +14,6 @@ async function addUser () {
{ useNewUrlParser: true, useUnifiedTopology: true }
)
const User = require('../../src/models/users')
const userData = {
email: EMAIL,
password: PASSWORD
+55
View File
@@ -0,0 +1,55 @@
/*
Use the system-user-dev.json file to log in as the admin user
and create a new user.
*/
// Global npm libraries
import axios from 'axios'
import fs from 'fs'
// Local libraries
// import config from '../../../config/index.js'
// import User from '../../../src/adapters/localdb/models/users.js'
// Customize these variables
const EMAIL = 'someone@somewhere.com'
const NAME = 'Someone'
const PASSWORD = 'test'
async function createUser () {
try {
// Open the system-user-dev.json file
const systemUser = JSON.parse(fs.readFileSync('../../../config/system-user-dev.json', 'utf8'))
// Log in as the admin user
const adminLogin = {
email: systemUser.email,
password: systemUser.password
}
const response = await axios.post('http://localhost:5700/auth', adminLogin)
console.log(`response: ${JSON.stringify(response.data, null, 2)}`)
const jwt = response.data.token
// Create a new user, using the admin credentials
const options = {
method: 'post',
url: 'http://localhost:5700/users/',
headers: {
Authorization: `Bearer ${jwt}`
},
data: {
user: {
email: EMAIL,
name: NAME,
password: PASSWORD
}
}
}
const result = await axios(options)
console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
} catch (err) {
console.error(err)
}
}
createUser()
@@ -1,6 +1,6 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/adapters/localdb/models/users.js'
import config from '../../../config/index.js'
import User from '../../../src/adapters/localdb/models/users.js'
async function getUsers () {
// Connect to the Mongo Database.
+57
View File
@@ -0,0 +1,57 @@
/*
Log in as an admin user to create a new user.
*/
// Global npm libraries
import axios from 'axios'
// import fs from 'fs'
// Local libraries
// import config from '../../../config/index.js'
// import User from '../../../src/adapters/localdb/models/users.js'
// Customize these variables
const EMAIL = 'someone@somewhere.com'
const NAME = 'Someone'
const PASSWORD = 'test'
const ADMIN_EMAIL = 'system@system.com'
const ADMIN_PASSWORD = 'admin'
async function createUser () {
try {
// Open the system-user-prod.json file
// const systemUser = JSON.parse(fs.readFileSync('../../../config/system-user-prod.json', 'utf8'))
// Log in as the admin user
const adminLogin = {
email: ADMIN_EMAIL,
password: ADMIN_PASSWORD
}
const response = await axios.post('http://localhost:5700/auth', adminLogin)
console.log(`response: ${JSON.stringify(response.data, null, 2)}`)
const jwt = response.data.token
// Create a new user, using the admin credentials
const options = {
method: 'post',
url: 'http://localhost:5700/users/',
headers: {
Authorization: `Bearer ${jwt}`
},
data: {
user: {
email: EMAIL,
name: NAME,
password: PASSWORD
}
}
}
const result = await axios(options)
console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
} catch (err) {
console.error(err)
}
}
createUser()
+21
View File
@@ -0,0 +1,21 @@
import mongoose from 'mongoose'
// import config from '../../config/index.js'
import User from '../../../src/adapters/localdb/models/users.js'
const mongooseConnectStr = 'mongodb://172.17.0.1:5666/bch-swap-service-prod'
async function getUsers () {
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
await mongoose.connect(
mongooseConnectStr,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
const users = await User.find({}, '-password')
console.log(`users: ${JSON.stringify(users, null, 2)}`)
mongoose.connection.close()
}
getUsers()
+83
View File
@@ -0,0 +1,83 @@
/*
This script will travers the HD wallet and sweep funds and tokens back
into the root address (index 0). That root address needs to have funds
to pay for the transactions.
This version will traverse the HD index of the wallet until it reaches
the value in the nextAddress property. This ensures that all UTXOs
that could be used by the wallet have been swept.
*/
// Public npm libraries
import BCHJS from '@psf/bch-js'
import BchTokenSweep from 'bch-token-sweep'
// Local libraries
import WalletAdapter from '../../src/adapters/wallet.js'
async function sweepFunds () {
try {
// Open the wallet files.
const wallet = new WalletAdapter()
const walletInfo = await wallet.openWallet()
const bchWallet = await wallet.instanceWallet(walletInfo)
console.log('walletInfo: ', walletInfo)
const lastIndex = walletInfo.nextAddress
const rootAddr = walletInfo.cashAddress
const rootWif = walletInfo.privateKey
console.log(`Sweeping all funds into root address ${rootAddr}...`)
// Generate an HD tree
const bchjs = new BCHJS()
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
let hdIndex = 1
do {
// Generate a keypair from the HD wallet.
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
const wifToSweep = bchjs.HDNode.toWIF(childNode)
console.log(`\nSweeping HD Index ${hdIndex} with address ${cashAddress}`)
try {
// Sweep tokens from address
const sweeper = new BchTokenSweep(
wifToSweep,
rootWif,
bchWallet,
550,
rootAddr
)
await sweeper.populateObjectFromNetwork()
const hex = await sweeper.sweepTo(rootAddr)
// console.log(`hex: ${hex}`)
const txid = await sweeper.blockchain.broadcast(hex)
// console.log('Transaction ID', txid)
console.log(`Swept HD index ${hdIndex}. TXID: ${txid}`)
// Wait between loop iterations.
await bchjs.Util.sleep(3000)
} catch (err) {
console.log(`error message with index ${hdIndex}: ${err}`)
}
hdIndex++
} while (hdIndex <= lastIndex)
console.log(`${lastIndex} empty addresses detected. Exiting.`)
console.log('\n\nDo not forget to reset the nextAddress property in the wallet.json file!\n\n')
} catch (err) {
console.error('Error in sweepFunds(): ', err)
}
}
sweepFunds()