mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa0ec3ece2 | ||
|
|
4f9f2f8370 | ||
|
|
0fb93be2cd | ||
|
|
26dcd6072e |
@@ -24,6 +24,7 @@ const Order = new mongoose.Schema({
|
||||
p2wdbTxid: { type: String },
|
||||
p2wdbHash: { type: String },
|
||||
makerAddr: { type: String, required: true },
|
||||
makerNpub: { type: String, required: true },
|
||||
|
||||
// Authentication data
|
||||
signature: { type: String },
|
||||
|
||||
@@ -7,6 +7,8 @@ import BchNostr from 'bch-nostr'
|
||||
import { RelayPool } from 'nostr'
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
import * as nip19 from 'nostr-tools/nip19'
|
||||
import { base58_to_binary as base58Tobinary } from 'base58-js'
|
||||
import { getPublicKey } from 'nostr-tools/pure'
|
||||
|
||||
class NostrAdapter {
|
||||
constructor (localConfig = { nostrRelay: '', nostrTopic: '' }) {
|
||||
@@ -44,6 +46,7 @@ class NostrAdapter {
|
||||
this.npub2pubkey = this.npub2pubkey.bind(this)
|
||||
this.readGlobalFeed = this.readGlobalFeed.bind(this)
|
||||
this.getFollowers = this.getFollowers.bind(this)
|
||||
this.privKeyToNpub = this.privKeyToNpub.bind(this)
|
||||
}
|
||||
|
||||
// Create nostr keys.
|
||||
@@ -247,6 +250,31 @@ class NostrAdapter {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a BCH private key (WIF) to a Nostr npub.
|
||||
privKeyToNpub (privKey) {
|
||||
try {
|
||||
if (!privKey || typeof privKey !== 'string') {
|
||||
throw new Error('privKey must be a string!')
|
||||
}
|
||||
// Extract the privaty key from the WIF, using this guide:
|
||||
// https://learnmeabitcoin.com/technical/keys/private-key/wif/
|
||||
const wifBuf = base58Tobinary(privKey)
|
||||
const privBuf = wifBuf.slice(1, 33)
|
||||
// console.log('privBuf: ', privBuf)
|
||||
|
||||
const nostrPubKey = getPublicKey(privBuf)
|
||||
// console.log('nostrPubKey: ', nostrPubKey)
|
||||
// Convert pubkey to npub
|
||||
const nostrNpub = nip19.npubEncode(nostrPubKey)
|
||||
// console.log('nostrNpub: ', nostrNpub)
|
||||
|
||||
return nostrNpub
|
||||
} catch (error) {
|
||||
console.log(`Error in nostr.js/privKeyToNpub() ${error.message} `)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NostrAdapter
|
||||
|
||||
@@ -28,6 +28,7 @@ class TimerControllers {
|
||||
this.backupUsageInterval = 60000 * 10 // 10 minutes
|
||||
this.newSmAccountsInterval = 60000 * 60 // 60 minutes
|
||||
this.updateSmAccountsInterval = 60000 * 10 // 10 minutes
|
||||
this.removeOlderDeletedChatsAndPostsInterval = 60000 * 60 * 1 // 1 hour
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
@@ -42,7 +43,7 @@ class TimerControllers {
|
||||
this.backupUsage = this.backupUsage.bind(this)
|
||||
this.newSmAccounts = this.newSmAccounts.bind(this)
|
||||
this.updateSmAccounts = this.updateSmAccounts.bind(this)
|
||||
|
||||
this.removeOlderDeletedChatsAndPosts = this.removeOlderDeletedChatsAndPosts.bind(this)
|
||||
// State
|
||||
this.gcOrdersInt = null
|
||||
this.gcOffersInt = null
|
||||
@@ -50,6 +51,7 @@ class TimerControllers {
|
||||
this.loadOffersInt = null
|
||||
this.newSmAccountsInt = null
|
||||
this.updateSmAccountsInt = null
|
||||
this.removeOlderDeletedChatsAndPostsInt = null
|
||||
}
|
||||
|
||||
// Start all the time-based controllers.
|
||||
@@ -62,6 +64,7 @@ class TimerControllers {
|
||||
this.backupUsageHandle = setInterval(this.backupUsage, this.backupUsageInterval)
|
||||
this.newSmAccountsInt = setInterval(this.newSmAccounts, this.newSmAccountsInterval)
|
||||
this.updateSmAccountsInt = setInterval(this.updateSmAccounts, this.updateSmAccountsInterval)
|
||||
this.removeOlderDeletedChatsAndPostsInt = setInterval(this.removeOlderDeletedChatsAndPosts, this.removeOlderDeletedChatsAndPostsInterval)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -74,6 +77,7 @@ class TimerControllers {
|
||||
clearInterval(this.backupUsageHandle)
|
||||
clearInterval(this.newSmAccountsInt)
|
||||
clearInterval(this.updateSmAccountsInt)
|
||||
clearInterval(this.removeOlderDeletedChatsAndPostsInt)
|
||||
}
|
||||
|
||||
// Garbage Collect the Orders.
|
||||
@@ -194,6 +198,22 @@ class TimerControllers {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Remove older deleted chats and posts.
|
||||
async removeOlderDeletedChatsAndPosts () {
|
||||
try {
|
||||
// console.log('removeOlderDeletedChatsAndPosts() Timer Controller executing at ', new Date().toLocaleString())
|
||||
clearInterval(this.removeOlderDeletedChatsAndPostsInt)
|
||||
await this.useCases.nostr.removeOlderDeletedChats()
|
||||
await this.useCases.nostr.removeOlderDeletedPosts()
|
||||
this.removeOlderDeletedChatsAndPostsInt = setInterval(this.removeOlderDeletedChatsAndPosts, this.removeOlderDeletedChatsAndPostsInterval)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in time-controller.js/removeOlderDeletedChatsAndPosts(): ', err)
|
||||
this.removeOlderDeletedChatsAndPostsInt = setInterval(this.removeOlderDeletedChatsAndPosts, this.removeOlderDeletedChatsAndPostsInterval)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TimerControllers
|
||||
|
||||
@@ -25,6 +25,8 @@ class NostrUseCases {
|
||||
this.getDeletedChats = this.getDeletedChats.bind(this)
|
||||
this.createDeletedPost = this.createDeletedPost.bind(this)
|
||||
this.getDeletedPosts = this.getDeletedPosts.bind(this)
|
||||
this.removeOlderDeletedChats = this.removeOlderDeletedChats.bind(this)
|
||||
this.removeOlderDeletedPosts = this.removeOlderDeletedPosts.bind(this)
|
||||
}
|
||||
|
||||
// Create a new deleted chat model and add it to the Mongo database.
|
||||
@@ -87,6 +89,42 @@ class NostrUseCases {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deleted chats that are older than 3 months.
|
||||
async removeOlderDeletedChats () {
|
||||
try {
|
||||
const deletedChats = await this.DeletedChatModel.find({})
|
||||
const threeMonthsAgo = new Date()
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3)
|
||||
for (let i = 0; i < deletedChats.length; i++) {
|
||||
const deletedChat = deletedChats[i]
|
||||
if (deletedChat.createdAt.getTime() < threeMonthsAgo.getTime()) {
|
||||
await deletedChat.remove()
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
wlogger.error('Error in lib/users.js/removeOlderDeletedChats()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deleted posts that are older than 3 months.
|
||||
async removeOlderDeletedPosts () {
|
||||
try {
|
||||
const deletedPosts = await this.DeletedPostModel.find({})
|
||||
const threeMonthsAgo = new Date()
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3)
|
||||
for (let i = 0; i < deletedPosts.length; i++) {
|
||||
const deletedPost = deletedPosts[i]
|
||||
if (deletedPost.createdAt.getTime() < threeMonthsAgo.getTime()) {
|
||||
await deletedPost.remove()
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
wlogger.error('Error in lib/users.js/removeOlderDeletedPosts()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NostrUseCases
|
||||
|
||||
@@ -116,6 +116,9 @@ class OrderLib {
|
||||
orderEntity.operatorAddress = this.config.operatorAddress
|
||||
orderEntity.operatorPercentage = this.config.operatorPercentage
|
||||
|
||||
// Convert the Seller's BCH private key to a Nostr npub. This is used to identify the seller in Nostr.
|
||||
orderEntity.makerNpub = this.adapters.nostr.privKeyToNpub(userWallet.walletInfo.privateKey)
|
||||
|
||||
// Post the new Order information to Nostr under the topic set in the
|
||||
// config file.
|
||||
const postObj = {
|
||||
|
||||
@@ -224,4 +224,33 @@ describe('#nostr.js', () => {
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
describe('#privKeyToNpub', () => {
|
||||
it('should handle error if providedprivKey is not a string', async () => {
|
||||
try {
|
||||
uut.privKeyToNpub()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'privKey must be a string!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return npub if privKey is valid', async () => {
|
||||
const result = uut.privKeyToNpub('L2kh34VinQNRq1ad8nnXnGoFrb9pT7j8GJXiT6Scsg18KMxsUVDr')
|
||||
// console.log('result: ', result)
|
||||
assert.isString(result)
|
||||
assert.include(result, 'npub')
|
||||
})
|
||||
it('should handle unexpected error', async () => {
|
||||
try {
|
||||
uut.privKeyToNpub('invalid format')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'Invalid base58 character')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -52,6 +52,7 @@ describe('#OrderPagination', () => {
|
||||
nostrEventId: 'nostrEventId',
|
||||
dataType: 'dataType',
|
||||
makerAddr: 'makerAddr',
|
||||
makerNpub: 'makerNpub',
|
||||
rateInBaseUnit: 'rateInBaseUnit',
|
||||
minUnitsToExchange: 'minUnitsToExchange',
|
||||
ticker: 'ticker',
|
||||
|
||||
@@ -187,4 +187,20 @@ describe('#Timer-Controllers', () => {
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
describe('#removeOlderDeletedChatsAndPosts', () => {
|
||||
it('should kick off the Use Cases and return true', async () => {
|
||||
sandbox.stub(uut.useCases.nostr, 'removeOlderDeletedChats').resolves()
|
||||
sandbox.stub(uut.useCases.nostr, 'removeOlderDeletedPosts').resolves()
|
||||
const result = await uut.removeOlderDeletedChatsAndPosts()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
it('should kick off the Use Cases and return false on error', async () => {
|
||||
sandbox.stub(uut.useCases.nostr, 'removeOlderDeletedChats').throws(new Error('test error'))
|
||||
sandbox.stub(uut.useCases.nostr, 'removeOlderDeletedPosts').throws(new Error('test error'))
|
||||
const result = await uut.removeOlderDeletedChatsAndPosts()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -240,7 +240,8 @@ const nostr = {
|
||||
read: async () => { return true },
|
||||
eventId2note: () => { return 'testNoteId' },
|
||||
readGlobalFeed: async () => { return [] },
|
||||
getFollowers: async () => { return [] }
|
||||
getFollowers: async () => { return [] },
|
||||
privKeyToNpub: async () => { return 'fakeNpub' }
|
||||
}
|
||||
|
||||
export default { ipfs, localdb, bch, wallet, p2wdb, bchjs, nostr}
|
||||
|
||||
@@ -148,7 +148,12 @@ class NostrUseCasesMock {
|
||||
async getDeletedPosts() {
|
||||
return []
|
||||
}
|
||||
|
||||
async removeOlderDeletedChats() {
|
||||
return true
|
||||
}
|
||||
async removeOlderDeletedPosts() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class UseCasesMock {
|
||||
|
||||
@@ -210,4 +210,78 @@ describe('#nostr-use-cases', () => {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
describe('#removeOlderDeletedChats', () => {
|
||||
it('should remove older deleted chats from the database', async () => {
|
||||
const threeMonthsAgo = new Date()
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3) // 3 months ago
|
||||
threeMonthsAgo.setDate(threeMonthsAgo.getDate() - 1) // 1 day ago
|
||||
const spy = sinon.spy()
|
||||
const deletedChatsMock = [
|
||||
// Newer deleted chat
|
||||
{
|
||||
eventId: 'note19e93faw4ffqepsqsrwrnstd3ee00nmzakwwuyfjm43dankgummfqms4p6q',
|
||||
createdAt: new Date(),
|
||||
remove: spy
|
||||
},
|
||||
// Older deleted chat
|
||||
{
|
||||
eventId: 'note19e93faw4ffqepsqsrwrnstd3ee00nmzakwwuyfjm43dankgummfqms4p6q',
|
||||
createdAt: threeMonthsAgo,
|
||||
remove: spy
|
||||
}
|
||||
]
|
||||
sandbox.stub(uut.DeletedChatModel, 'find').resolves(deletedChatsMock)
|
||||
await uut.removeOlderDeletedChats()
|
||||
assert.equal(spy.callCount, 1, 'Expected to be called once.')
|
||||
})
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Force an error.
|
||||
sandbox.stub(uut.DeletedChatModel, 'find').throws(new Error('test error'))
|
||||
|
||||
await uut.removeOlderDeletedChats()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('#removeOlderDeletedPosts', () => {
|
||||
it('should remove older deleted posts from the database', async () => {
|
||||
const threeMonthsAgo = new Date()
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3) // 3 months ago
|
||||
threeMonthsAgo.setDate(threeMonthsAgo.getDate() - 1) // 1 day ago
|
||||
const spy = sinon.spy()
|
||||
const deletedPostsMock = [
|
||||
// Newer deleted post
|
||||
{
|
||||
eventId: 'note19e93faw4ffqepsqsrwrnstd3ee00nmzakwwuyfjm43dankgummfqms4p6q',
|
||||
createdAt: new Date(),
|
||||
remove: spy
|
||||
},
|
||||
// Older deleted post
|
||||
{
|
||||
eventId: 'note19e93faw4ffqepsqsrwrnstd3ee00nmzakwwuyfjm43dankgummfqms4p6q',
|
||||
createdAt: threeMonthsAgo,
|
||||
remove: spy
|
||||
}
|
||||
]
|
||||
sandbox.stub(uut.DeletedPostModel, 'find').resolves(deletedPostsMock)
|
||||
await uut.removeOlderDeletedPosts()
|
||||
assert.equal(spy.callCount, 1, 'Expected to be called once.')
|
||||
})
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Force an error.
|
||||
sandbox.stub(uut.DeletedPostModel, 'find').throws(new Error('test error'))
|
||||
|
||||
await uut.removeOlderDeletedPosts()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user