mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-22 01:02:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
285456656c | ||
|
|
179d8f1656 | ||
|
|
94241c08ce | ||
|
|
abd4e872bf | ||
|
|
81521151f4 | ||
|
|
5a819c1d9c | ||
|
|
f2f3208fda | ||
|
|
7fe888cdda | ||
|
|
db3f1bf2cf | ||
|
|
5f64257dca | ||
|
|
2fbdccb883 | ||
|
|
ecc10afac3 | ||
|
|
4fc54aa9c5 | ||
|
|
2bd7f0c45c | ||
|
|
900423339e | ||
|
|
d907afa6bb | ||
|
|
9b116d82b7 | ||
|
|
1d1bf50ee3 | ||
|
|
5002004697 | ||
|
|
f311a3b9c6 | ||
|
|
fe91f4b875 | ||
|
|
2d9a2b811c | ||
|
|
514b65ed24 | ||
|
|
0c7f3088a8 | ||
|
|
12cab02300 | ||
|
|
04a908261a | ||
|
|
83898742f1 | ||
|
|
1795918719 | ||
|
|
6894217078 | ||
|
|
3ffbed27b5 | ||
|
|
b372e3d260 | ||
|
|
707b385b8a | ||
|
|
6cb140a491 | ||
|
|
c43ee8c25a | ||
|
|
0a7ec11510 | ||
|
|
f7aaba71d1 | ||
|
|
418a2e2ad2 | ||
|
|
a1023e8224 | ||
|
|
a32ed63d47 | ||
|
|
934fb46ffc | ||
|
|
b27136d03b | ||
|
|
60a3d67c0b | ||
|
|
a03c5d8c91 | ||
|
|
bcc90da49b | ||
|
|
b473a3803c | ||
|
|
9614f186b4 | ||
|
|
0390f2d956 | ||
|
|
ba5ead998d | ||
|
|
409a0f1049 | ||
|
|
bd2f27bb64 | ||
|
|
55d8d131cf | ||
|
|
5a6e3247e8 | ||
|
|
2a522775eb | ||
|
|
d677c83795 | ||
|
|
7cffd9f377 | ||
|
|
0a406cb9a4 | ||
|
|
f172a51016 | ||
|
|
00d53659b1 | ||
|
|
b0797d92f5 | ||
|
|
48484ed7af | ||
|
|
6a4aadde8a | ||
|
|
79e3aba26e | ||
|
|
bf61359bc7 | ||
|
|
a79d152993 | ||
|
|
ce7c3a0e2f | ||
|
|
e6b0580209 | ||
|
|
1f3c639100 | ||
|
|
e6efc4f49b | ||
|
|
dbbd321029 | ||
|
|
439dbc54ae | ||
|
|
547e59f52f | ||
|
|
a0d385de73 | ||
|
|
bec136009f | ||
|
|
98b7fa7734 | ||
|
|
d7bb476858 | ||
|
|
18c71c383f | ||
|
|
de85a933c4 | ||
|
|
16247372f1 | ||
|
|
c6c8b6418c | ||
|
|
10c4e7380f | ||
|
|
4933dce472 | ||
|
|
80c2822716 | ||
|
|
f48ff9a67e | ||
|
|
4bd2385359 | ||
|
|
be71492773 | ||
|
|
bcb70edf2b | ||
|
|
07071032bc | ||
|
|
f476e119a2 | ||
|
|
0fed232428 | ||
|
|
0273510485 | ||
|
|
a62dee37f4 | ||
|
|
8d6af3e1b7 | ||
|
|
ee0f2da641 | ||
|
|
44499d9652 | ||
|
|
365a5dcd4b | ||
|
|
7f5b456018 | ||
|
|
c7217486bd | ||
|
|
050141d31e | ||
|
|
b697f37199 | ||
|
|
ceb58caba1 | ||
|
|
2724b500ee | ||
|
|
1b84e7beb8 | ||
|
|
383900d8b5 | ||
|
|
ba806d86b5 | ||
|
|
4d7c7350a0 | ||
|
|
adc6500aed | ||
|
|
eb9fce73eb | ||
|
|
ec3ed165e4 | ||
|
|
d41d20cc81 | ||
|
|
5f965e7146 | ||
|
|
f382bb4578 | ||
|
|
ba37f87f80 | ||
|
|
eaa9c23f55 | ||
|
|
ac3730dbd9 | ||
|
|
581b8eb626 | ||
|
|
cd4ec2711e | ||
|
|
e9e3dbad0d | ||
|
|
295b361431 | ||
|
|
ad60df9d51 | ||
|
|
6ae635c21d | ||
|
|
61807a2f3d | ||
|
|
16ba5a8980 | ||
|
|
99b817549e | ||
|
|
60a8c335c8 | ||
|
|
1783768c32 | ||
|
|
b3630eaa06 | ||
|
|
d691016911 | ||
|
|
6506aead8c | ||
|
|
ddfc83d026 | ||
|
|
fe42c1e0e3 | ||
|
|
928a211a63 | ||
|
|
8bff69a4ca | ||
|
|
5fced7d501 | ||
|
|
7cdf622ad6 | ||
|
|
afd8ceac7b | ||
|
|
5d63db3662 | ||
|
|
9f6cd7dbf1 | ||
|
|
0725aa3ada | ||
|
|
3eb88d4076 | ||
|
|
1c61f150b1 | ||
|
|
4c74b287d8 | ||
|
|
264d762f74 | ||
|
|
3c02461c6b | ||
|
|
a30f2be51f | ||
|
|
0690a9fb65 | ||
|
|
4fa103765e | ||
|
|
b0d6ce40a9 | ||
|
|
c57a3d935b | ||
|
|
82b5943601 | ||
|
|
41b76967f5 | ||
|
|
e4c2ee44c1 | ||
|
|
c05f022995 | ||
|
|
7723c3a201 | ||
|
|
8e338818ca | ||
|
|
ea51aa420c | ||
|
|
bc410678b4 | ||
|
|
a42c15c969 | ||
|
|
a3102a0278 | ||
|
|
f4f142dc48 | ||
|
|
e03a70c9a3 | ||
|
|
d47b8797d4 | ||
|
|
56ba0aad02 | ||
|
|
825bc6d2de | ||
|
|
18fe63a5ad | ||
|
|
d6e0e7b903 | ||
|
|
8759e1af5e | ||
|
|
b78f3fb08b | ||
|
|
5fa0b2e1c8 | ||
|
|
6a88d0559a | ||
|
|
314fdc2bb0 | ||
|
|
acd7154d41 | ||
|
|
63a1bea60d | ||
|
|
077c60bd34 | ||
|
|
6485537207 | ||
|
|
286f079679 | ||
|
|
075818d551 | ||
|
|
b0f42d613b | ||
|
|
8e5b87f142 | ||
|
|
92483ee5e1 | ||
|
|
a80b82da7a | ||
|
|
41d2aaa16d | ||
|
|
9de2caba94 | ||
|
|
a45fb3ad08 | ||
|
|
666f954529 | ||
|
|
7d2e02fe64 | ||
|
|
9912ed7af7 | ||
|
|
441d898ae7 | ||
|
|
553a7ff86a | ||
|
|
349d070273 | ||
|
|
b6dcb9c177 | ||
|
|
8aff3a6568 | ||
|
|
9e885b8bc3 | ||
|
|
d6b924a5f4 | ||
|
|
97691a0dd9 | ||
|
|
3781af6305 | ||
|
|
cb5d5a603d | ||
|
|
81713bfebe | ||
|
|
b9aeb8faa0 | ||
|
|
a4e75cdccc | ||
|
|
f74c94ef50 | ||
|
|
bfb51fdcc6 | ||
|
|
25aaf4146f | ||
|
|
8ee10f2f8c | ||
|
|
bf96821c2c | ||
|
|
9787806427 | ||
|
|
9043aab432 | ||
|
|
58989a81dc | ||
|
|
82acd623d8 | ||
|
|
e4fe8cc492 | ||
|
|
0f8a6b9299 | ||
|
|
4f2c87bbd4 | ||
|
|
66b8e6b263 | ||
|
|
cabff83222 | ||
|
|
d94b527780 | ||
|
|
f0e8a05f08 | ||
|
|
278b4127e1 | ||
|
|
87b3d7519c | ||
|
|
ed4e75c9ad | ||
|
|
b0cc5a6e0b | ||
|
|
6b4139a29e | ||
|
|
925242262a | ||
|
|
74fe77009c | ||
|
|
51f7905dad | ||
|
|
0b1d3e63f6 | ||
|
|
549fd4fd5a | ||
|
|
846ee21c48 | ||
|
|
6654836440 | ||
|
|
a25a13673e | ||
|
|
dd7db3814a | ||
|
|
fdb3b9c3ac | ||
|
|
30eed1ffb1 | ||
|
|
7884cffdc6 | ||
|
|
c1a8210fa7 | ||
|
|
9f3b482f57 | ||
|
|
9487027852 | ||
|
|
9065900608 | ||
|
|
2da319f896 | ||
|
|
449a98d80c | ||
|
|
2343120fcf | ||
|
|
2d4fa523cd | ||
|
|
c8cc3adbc5 | ||
|
|
169e0ad3d2 | ||
|
|
e20a0a5d27 | ||
|
|
6dda29bb14 | ||
|
|
dd662e9fe9 | ||
|
|
9b775c5bf6 | ||
|
|
2047673862 | ||
|
|
5b545aebeb | ||
|
|
1e75816125 | ||
|
|
455fd807a4 | ||
|
|
8b75ac0f32 | ||
|
|
f1a5ae70e2 | ||
|
|
46bd532eeb | ||
|
|
6f99b215cd | ||
|
|
3482540fc8 | ||
|
|
fb999c7bd4 | ||
|
|
a09e5a5ba7 | ||
|
|
ba14b97258 | ||
|
|
79ac3729c9 | ||
|
|
bd77754686 | ||
|
|
b3b5322226 | ||
|
|
ec1f950ffb | ||
|
|
b18bc51885 | ||
|
|
e8c35dd1f3 | ||
|
|
8be0accdca | ||
|
|
94c56a06b7 | ||
|
|
42f64e3304 | ||
|
|
cc9bbc3c5d | ||
|
|
0c6696875b | ||
|
|
37b2e426a5 | ||
|
|
f3713985ed | ||
|
|
4ed9994414 | ||
|
|
140707ce6b | ||
|
|
c289e82b0e | ||
|
|
84210d07c4 | ||
|
|
6d285c77cd | ||
|
|
7cefbd71ef | ||
|
|
85d6aa7d46 | ||
|
|
aaee25389f | ||
|
|
ce83ddcadf | ||
|
|
1b961905e0 | ||
|
|
e757bd0fe0 | ||
|
|
3da78ce46b | ||
|
|
c9d1d79e11 | ||
|
|
ce118069eb | ||
|
|
f8dce21d95 | ||
|
|
e8104a8a12 | ||
|
|
2c43793bf2 | ||
|
|
c13a5d1593 | ||
|
|
6d7b3db370 | ||
|
|
33d06e99e8 | ||
|
|
3203e56a4d | ||
|
|
ff47f1b356 | ||
|
|
d9377ea78b | ||
|
|
d266bcc54b | ||
|
|
a033fb1042 | ||
|
|
b0eda392a6 | ||
|
|
f81d6853fc | ||
|
|
02a889385d | ||
|
|
f17b21bee8 | ||
|
|
78b9e4fcb1 | ||
|
|
eb1bee1b61 | ||
|
|
611c2d1631 | ||
|
|
cb9f5af62e | ||
|
|
2fca908c97 | ||
|
|
3aba1e061b | ||
|
|
c398942c95 | ||
|
|
c2bffd8c4e | ||
|
|
ac97831721 | ||
|
|
02eab8686c | ||
|
|
1c1169dd93 | ||
|
|
0896adffbb | ||
|
|
56e0f7e8df | ||
|
|
34841f53db | ||
|
|
c27209cceb | ||
|
|
5a0fc31c7a | ||
|
|
4701f245b9 | ||
|
|
daa06a57ed | ||
|
|
b3157a04ed | ||
|
|
3753db46f6 | ||
|
|
b924449d02 | ||
|
|
569dcbd576 | ||
|
|
b0a2d73540 | ||
|
|
0d4c8cd56e | ||
|
|
b37dae60d1 | ||
|
|
448b028bb4 | ||
|
|
dd8d992b8a | ||
|
|
4ed657ae28 | ||
|
|
e14633db79 | ||
|
|
6a4e749218 | ||
|
|
7a5036e3aa | ||
|
|
cca10452c0 | ||
|
|
309ea0774d | ||
|
|
c2b68a0787 | ||
|
|
2bb4cd7cae | ||
|
|
9ca581e998 | ||
|
|
02adbd5634 | ||
|
|
da637df44a | ||
|
|
9315e6f166 | ||
|
|
7e965e8415 | ||
|
|
a5916c95f9 | ||
|
|
708f97d107 | ||
|
|
637738bfb9 | ||
|
|
d8e82c5ad2 | ||
|
|
b23acd3cdc | ||
|
|
c71fda38ff | ||
|
|
f99f49d840 | ||
|
|
2edd924c57 | ||
|
|
689c8fd8d9 | ||
|
|
ffd6c452c9 | ||
|
|
fc8e160874 | ||
|
|
d2f83307e6 | ||
|
|
1a09fcd26f | ||
|
|
db54a067f4 | ||
|
|
9fcee20498 | ||
|
|
0a04ee61ef | ||
|
|
7cae06dd65 | ||
|
|
96064ac9a7 | ||
|
|
3690b749fe | ||
|
|
e2c5821dbe | ||
|
|
9967d5145b | ||
|
|
9d16291855 | ||
|
|
1518d1a12b | ||
|
|
6dbf284876 | ||
|
|
d1e96505c8 | ||
|
|
1e80a3159e | ||
|
|
d3df9546a7 |
@@ -69,5 +69,9 @@ ipfs-service-provider.sh
|
||||
run-dev.sh
|
||||
wallet.json
|
||||
data/
|
||||
run-dev.sh
|
||||
*.zip
|
||||
|
||||
!README.md
|
||||
|
||||
wallet.json
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# Permissionless Software Foundation Community Contributing Guide 1.0
|
||||
|
||||
This document describes a very simple process suitable for most projects under
|
||||
the PSF umbrella. It is based on [this Medium article](https://medium.com/the-node-js-collection/healthy-open-source-967fa8be7951) and the [Node.js Community Contribution Guide](https://github.com/nodejs/TSC/blob/master/BasePolicies/CONTRIBUTING.md).
|
||||
Projects are encouraged to adopt this whether they
|
||||
are hosted under the PSF or not.
|
||||
|
||||
The goal of this document is to create a contribution process that:
|
||||
|
||||
* Encourages new contributions.
|
||||
* Encourages contributors to remain involved.
|
||||
* Avoids unnecessary processes and bureaucracy whenever possible.
|
||||
* Creates a transparent decision making process which makes it clear how
|
||||
contributors can be involved in decision making.
|
||||
|
||||
This document is based on much prior art in the Node.js community, io.js,
|
||||
and the Node.js project.
|
||||
|
||||
Additional guidance can be found at the [Permissionless Software Foundation Telegram Channel](https://t.me/permissionless_software).
|
||||
|
||||
## Vocabulary
|
||||
|
||||
* A **Contributor** is any individual creating or commenting on an issue or pull request.
|
||||
* A **Committer** is a subset of contributors who have been given write access to the repository.
|
||||
* A **TC (Technical Committee)** is a group of committers representing the required technical
|
||||
expertise to resolve rare disputes.
|
||||
|
||||
# Logging Issues
|
||||
|
||||
Log an issue for any question or problem you might have. When in doubt, log an issue,
|
||||
any additional policies about what to include will be provided in the responses. The only
|
||||
exception is security disclosures which should be sent privately.
|
||||
|
||||
Committers may direct you to another repository, ask for additional clarifications, and
|
||||
add appropriate metadata before the issue is addressed.
|
||||
|
||||
Please be courteous, respectful, and every participant is expected to follow the
|
||||
project's Code of Conduct.
|
||||
|
||||
# Contributions
|
||||
|
||||
Any change to resources in this repository must be through pull requests. This applies to all changes
|
||||
to documentation, code, binary files, etc. Even long term committers and TC members must use
|
||||
pull requests.
|
||||
|
||||
No pull request can be merged without being reviewed.
|
||||
|
||||
For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that
|
||||
contributors in other timezones have time to review. Consideration should also be given to
|
||||
weekends and other holiday periods to ensure active committers all have reasonable time to
|
||||
become involved in the discussion and review process if they wish.
|
||||
|
||||
The default for each contribution is that it is accepted once no committer has an objection.
|
||||
During review committers may also request that a specific contributor who is most versed in a
|
||||
particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off"
|
||||
process for contributions to land. Once all issues brought by committers are addressed it can
|
||||
be landed by any committer.
|
||||
|
||||
In the case of an objection being raised in a pull request by another committer, all involved
|
||||
committers should seek to arrive at a consensus by way of addressing concerns being expressed
|
||||
by discussion, compromise on the proposed change, or withdrawal of the proposed change.
|
||||
|
||||
If a contribution is controversial and committers cannot agree about how to get it to land
|
||||
or if it should land then it should be escalated to the TC. TC members should regularly
|
||||
discuss pending contributions in order to find a resolution. It is expected that only a
|
||||
small minority of issues be brought to the TC for resolution and that discussion and
|
||||
compromise among committers be the default resolution mechanism.
|
||||
|
||||
# Becoming a Committer
|
||||
|
||||
All contributors who land a non-trivial contribution should be on-boarded in a timely manner,
|
||||
and added as a committer, and be given write access to the repository.
|
||||
|
||||
Committers are expected to follow this policy and continue to send pull requests, go through
|
||||
proper review, and have other committers merge their pull requests.
|
||||
|
||||
# TC Process
|
||||
|
||||
The TC uses a "consensus seeking" process for issues that are escalated to the TC.
|
||||
The group tries to find a resolution that has no open objections among TC members.
|
||||
If a consensus cannot be reached that has no objections then a majority wins vote
|
||||
is called. It is also expected that the majority of decisions made by the TC are via
|
||||
a consensus seeking process and that voting is only used as a last-resort.
|
||||
|
||||
Resolution may involve returning the issue to committers with suggestions on how to
|
||||
move forward towards a consensus. It is not expected that a meeting of the TC
|
||||
will resolve all issues on its agenda during that meeting and may prefer to continue
|
||||
the discussion happening among the committers.
|
||||
|
||||
Members can be added to the TC at any time. Any committer can nominate another committer
|
||||
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
|
||||
not to add this new member. Members who do not participate consistently at the level of
|
||||
a majority of the other members are expected to resign.
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2022 Permissionless Software Foundation
|
||||
Copyright (c) 2021-2024 Permissionless Software Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Pedigree
|
||||
|
||||
This repository was forked from [koa-api-boilerplate](https://github.com/christroutner/koa-api-boilerplate). Code changes from that upstream repository is frequently pulled in and merged to this repository.
|
||||
+55
-53
@@ -8,31 +8,26 @@
|
||||
*/
|
||||
|
||||
// npm libraries
|
||||
const Koa = require('koa')
|
||||
const bodyParser = require('koa-bodyparser')
|
||||
const convert = require('koa-convert')
|
||||
const logger = require('koa-logger')
|
||||
const mongoose = require('mongoose')
|
||||
const session = require('koa-generic-session')
|
||||
const passport = require('koa-passport')
|
||||
const mount = require('koa-mount')
|
||||
const serve = require('koa-static')
|
||||
const cors = require('kcors')
|
||||
import Koa from 'koa'
|
||||
|
||||
import bodyParser from 'koa-bodyparser'
|
||||
import convert from 'koa-convert'
|
||||
import logger from 'koa-logger'
|
||||
import mongoose from 'mongoose'
|
||||
import session from 'koa-generic-session'
|
||||
import passport from 'koa-passport'
|
||||
import mount from 'koa-mount'
|
||||
import serve from 'koa-static'
|
||||
import cors from 'kcors'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../config') // this first.
|
||||
const AdminLib = require('../src/adapters/admin')
|
||||
// const adminLib = new AdminLib()
|
||||
import config from '../config/index.js' // this first.
|
||||
|
||||
const WebHookLib = require('../src/adapters/webhook')
|
||||
const webhookLib = new WebHookLib()
|
||||
|
||||
// const JSONRPC = require('../src/rpc')
|
||||
// const rpc = new JSONRPC()
|
||||
|
||||
const errorMiddleware = require('../src/controllers/rest-api/middleware/error')
|
||||
// const { wlogger } = require('../src/adapters/wlogger')
|
||||
const Controllers = require('../src/controllers')
|
||||
import AdminLib from '../src/adapters/admin.js'
|
||||
import errorMiddleware from '../src/controllers/rest-api/middleware/error.js'
|
||||
// import wlogger from '../src/adapters/wlogger.js'
|
||||
import Controllers from '../src/controllers/index.js'
|
||||
import { applyPassportMods } from '../config/passport.js'
|
||||
|
||||
class Server {
|
||||
constructor () {
|
||||
@@ -50,16 +45,18 @@ class Server {
|
||||
const app = new Koa()
|
||||
app.keys = [this.config.session]
|
||||
|
||||
// Connect to the Mongo Database.
|
||||
this.mongoose.Promise = global.Promise
|
||||
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${this.config.database}`
|
||||
)
|
||||
await this.mongoose.connect(this.config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
if (!this.config.noMongo) {
|
||||
// Connect to the Mongo Database.
|
||||
this.mongoose.Promise = global.Promise
|
||||
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
|
||||
console.log(
|
||||
`Connecting to MongoDB with this connection string: ${this.config.database}`
|
||||
)
|
||||
await this.mongoose.connect(this.config.database, {
|
||||
useUnifiedTopology: true,
|
||||
useNewUrlParser: true
|
||||
})
|
||||
}
|
||||
|
||||
console.log(`Starting environment: ${this.config.env}`)
|
||||
console.log(`Debug level: ${this.config.debugLevel}\n`)
|
||||
@@ -81,7 +78,8 @@ class Server {
|
||||
app.use(mount('/logs', serve(`${process.cwd()}/config/logs`)))
|
||||
|
||||
// User Authentication
|
||||
require('../config/passport')
|
||||
// require('../config/passport')
|
||||
applyPassportMods(passport)
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
|
||||
@@ -103,32 +101,36 @@ class Server {
|
||||
|
||||
// MIDDLEWARE END
|
||||
|
||||
// 7/7/22 CT: This code paragraph can be deleted
|
||||
// Delay startup to give the P2WDB time to start first, so that it accepts the webook call
|
||||
await this.sleep(20000)
|
||||
|
||||
// if (this.config.env !== 'test') {
|
||||
// await this.sleep(20000)
|
||||
// }
|
||||
// Create webhook
|
||||
try {
|
||||
try {
|
||||
// Delete an old webhook if it exists.
|
||||
await webhookLib.deleteWebhook(config.webhookTarget)
|
||||
} catch (err) {
|
||||
/* exit quietly */
|
||||
// console.log('err deleting webhook: ', err)
|
||||
}
|
||||
|
||||
await webhookLib.createWebhook(config.webhookTarget)
|
||||
console.log('Webhook created')
|
||||
} catch (error) {
|
||||
console.log('Webhook cant be created')
|
||||
}
|
||||
// try {
|
||||
// try {
|
||||
// // Delete an old webhook if it exists.
|
||||
// await webhookLib.deleteWebhook(config.webhookTarget)
|
||||
// } catch (err) {
|
||||
// /* exit quietly */
|
||||
// // console.log('err deleting webhook: ', err)
|
||||
// }
|
||||
//
|
||||
// await webhookLib.createWebhook(config.webhookTarget)
|
||||
// console.log('Webhook created')
|
||||
// } catch (error) {
|
||||
// console.log('Webhook cant be created')
|
||||
// }
|
||||
|
||||
// startServer()
|
||||
this.server = await app.listen(config.port)
|
||||
console.log(`Server started on ${config.port}`)
|
||||
|
||||
// Create the system admin user.
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
if (!this.config.noMongo) {
|
||||
// Create the system admin user.
|
||||
const success = await this.adminLib.createSystemUser()
|
||||
if (success) console.log('System admin user created.')
|
||||
}
|
||||
|
||||
// Attach the other IPFS controllers.
|
||||
// Skip if this is a test environment.
|
||||
@@ -159,4 +161,4 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Server
|
||||
export default Server
|
||||
|
||||
Vendored
+85
-14
@@ -1,19 +1,31 @@
|
||||
/*
|
||||
This file is used to store unsecure, application-specific data common to all
|
||||
environments.
|
||||
|
||||
Additional Environent Variables:
|
||||
- CONNECT_PREF: should have a value of 'cr' (default), or 'direct'. This is
|
||||
used by helia-coord to select a connection preference between peers. Servers
|
||||
with an ip4 or ip6 address should use 'direct'.
|
||||
*/
|
||||
|
||||
/* eslint no-unneeded-ternary:0 */
|
||||
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
|
||||
// Get the version from the package.json file.
|
||||
const pkgInfo = require('../../package.json')
|
||||
import { readFileSync } from 'fs'
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
const pkgInfo = JSON.parse(readFileSync(`${__dirname.toString()}/../../package.json`))
|
||||
|
||||
const version = pkgInfo.version
|
||||
|
||||
const ipfsCoordName = process.env.COORD_NAME
|
||||
? process.env.COORD_NAME
|
||||
: 'ipfs-torlist-service-generic'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
// Configure TCP port.
|
||||
port: process.env.PORT || 5700,
|
||||
|
||||
@@ -40,23 +52,70 @@ module.exports = {
|
||||
|
||||
// P2WDB URL that will accept API calls from the p2wdb npm library.
|
||||
p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'https://p2wdb.fullstack.cash',
|
||||
// p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'http://localhost:5010',
|
||||
|
||||
// Enable or Disable the usage of Mongo DB.
|
||||
noMongo: process.env.NO_MONGO ? true : false,
|
||||
|
||||
// BEGIN WALLET CONFIGURATION
|
||||
|
||||
// BCH Mnemonic for generating encryption keys and payment address
|
||||
mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '',
|
||||
|
||||
// (Optional) use a wallet file generated by psf-bch-wallet.
|
||||
walletFile: process.env.WALLET_FILE
|
||||
? process.env.WALLET_FILE
|
||||
: `${__dirname.toString()}/../../wallet.json`,
|
||||
|
||||
// Wallet Configuration
|
||||
// Default value: web3 - connect to bch-api through web 3 JSON RPC over IPFS.
|
||||
// Alternate value: web2 - connect to bch-api through a web 2 REST API over HTTP.
|
||||
walletInterface: process.env.WALLET_INTERFACE ? process.env.WALLET_INTERFACE : 'web3',
|
||||
|
||||
// Wallet API Server
|
||||
// Default web2 server: https://api.fullstack.cash/v5/
|
||||
// Default web3 server: https://free-bch.fullstack.cash/
|
||||
apiServer: process.env.WALLET_INTERFACE === 'web2'
|
||||
? (
|
||||
process.env.APISERVER
|
||||
? process.env.APISERVER
|
||||
: 'https://api.fullstack.cash/v5/'
|
||||
)
|
||||
: 'https://free-bch.fullstack.cash/',
|
||||
|
||||
// Basic Authentication Password for connecting to bch-api through a web 2
|
||||
// REST API over HTTP.
|
||||
authPass: process.env.WALLET_AUTH_PASS
|
||||
? process.env.WALLET_AUTH_PASS
|
||||
: '',
|
||||
|
||||
// FullStack.cash account information, used for automatic JWT handling.
|
||||
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
|
||||
authServer: process.env.AUTHSERVER
|
||||
? process.env.AUTHSERVER
|
||||
: 'https://auth.fullstack.cash',
|
||||
apiServer: process.env.APISERVER
|
||||
? process.env.APISERVER
|
||||
: 'https://api.fullstack.cash/v5/',
|
||||
fullstackLogin: process.env.FULLSTACKLOGIN
|
||||
? process.env.FULLSTACKLOGIN
|
||||
: 'demo@demo.com',
|
||||
fullstackPassword: process.env.FULLSTACKPASS
|
||||
? process.env.FULLSTACKPASS
|
||||
: 'demo',
|
||||
// authPass: process.env.FULLSTACK_AUTH_PASS
|
||||
// ? process.env.FULLSTACK_AUTH_PASS
|
||||
// : '',
|
||||
|
||||
// END WALLET CONFIGURATION
|
||||
|
||||
// BEGIN IPFS CONFIGURATION
|
||||
|
||||
// Debug verbosity level of helia-coord.
|
||||
// 0 = no debug logs. 3 = maximum logs.
|
||||
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 2,
|
||||
|
||||
// Enable/Disable the IPFS node at startup. Enabled by default.
|
||||
// useIpfs: process.env.DISABLE_IPFS ? false : true,
|
||||
useIpfs: false,
|
||||
|
||||
// IPFS settings.
|
||||
isCircuitRelay: process.env.ENABLE_CIRCUIT_RELAY ? true : false,
|
||||
// SSL domain used for websocket connection via browsers.
|
||||
crDomain: process.env.CR_DOMAIN ? process.env.CR_DOMAIN : '',
|
||||
@@ -77,7 +136,10 @@ module.exports = {
|
||||
'@type': 'Organization',
|
||||
name: 'Permissionless Software Foundation',
|
||||
url: 'https://PSFoundation.cash'
|
||||
}
|
||||
},
|
||||
|
||||
// If this node has an IP4 address or domain name used to provide a REST API.
|
||||
web2Api: process.env.WEB2_API ? process.env.WEB2_API : null
|
||||
},
|
||||
|
||||
// P2WDB webhook endpoint
|
||||
@@ -94,17 +156,26 @@ module.exports = {
|
||||
ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 4001,
|
||||
ipfsWsPort: process.env.IPFS_WS_PORT ? process.env.IPFS_WS_PORT : 4003,
|
||||
|
||||
// BCH Mnemonic for generating encryption keys and payment address
|
||||
mnemonic: process.env.MNEMONIC ? process.env.MNEMONIC : '',
|
||||
|
||||
debugLevel: process.env.DEBUG_LEVEL ? parseInt(process.env.DEBUG_LEVEL) : 1,
|
||||
|
||||
// Settings for production, using external go-ipfs node.
|
||||
isProduction: process.env.SVC_ENV === 'production' ? true : false,
|
||||
isProduction: process.env.SVC_ENV === 'prod' ? true : false,
|
||||
ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost',
|
||||
ipfsApiPort: process.env.IPFS_API_PORT
|
||||
? parseInt(process.env.IPFS_API_PORT)
|
||||
: 5001,
|
||||
|
||||
chatPubSubChan: 'psf-ipfs-chat-001'
|
||||
chatPubSubChan: 'psf-ipfs-chat-001',
|
||||
|
||||
// IPFS gateway
|
||||
ipfsGateway: process.env.IPFS_GATEWAY ? process.env.IPFS_GATEWAY : 'https://p2wdb-gateway-678.fullstack.cash/ipfs/',
|
||||
|
||||
// This can add specific Circuit Relay v2 servers to connect to.
|
||||
bootstrapRelays: [
|
||||
// v2 Circuit Relay (Token Tiger)
|
||||
// '/ip4/137.184.93.145/tcp/8001/p2p/12D3KooWGMEKkdJfyZbwdH9EafZbRTtMn7FnhWPrE4MhRty2763g',
|
||||
|
||||
// v2 Circuit Relay server (FullStack.cash)
|
||||
// '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL'
|
||||
]
|
||||
|
||||
// END IPFS CONFIGURATION
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
||||
specified.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
session: 'secret-boilerplate-token',
|
||||
token: 'secret-jwt-token',
|
||||
database: 'mongodb://localhost:27017/bch-swap-service-dev',
|
||||
|
||||
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
so as not to conflict with the default host port of 27017 for MongoDB.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
session: 'secret-boilerplate-token',
|
||||
token: 'secret-jwt-token',
|
||||
// database: 'mongodb://172.17.0.1:5555/ipfs-service-prod',
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@
|
||||
This is the environment run by the test suite.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
session: 'secret-boilerplate-token',
|
||||
token: 'secret-jwt-token',
|
||||
database: 'mongodb://localhost:27017/bch-swap-service-test',
|
||||
|
||||
+14
-3
@@ -1,6 +1,17 @@
|
||||
const common = require('./env/common')
|
||||
import common from './env/common.js'
|
||||
|
||||
import development from './env/development.js'
|
||||
import production from './env/production.js'
|
||||
import test from './env/test.js'
|
||||
|
||||
const env = process.env.BCH_DEX || 'development'
|
||||
const config = require(`./env/${env}`)
|
||||
console.log(`Loading config for this environment: ${env}`)
|
||||
|
||||
module.exports = Object.assign({}, common, config)
|
||||
let config = development
|
||||
if (env === 'test') {
|
||||
config = test
|
||||
} else if (env === 'prod') {
|
||||
config = production
|
||||
}
|
||||
|
||||
export default Object.assign({}, common, config)
|
||||
|
||||
+32
-29
@@ -1,30 +1,6 @@
|
||||
const passport = require('koa-passport')
|
||||
const User = require('../src/adapters/localdb/models/users')
|
||||
const Strategy = require('passport-local')
|
||||
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user.id)
|
||||
})
|
||||
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
try {
|
||||
const user = await User.findById(id, '-password')
|
||||
done(null, user)
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
|
||||
passport.use(
|
||||
'local',
|
||||
new Strategy(
|
||||
{
|
||||
usernameField: 'email',
|
||||
passwordField: 'password'
|
||||
},
|
||||
passportCallback
|
||||
)
|
||||
)
|
||||
// import passport from 'koa-passport';
|
||||
import User from '../src/adapters/localdb/models/users.js'
|
||||
import Strategy from 'passport-local'
|
||||
|
||||
async function passportCallback (email, password, done) {
|
||||
try {
|
||||
@@ -49,5 +25,32 @@ async function passportCallback (email, password, done) {
|
||||
}
|
||||
}
|
||||
|
||||
// For testing
|
||||
module.exports = { passport, passportCallback }
|
||||
function applyPassportMods (passport) {
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user.id)
|
||||
})
|
||||
|
||||
passport.deserializeUser(async (id, done) => {
|
||||
try {
|
||||
const user = await User.findById(id, '-password')
|
||||
done(null, user)
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
|
||||
passport.use(
|
||||
'local',
|
||||
new Strategy(
|
||||
{
|
||||
usernameField: 'email',
|
||||
passwordField: 'password'
|
||||
},
|
||||
passportCallback
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export { applyPassportMods, passportCallback }
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -1,4 +1,4 @@
|
||||
const Server = require('./bin/server.js')
|
||||
import Server from './bin/server.js'
|
||||
const server = new Server()
|
||||
|
||||
server.startServer()
|
||||
|
||||
Generated
+15590
-27379
File diff suppressed because it is too large
Load Diff
+36
-24
@@ -1,20 +1,21 @@
|
||||
{
|
||||
"name": "bch-dex",
|
||||
"version": "1.0.0",
|
||||
"version": "3.0.0",
|
||||
"description": "A DEX on BCH for trading SLP tokens.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "npm run test:all",
|
||||
"test:all": "export BCH_DEX=test && nyc --reporter=text mocha --exit --timeout 30000 --recursive test/unit test/e2e/automated/",
|
||||
"test:all": "export BCH_DEX=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
|
||||
"test:unit": "export BCH_DEX=test && mocha --exit --timeout 15000 --recursive test/unit/",
|
||||
"test:e2e:auto": "export BCH_DEX=test && mocha --exit --timeout 30000 test/e2e/automated/",
|
||||
"test:integration": "export BCH_DEX=test && mocha --exit --timeout 45000 --recursive test/integration",
|
||||
"test:temp": "export BCH_DEX=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
|
||||
"lint": "standard --env mocha --fix",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "export BCH_DEX=test && nyc --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/"
|
||||
"coverage": "c8 report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "export BCH_DEX=test && c8 --reporter=html mocha --exit --timeout 15000 --recursive test/unit/ test/e2e/automated/"
|
||||
},
|
||||
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
||||
"contributors": [
|
||||
@@ -27,15 +28,33 @@
|
||||
},
|
||||
"repository": "Permissionless-Software-Foundation/bch-dex",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "6.4.1",
|
||||
"@chris.troutner/retry-queue": "1.0.8",
|
||||
"@psf/bch-js": "6.7.3",
|
||||
"@chainsafe/libp2p-gossipsub": "13.1.1",
|
||||
"@chainsafe/libp2p-noise": "15.1.1",
|
||||
"@chainsafe/libp2p-yamux": "6.0.2",
|
||||
"@helia/unixfs": "3.0.7",
|
||||
"@libp2p/bootstrap": "10.1.5",
|
||||
"@libp2p/circuit-relay-v2": "1.1.5",
|
||||
"@libp2p/identify": "2.1.5",
|
||||
"@libp2p/keychain": "4.1.5",
|
||||
"@libp2p/logger": "4.0.20",
|
||||
"@libp2p/tcp": "9.1.5",
|
||||
"@libp2p/webrtc": "4.1.7",
|
||||
"@libp2p/websockets": "8.2.0",
|
||||
"@multiformats/multiaddr": "12.3.0",
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.0",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bch-token-sweep": "2.2.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bitcoincashjs-lib": "3.3.3",
|
||||
"ipfs-coord-esm": "9.1.7",
|
||||
"ipfs-http-client": "58.0.0",
|
||||
"blockstore-fs": "2.0.0",
|
||||
"datastore-fs": "9.1.7",
|
||||
"glob": "7.1.6",
|
||||
"ipfs": "0.62.3",
|
||||
"ipfs-coord": "7.1.19",
|
||||
"ipfs-http-client": "55.0.0",
|
||||
"helia": "4.2.5",
|
||||
"helia-coord": "1.5.15",
|
||||
"jsonrpc-lite": "2.2.0",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"jwt-bch-lib": "1.3.0",
|
||||
@@ -50,36 +69,29 @@
|
||||
"koa-router": "10.0.0",
|
||||
"koa-static": "5.0.0",
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "^0.36.2",
|
||||
"libp2p": "1.9.1",
|
||||
"line-reader": "0.4.0",
|
||||
"minimal-slp-wallet": "4.6.5",
|
||||
"mongoose": "5.13.13",
|
||||
"minimal-slp-wallet": "5.12.0",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"p2wdb": "1.4.0",
|
||||
"p2wdb-esm": "2.2.9",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "^4.0.4",
|
||||
"public-ip": "6.0.1",
|
||||
"winston": "3.3.3",
|
||||
"winston-daily-rotate-file": "4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "0.51.1",
|
||||
"bch-token-sweep": "2.1.2",
|
||||
"c8": "7.12.0",
|
||||
"chai": "4.3.0",
|
||||
"coveralls": "2.11.4",
|
||||
"eslint": "7.19.0",
|
||||
"eslint-config-prettier": "7.2.0",
|
||||
"eslint-config-standard": "16.0.2",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-promise": "^4.3.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "4.3.8",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"mocha": "10.0.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "19.0.3",
|
||||
"sinon": "9.2.4",
|
||||
"standard": "16.0.4",
|
||||
"standard": "17.0.0",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"release": {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
COMPOSE_PROJECT_NAME=ipfs-service-provider
|
||||
@@ -37,7 +37,7 @@ WORKDIR /home/safeuser
|
||||
RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
#RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Switch to user account.
|
||||
#USER safeuser
|
||||
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/bch-dex-ui
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v2
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -58,7 +58,7 @@ RUN git checkout ct-unstable
|
||||
# Install dependencies
|
||||
#RUN mkdir .ipfsdata
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install --force
|
||||
RUN npm install
|
||||
|
||||
# Build the site
|
||||
RUN CI=true npm run build
|
||||
@@ -68,6 +68,6 @@ 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/public /usr/share/nginx/html
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui-v2/build /usr/share/nginx/html
|
||||
|
||||
#USER safeuser
|
||||
|
||||
@@ -72,7 +72,10 @@ RUN npm run docs
|
||||
#EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
# Used for debugging
|
||||
#COPY dummy-app.js dummy-app.js
|
||||
#CMD ["node", "dummy-app.js"]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
setInterval(function () {
|
||||
const now = new Date()
|
||||
console.log(`timestamp: ${now.toLocaleString()}`)
|
||||
}, 5000)
|
||||
@@ -24,9 +24,11 @@ export DBURL=mongodb://172.17.0.1:5666/bch-swap-service-prod
|
||||
export PORT=5700
|
||||
|
||||
# bch-dex specific env vars
|
||||
export BCH_DEX=production
|
||||
export BCH_DEX=prod
|
||||
export WEBHOOKSERVICE=http://172.17.0.1:5667/webhook
|
||||
export WEBHOOKTARGET=http://172.17.0.1:5700/p2wdb
|
||||
export APP_ID=bch-dex-001
|
||||
|
||||
#export P2WDB_URL=http://172.17.0.1:5667
|
||||
|
||||
npm start
|
||||
|
||||
@@ -4,7 +4,7 @@ version: '3.9'
|
||||
|
||||
services:
|
||||
mongo-p2wdb-service:
|
||||
image: mongo:4.4.2-bionic
|
||||
image: mongo:4.2.0
|
||||
container_name: mongo-p2wdb-service
|
||||
ports:
|
||||
- '5666:27017' # <host port>:<container port>
|
||||
@@ -14,10 +14,16 @@ services:
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
image: ipfs/go-ipfs:v0.13.0
|
||||
# https://github.com/christroutner/trickle-ipfs
|
||||
image: christroutner/trickle-ipfs:v1.0.1
|
||||
#build:
|
||||
# context: ./
|
||||
# dockerfile: Dockerfile
|
||||
container_name: ipfs
|
||||
environment:
|
||||
MY_ENV_VAR: 'placeholder'
|
||||
IPFS_DAEMON_ARGUMENTS: '--enable-pubsub-experiment --migrate=true --agent-version-suffix=docker --routing=dhtclient'
|
||||
UPLOAD_KBPS: '1000'
|
||||
DOWNLOAD_KBPS: '1000'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
@@ -26,30 +32,25 @@ services:
|
||||
mem_limit: 2000mb
|
||||
ports:
|
||||
- 4001:4001
|
||||
- 4003:4003
|
||||
- 172.17.0.1:5001:5001
|
||||
- 172.17.0.1:8080:8080
|
||||
command: [
|
||||
'./start-ipfs.sh'
|
||||
]
|
||||
volumes:
|
||||
- ../data/go-ipfs/data:/data/ipfs
|
||||
- ../../swarm.key:/data/ipfs/swarm.key
|
||||
# https://docs.docker.com/compose/compose-file/compose-file-v3/#command
|
||||
# https://github.com/ipfs/go-ipfs/blob/91c52657166bcf86f2476926e4fe56694dc26562/Dockerfile#L115
|
||||
command:
|
||||
[
|
||||
'daemon',
|
||||
'--migrate=true',
|
||||
'--agent-version-suffix=docker',
|
||||
'--enable-pubsub-experiment'
|
||||
]
|
||||
- ../data/go-ipfs/data:/root/.ipfs
|
||||
restart: always
|
||||
|
||||
p2wdb:
|
||||
build:
|
||||
context: ./p2wdb/
|
||||
dockerfile: Dockerfile
|
||||
#image: christroutner/ipfs-p2wdb-service:v3.0.4
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb:v4.1.3
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
#ENABLE_BCH_PAYMENT: 1
|
||||
DEBUG_LEVEL: 1
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
@@ -67,13 +68,14 @@ services:
|
||||
volumes:
|
||||
- ../data/ipfsdata/p2wdb:/home/safeuser/ipfs-p2wdb-service/.ipfsdata/p2wdb
|
||||
- ./p2wdb/start-production.sh:/home/safeuser/ipfs-p2wdb-service/start-production.sh
|
||||
#- ../scripts/wallet.json:/home/safeuser/ipfs-p2wdb-service/wallet.json
|
||||
restart: always
|
||||
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex:v1.8.0
|
||||
image: christroutner/bch-dex:v2.2.4
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -97,7 +99,7 @@ services:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui:v1.6.5
|
||||
image: christroutner/bch-dex-ui:v1.2.4
|
||||
container_name: dex-ui
|
||||
environment:
|
||||
SERVER: 'http://192.168.2.3'
|
||||
@@ -106,7 +108,7 @@ services:
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 500mb
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- bch-dex
|
||||
ports:
|
||||
|
||||
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
WORKDIR /home/safeuser
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/bch-dex-ui-v2
|
||||
|
||||
# Switch to the desired branch. `master` is usually stable,
|
||||
# and `stage` has the most up-to-date changes.
|
||||
WORKDIR /home/safeuser/bch-dex-ui
|
||||
WORKDIR /home/safeuser/bch-dex-ui-v2
|
||||
|
||||
# For development: switch to unstable branch
|
||||
RUN git checkout ct-unstable
|
||||
@@ -58,7 +58,7 @@ RUN git checkout ct-unstable
|
||||
# Install dependencies
|
||||
#RUN mkdir .ipfsdata
|
||||
#RUN npm install -g @mapbox/node-pre-gyp
|
||||
RUN npm install --force
|
||||
RUN npm install
|
||||
|
||||
# Build the site
|
||||
RUN CI=true npm run build
|
||||
@@ -68,6 +68,6 @@ 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/public /usr/share/nginx/html
|
||||
COPY --from=builder /home/safeuser/bch-dex-ui-v2/build /usr/share/nginx/html
|
||||
|
||||
#USER safeuser
|
||||
|
||||
@@ -12,9 +12,12 @@ RUN apt-get update
|
||||
RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
|
||||
#Install Node and NPM
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
RUN sudo apt-get install -y ca-certificates curl gnupg
|
||||
RUN sudo mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||
RUN sudo apt-get update
|
||||
RUN sudo apt-get install -y nodejs build-essential
|
||||
|
||||
#Create the user 'safeuser' and add them to the sudo group.
|
||||
#RUN useradd -ms /bin/bash safeuser
|
||||
@@ -34,7 +37,7 @@ WORKDIR /home/safeuser
|
||||
|
||||
# Update to the latest version of npm.
|
||||
#RUN npm install -g npm@7.23.0
|
||||
RUN npm install -g npm
|
||||
#RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
@@ -72,7 +75,7 @@ RUN npm run docs
|
||||
#EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
@@ -24,9 +24,9 @@ export DBURL=mongodb://172.17.0.1:5666/bch-swap-service-prod
|
||||
export PORT=5700
|
||||
|
||||
# bch-dex specific env vars
|
||||
export BCH_ENV=production
|
||||
export BCH_DEX=prod
|
||||
export WEBHOOKSERVICE=http://172.17.0.1:5667/webhook
|
||||
export WEBHOOKTARGET=http://172.17.0.1:5700/p2wdb
|
||||
export APP_ID=bch-dex-test557
|
||||
export APP_ID=bch-dex-001
|
||||
|
||||
npm start
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
mongo-p2wdb-service:
|
||||
image: mongo:4.4
|
||||
container_name: mongo-p2wdb-service
|
||||
mongo-bch-dex:
|
||||
image: mongo:4.2.0
|
||||
container_name: mongo-bch-dex
|
||||
ports:
|
||||
- '5666:27017' # <host port>:<container port>
|
||||
volumes:
|
||||
@@ -14,43 +14,39 @@ services:
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
#image: ipfs/go-ipfs:v0.12.0
|
||||
image: odanado/go-ipfs:v0.11.0
|
||||
image: christroutner/trickle-ipfs-rpi:v1.0.1
|
||||
#image: odanado/go-ipfs:v0.11.0
|
||||
container_name: ipfs
|
||||
environment:
|
||||
MY_ENV_VAR: 'placeholder'
|
||||
IPFS_DAEMON_ARGUMENTS: '--enable-pubsub-experiment --migrate=true --agent-version-suffix=docker --routing=dhtclient'
|
||||
UPLOAD_KBPS: '25'
|
||||
DOWNLOAD_KBPS: '25'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 2000mb
|
||||
mem_limit: 1000mb
|
||||
ports:
|
||||
- 4001:4001
|
||||
- 4003:4003
|
||||
- 172.17.0.1:5001:5001
|
||||
- 172.17.0.1:8080:8080
|
||||
volumes:
|
||||
- ../data/go-ipfs/data:/data/ipfs
|
||||
- ../../swarm.key:/data/ipfs/swarm.key
|
||||
# https://docs.docker.com/compose/compose-file/compose-file-v3/#command
|
||||
# https://github.com/ipfs/go-ipfs/blob/91c52657166bcf86f2476926e4fe56694dc26562/Dockerfile#L115
|
||||
command:
|
||||
[
|
||||
'daemon',
|
||||
'--migrate=true',
|
||||
'--agent-version-suffix=docker',
|
||||
'--enable-pubsub-experiment'
|
||||
]
|
||||
- ./data/go-ipfs:/root/.ipfs
|
||||
command: [
|
||||
'./start-ipfs.sh'
|
||||
]
|
||||
restart: always
|
||||
|
||||
p2wdb:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/pwd-bch-dex-rpi:v1.7.1
|
||||
image: christroutner/p2wdb-rpi:v3.1.9
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
CONSUMER_URL: 'https://wa-usa-bch-consumer.fullstackcash.nl'
|
||||
DEBUG_LEVEL: 1
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
@@ -59,7 +55,7 @@ services:
|
||||
max-file: '10'
|
||||
mem_limit: 1gb
|
||||
links:
|
||||
- mongo-p2wdb-service
|
||||
- mongo-bch-dex
|
||||
ports:
|
||||
# <host port>:<container port>
|
||||
- 5667:5667 # REST API
|
||||
@@ -67,16 +63,18 @@ services:
|
||||
#- 5669:5669 # IPFS WS Port
|
||||
volumes:
|
||||
- ../data/ipfsdata/p2wdb:/home/safeuser/ipfs-p2wdb-service/.ipfsdata/p2wdb
|
||||
- ./p2wdb/start-production.sh:/home/safeuser/ipfs-p2wdb-service/start-production.sh
|
||||
restart: always
|
||||
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v1.7.1
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-rpi:v2.0.8
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
#CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
CONSUMER_URL: 'https://wa-usa-bch-consumer.fullstackcash.nl'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
@@ -84,19 +82,20 @@ services:
|
||||
max-file: '10'
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- mongo-p2wdb-service
|
||||
- mongo-bch-dex
|
||||
- p2wdb
|
||||
ports:
|
||||
- '5700:5700' # <host port>:<container port>
|
||||
volumes:
|
||||
- ../scripts/wallet.json:/home/safeuser/bch-dex/wallet.json
|
||||
- ./bch-dex/start-production.sh:/home/safeuser/bch-dex/start-production.sh
|
||||
restart: always
|
||||
|
||||
dex-ui:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui-rpi:v1.7.3
|
||||
image: christroutner/bch-dex-ui-rpi:v1.2.4
|
||||
container_name: dex-ui
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
|
||||
@@ -61,7 +61,7 @@ RUN npm run docs
|
||||
#EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
COPY start-production.sh start-production.sh
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
@@ -40,8 +40,9 @@ export P2W_ENV=production
|
||||
export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
export IPFS_TCP_PORT=4001
|
||||
export IPFS_WS_PORT=4003
|
||||
|
||||
# P2WDB specific env vars
|
||||
export ORBITDB_NAME=/orbitdb/zdpuAm4fPafpjs5mgbBUJV8Az7kd8Xj1ttD6M1JaAxF8RMRrw/psf-bch-p2wdb-keyvalue-v1.0.0-0004
|
||||
#export ORBITDB_NAME=/orbitdb/zdpuAqNiwLiJBfbRK7uihV2hAbNSXj78ufzv5VyQb8GuvRwDh/psf-bch-p2wdb-keyvalue-v3.0.0-0001
|
||||
|
||||
npm start
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
This script is similar ot sweep-wallet.js, but it can do a much faster (bulk)
|
||||
scanning of addresses. However, this requires a subscription to a FullStack.cash
|
||||
or access to a web2 Cash Stack.
|
||||
|
||||
This script will scan 20 addresses at a time. If a balance is found in any
|
||||
of the addresses, it will sweep the funds from that address.
|
||||
*/
|
||||
/*
|
||||
// Public npm libraries
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
import BCHJS from '@psf/bch-js'
|
||||
import JwtLib from 'jwt-bch-lib'
|
||||
|
||||
// Constants
|
||||
// const EMTPY_ADDR_CUTOFF = 100
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
if (!process.env.BCHJSTOKEN) {
|
||||
console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.')
|
||||
exit(0)
|
||||
}
|
||||
|
||||
async function sweepFunds () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
walletLib.config.useFullStackCash = true
|
||||
walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/'
|
||||
|
||||
// const walletInfo = await walletLib.openWallet()
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Instantiate bch-js, and use the JWT token saved to environment variable.
|
||||
|
||||
// Generate an HD tree
|
||||
// const bchjs = wallet.bchjs
|
||||
const bchjs = new BCHJS()
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
const emptyAddrCnt = 0
|
||||
const hdIndex = 1
|
||||
|
||||
// Divide all the addresses by 20, since that's how many will be checked at once.
|
||||
const numOfLoops = Math.ceil(LAST_ADDR_INDEX / 20)
|
||||
|
||||
for (let i = 1; i < numOfLoops; i++) {
|
||||
const startIndex = i
|
||||
const stopIndex = i * 20
|
||||
console.log(`startIndex: ${startIndex}, stopIndex: ${stopIndex}`)
|
||||
|
||||
// Generate the next block of 20 key pairs to scan.
|
||||
const addrsToScan = []
|
||||
const wifsToScan = []
|
||||
for (let j = startIndex; j < stopIndex; j++) {
|
||||
// Generate a keypair from the HD wallet.
|
||||
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${j}`)
|
||||
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
|
||||
const wifToSweep = bchjs.HDNode.toWIF(childNode)
|
||||
|
||||
addrsToScan.push(cashAddress)
|
||||
wifsToScan.push(wifToSweep)
|
||||
}
|
||||
console.log(`Scanning these addresses for a balance: ${JSON.stringify(addrsToScan, null, 2)}`)
|
||||
|
||||
// Scan a block of 20 addresses for a balance.
|
||||
const balances = await bchjs.Electrumx.balance(addrsToScan)
|
||||
console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
// Scan through the results and look for a balance.
|
||||
for (let j = 0; j < balances.balances.length; j++) {
|
||||
const thisBalance = balances.balances[j]
|
||||
|
||||
// const combinedBal = thisBalance.balance.confirmed + thisBalance.balance.
|
||||
|
||||
// if(thisBalance.)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ${cashAddress} with private key ${wifToSweep}`)
|
||||
//
|
||||
// try {
|
||||
// // Sweep tokens from address
|
||||
// const sweeper = new BchTokenSweep(
|
||||
// wifToSweep,
|
||||
// rootWif,
|
||||
// wallet,
|
||||
// 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}`)
|
||||
//
|
||||
// emptyAddrCnt = 0
|
||||
//
|
||||
// // Wait between loop iterations.
|
||||
// await bchjs.Util.sleep(3000)
|
||||
// } catch (err) {
|
||||
// console.log(`error message with index ${hdIndex}: ${err.message}`)
|
||||
// // console.log(err)
|
||||
// emptyAddrCnt++
|
||||
// }
|
||||
//
|
||||
// hdIndex++
|
||||
// } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF)
|
||||
//
|
||||
// console.log(`${EMTPY_ADDR_CUTOFF} 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()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -6,7 +6,7 @@
|
||||
the DEX app wallet in order to be acceped.
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
import axios from 'axios'
|
||||
|
||||
const LOCALHOST = 'http://localhost:5700'
|
||||
// const LOCALHOST = 'http://172.17.0.1:5700'
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*/
|
||||
|
||||
// Public NPM libraries
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
const fs = require('fs').promises
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
import { promises as fs } from 'fs'
|
||||
|
||||
async function createWallet () {
|
||||
try {
|
||||
@@ -15,9 +16,9 @@ async function createWallet () {
|
||||
}
|
||||
|
||||
// Wait for the wallet to be created.
|
||||
this.bchWallet = new BchWallet(undefined, advancedConfig)
|
||||
await this.bchWallet.walletInfoPromise
|
||||
const walletData = this.bchWallet.walletInfo
|
||||
const bchWallet = new BchWallet(undefined, advancedConfig)
|
||||
await bchWallet.walletInfoPromise
|
||||
const walletData = bchWallet.walletInfo
|
||||
walletData.nextAddress = 1
|
||||
|
||||
// Save the wallet file to disk.
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
This script is the same as sweep-wallet.js, but it requires a FullStack.cash
|
||||
account, which makes the scanning much faster.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
// const EMTPY_ADDR_CUTOFF = 10
|
||||
const START_INDEX = 1
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
if (!process.env.BCHJSTOKEN) {
|
||||
console.log('You will need a JWT token from FullStack.cash to execute this script. Export it to the BCHJSTOKEN environment variable and try again.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
async function sweepFunds () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
walletLib.config.useFullStackCash = true
|
||||
// walletLib.config.apiServer = 'http://192.168.2.129:3000/v5/'
|
||||
walletLib.config.apiServer = 'https://api.fullstack.cash/v5/'
|
||||
|
||||
// Open the wallet files.
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Generate an HD tree
|
||||
const bchjs = wallet.bchjs
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
let emptyAddrCnt = 0
|
||||
let hdIndex = START_INDEX
|
||||
|
||||
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 ${cashAddress} with private key ${wifToSweep}`)
|
||||
|
||||
try {
|
||||
// Sweep tokens from address
|
||||
const sweeper = new BchTokenSweep(
|
||||
wifToSweep,
|
||||
rootWif,
|
||||
wallet,
|
||||
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}`)
|
||||
|
||||
emptyAddrCnt = 0
|
||||
|
||||
// Wait between loop iterations.
|
||||
await bchjs.Util.sleep(3000)
|
||||
} catch (err) {
|
||||
console.log(`error message with index ${hdIndex}: ${err.message}`)
|
||||
// console.log(err)
|
||||
emptyAddrCnt++
|
||||
}
|
||||
|
||||
hdIndex++
|
||||
// } while (emptyAddrCnt < EMTPY_ADDR_CUTOFF)
|
||||
// console.log(`${EMTPY_ADDR_CUTOFF} empty addresses detected. Exiting.`)
|
||||
} while (hdIndex < LAST_ADDR_INDEX)
|
||||
console.log(`Last index of ${LAST_ADDR_INDEX} reached. emptyAddrCnt: ${emptyAddrCnt}`)
|
||||
|
||||
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()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
This script is the same as sweep-wallet.js, but instead of sweeping the keys,
|
||||
it simply lists out the addresses in the HD keychain. This is hany when trying
|
||||
to figure out which address a token was sent to.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const START_INDEX = 1
|
||||
const LAST_ADDR_INDEX = 100
|
||||
|
||||
async function listAddrs () {
|
||||
try {
|
||||
// Configure the wallet library to use a FullStack.cash or a local Cash Stack
|
||||
const walletLib = new WalletAdapter()
|
||||
|
||||
// Open the wallet files.
|
||||
const walletInfo = await openWallet(walletLib)
|
||||
const wallet = await walletLib.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
// const rootWif = walletInfo.privateKey
|
||||
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||
|
||||
// Generate an HD tree
|
||||
const bchjs = wallet.bchjs
|
||||
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
let hdIndex = START_INDEX
|
||||
|
||||
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(`HD Index ${hdIndex}: ${cashAddress}`)
|
||||
|
||||
hdIndex++
|
||||
} while (hdIndex < LAST_ADDR_INDEX)
|
||||
console.log(`Last index of ${LAST_ADDR_INDEX} reached.`)
|
||||
} catch (err) {
|
||||
console.error('Error in sweepFunds(): ', err)
|
||||
}
|
||||
}
|
||||
listAddrs()
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async function openWallet (walletLib) {
|
||||
try {
|
||||
// console.log('this.WALLET_FILE: ', this.WALLET_FILE)
|
||||
const walletData = await walletLib.jsonFiles.readJSON('./wallet.json')
|
||||
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
// Local libraries
|
||||
const WalletAdapter = require('../../src/adapters/wallet')
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 10
|
||||
@@ -64,6 +64,8 @@ async function sweepNfts () {
|
||||
if (thisToken.tokenType === 65) {
|
||||
console.log(`NFT ${thisToken.ticker} (${thisToken.name}) found. Sweeping to ${rootAddr}`)
|
||||
|
||||
await walletRoot.initialize()
|
||||
|
||||
// Send BCH to the child address, to pay for transaction fees.
|
||||
const receiver1 = [{
|
||||
address: cashAddress,
|
||||
@@ -74,7 +76,9 @@ async function sweepNfts () {
|
||||
|
||||
// Wait for indexers to update.
|
||||
console.log('Waiting for indexers to update...')
|
||||
await bchjs.Util.sleep(20000)
|
||||
await bchjs.Util.sleep(120000)
|
||||
|
||||
await walletChild.initialize()
|
||||
|
||||
// Send NFT to root address.
|
||||
const receiver2 = [{
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
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 script will sweep BCH and fungible (Type 1) tokens, but not NFTs.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
// const BCHJS = require('@psf/bch-js')
|
||||
const BchTokenSweep = require('bch-token-sweep/index')
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
// Local libraries
|
||||
const WalletAdapter = require('../../src/adapters/wallet')
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 10
|
||||
|
||||
@@ -13,7 +13,7 @@ export COORD_NAME=ipfs-service-provider-generic
|
||||
export MNEMONIC="churn aisle shield silver ladder swear hunt slim pen demand spoil veteran"
|
||||
|
||||
# 0 = less verbose. 3 = most verbose
|
||||
export DEBUG_LEVEL=2
|
||||
export DEBUG_LEVEL=0
|
||||
|
||||
# Production settings that use external IPFS node.
|
||||
# https://github.com/christroutner/docker-ipfs
|
||||
|
||||
+18
-8
@@ -13,16 +13,24 @@
|
||||
in the Adapter directory.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
const axios = require('axios').default
|
||||
const mongoose = require('mongoose')
|
||||
const User = require('../adapters/localdb/models/users')
|
||||
const config = require('../../config')
|
||||
const JsonFiles = require('../adapters/json-files')
|
||||
// Global npm libraries
|
||||
import axios from 'axios'
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
// Local libraries
|
||||
import User from '../adapters/localdb/models/users.js'
|
||||
import config from '../../config/index.js'
|
||||
import JsonFiles from '../adapters/json-files.js'
|
||||
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const jsonFiles = new JsonFiles()
|
||||
|
||||
const JSON_FILE = `system-user-${config.env}.json`
|
||||
const JSON_PATH = `${__dirname.toString()}/../../config/${JSON_FILE}`
|
||||
const JSON_PATH = `${__dirname.toString()}../../config/${JSON_FILE}`
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
const context = {}
|
||||
@@ -82,6 +90,8 @@ class Admin {
|
||||
// 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)
|
||||
|
||||
return context
|
||||
} catch (err) {
|
||||
@@ -169,4 +179,4 @@ class Admin {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Admin
|
||||
export default Admin
|
||||
|
||||
+5
-4
@@ -3,9 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
const MsgLib = require('bch-message-lib')
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
import MsgLib from 'bch-message-lib'
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
class Bch {
|
||||
constructor () {
|
||||
@@ -79,4 +80,4 @@ class Bch {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Bch
|
||||
export default Bch
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
*/
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
import config from '../../config/index.js'
|
||||
import NodeMailer from '../adapters/nodemailer.js'
|
||||
import wlogger from '../adapters/wlogger.js'
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
const NodeMailer = require('../adapters/nodemailer')
|
||||
const nodemailer = new NodeMailer()
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
|
||||
let _this
|
||||
|
||||
@@ -60,4 +59,4 @@ class ContactLib {
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = ContactLib
|
||||
export default ContactLib
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
for this file.
|
||||
*/
|
||||
|
||||
const JwtLib = require('jwt-bch-lib')
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
import JwtLib from 'jwt-bch-lib'
|
||||
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
class FullStackJWT {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -98,4 +99,4 @@ class FullStackJWT {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FullStackJWT
|
||||
export default FullStackJWT
|
||||
|
||||
+41
-14
@@ -5,22 +5,24 @@
|
||||
*/
|
||||
|
||||
// Public NPM libraries
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
// Load individual adapter libraries.
|
||||
const IPFSAdapter = require('./ipfs')
|
||||
const LocalDB = require('./localdb')
|
||||
const LogsAPI = require('./logapi')
|
||||
const Passport = require('./passport')
|
||||
const Nodemailer = require('./nodemailer')
|
||||
import IPFSAdapter from './ipfs/index.js'
|
||||
import LocalDB from './localdb/index.js'
|
||||
import LogsAPI from './logapi.js'
|
||||
import Passport from './passport.js'
|
||||
import Nodemailer from './nodemailer.js'
|
||||
// const { wlogger } = require('./wlogger')
|
||||
const JSONFiles = require('./json-files')
|
||||
const FullStackJWT = require('./fullstack-jwt')
|
||||
const BCHAdapter = require('./bch')
|
||||
const WalletAdapter = require('./wallet')
|
||||
const P2wdbAdapter = require('./p2wdb-adapter')
|
||||
import JSONFiles from './json-files.js'
|
||||
import FullStackJWT from './fullstack-jwt.js'
|
||||
import config from '../../config/index.js'
|
||||
// import Wallet from './wallet.adapter.js'
|
||||
|
||||
const config = require('../../config')
|
||||
import BCHAdapter from './bch.js'
|
||||
import WalletAdapter from './wallet.js'
|
||||
import P2wdbAdapter from './p2wdb-adapter.js'
|
||||
import Webhook from './webhook.js'
|
||||
|
||||
class Adapters {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -36,7 +38,10 @@ class Adapters {
|
||||
this.bch = new BCHAdapter()
|
||||
this.config = config
|
||||
this.wallet = new WalletAdapter()
|
||||
localConfig.bchWallet = this.wallet.bchWallet
|
||||
this.p2wdb = new P2wdbAdapter(localConfig)
|
||||
this.webhook = new Webhook()
|
||||
// this.wallet = new Wallet(localConfig)
|
||||
|
||||
// Get a valid JWT API key and instance bch-js.
|
||||
this.fullStackJwt = new FullStackJWT(config)
|
||||
@@ -44,14 +49,22 @@ class Adapters {
|
||||
|
||||
async start () {
|
||||
try {
|
||||
// let apiToken
|
||||
if (this.config.getJwtAtStartup) {
|
||||
// Get a JWT token and instantiate bch-js with it. Then pass that instance
|
||||
// to all the rest of the apps controllers and adapters.
|
||||
await this.fullStackJwt.getJWT()
|
||||
// apiToken = await this.fullStackJwt.getJWT()
|
||||
// Instantiate bch-js with the JWT token, and overwrite the placeholder for bch-js.
|
||||
this.bchjs = await this.fullStackJwt.instanceBchjs()
|
||||
}
|
||||
|
||||
// Create a default instance of minimal-slp-wallet without initializing it
|
||||
// (without retrieving the wallets UTXOs). This instance will be overwritten
|
||||
// if the operator has configured BCH payments.
|
||||
console.log('\nCreating default startup wallet. This wallet may be overwritten.')
|
||||
// await this.wallet.instanceWalletWithoutInitialization({}, { apiToken })
|
||||
this.bchjs = this.wallet.bchWallet.bchjs
|
||||
|
||||
// Start the IPFS node.
|
||||
// Do not start these adapters if this is an e2e test.
|
||||
// if (this.config.env !== 'test') {
|
||||
@@ -65,6 +78,20 @@ class Adapters {
|
||||
if (this.config.env !== 'test') {
|
||||
// Instance the wallet.
|
||||
await this.wallet.instanceWallet(walletData, this.bchjs)
|
||||
|
||||
// Wait until a webhook is established with the P2WDB
|
||||
// await this.webhook.waitUntilSuccess(this.config.webhookTarget)
|
||||
|
||||
// Overwrite instances of wallet used by P2WDB lib.
|
||||
this.p2wdb.bchWallet = this.wallet.bchWallet
|
||||
this.p2wdb.bchjs = this.wallet.bchWallet.bchjs
|
||||
|
||||
if (this.config.useIpfs) {
|
||||
await this.ipfs.start()
|
||||
}
|
||||
} else {
|
||||
// These lines are here to ensure code coverage hits 100%.
|
||||
console.log('Not starting IPFS node since this is an e2e test.')
|
||||
}
|
||||
|
||||
console.log('Async Adapters have been started.')
|
||||
@@ -77,4 +104,4 @@ class Adapters {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Adapters
|
||||
export default Adapters
|
||||
|
||||
+143
-4
@@ -3,8 +3,10 @@
|
||||
*/
|
||||
|
||||
// Local libraries
|
||||
const IpfsAdapter = require('./ipfs')
|
||||
const IpfsCoordAdapter = require('./ipfs-coord')
|
||||
import IpfsAdapter from './ipfs.js'
|
||||
|
||||
import IpfsCoordAdapter from './ipfs-coord.js'
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
class IPFS {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -12,6 +14,7 @@ class IPFS {
|
||||
this.ipfsAdapter = new IpfsAdapter()
|
||||
this.IpfsCoordAdapter = IpfsCoordAdapter
|
||||
this.process = process
|
||||
this.config = config
|
||||
|
||||
this.ipfsCoordAdapter = {} // placeholder
|
||||
|
||||
@@ -32,7 +35,9 @@ class IPFS {
|
||||
|
||||
// Start ipfs-coord
|
||||
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
|
||||
ipfs: this.ipfs
|
||||
ipfs: this.ipfs,
|
||||
tcpPort: this.config.ipfsTcpPort,
|
||||
wsPort: this.config.ipfsWsPort
|
||||
})
|
||||
await this.ipfsCoordAdapter.start()
|
||||
console.log('ipfs-coord is ready.')
|
||||
@@ -53,6 +58,140 @@ class IPFS {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status of this IPFS node.
|
||||
// Create an object with different metrics about this IPFS node, then return
|
||||
// the object.
|
||||
getStatus () {
|
||||
try {
|
||||
const statusObj = {
|
||||
ipfsId: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsId,
|
||||
multiAddrs: this.ipfsCoordAdapter.ipfsCoord.thisNode.ipfsMultiaddrs,
|
||||
bchAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.bchAddr,
|
||||
slpAddr: this.ipfsCoordAdapter.ipfsCoord.thisNode.slpAddr,
|
||||
pubKey: this.ipfsCoordAdapter.ipfsCoord.thisNode.pubKey,
|
||||
peers: this.ipfsCoordAdapter.ipfsCoord.thisNode.peerList.length,
|
||||
relays: this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData.length
|
||||
}
|
||||
|
||||
return statusObj
|
||||
} catch (err) {
|
||||
console.error('Error in ipfs-coord.js/getStatus()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Get details on the other peers this node is connected to.
|
||||
async getPeers (showAll) {
|
||||
try {
|
||||
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
|
||||
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
|
||||
|
||||
let ipfsPeers =
|
||||
await this.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.getPeers()
|
||||
// console.log('ipfsPeers: ', ipfsPeers)
|
||||
|
||||
ipfsPeers = this._removeDuplicatePeers(ipfsPeers)
|
||||
// console.log('filtered ipfsPeers: ', ipfsPeers)
|
||||
|
||||
const peerDataOut = []
|
||||
|
||||
// Loop through each IPFS peer and hydrate it with data from the peerData.
|
||||
for (let i = 0; i < ipfsPeers.length; i++) {
|
||||
const thisPeer = {
|
||||
peer: ipfsPeers[i]
|
||||
}
|
||||
|
||||
// if (!showAll) {
|
||||
// // Delete properties that don't contain good info.
|
||||
// delete thisPeer.muxer
|
||||
// delete thisPeer.latency
|
||||
// delete thisPeer.streams
|
||||
// }
|
||||
|
||||
// Get the ipfs-coord peer data for this peer.
|
||||
let thisPeerData = peerData.filter((x) =>
|
||||
x.from.includes(thisPeer.peer)
|
||||
)
|
||||
thisPeerData = thisPeerData[0]
|
||||
// console.log('thisPeerData: ', thisPeerData)
|
||||
|
||||
// Skip if peerData for this IPFS peer could not be found.
|
||||
// if (!thisPeerData) continue
|
||||
|
||||
try {
|
||||
// console.log('thisPeerData: ', thisPeerData)
|
||||
|
||||
// Add data to the IPFS peer data.
|
||||
thisPeer.name = thisPeerData.data.jsonLd.name
|
||||
thisPeer.protocol = thisPeerData.data.jsonLd.protocol
|
||||
thisPeer.version = thisPeerData.data.jsonLd.version
|
||||
thisPeer.connectionAddr = thisPeerData.data.connectionAddr
|
||||
|
||||
if (showAll) {
|
||||
// Add all the peer data.
|
||||
thisPeer.peerData = thisPeerData
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`warning: ${thisPeer.peer}: ${err.message}`
|
||||
)
|
||||
}
|
||||
|
||||
peerDataOut.push(thisPeer)
|
||||
}
|
||||
|
||||
return peerDataOut
|
||||
} catch (err) {
|
||||
console.error('Error in getPeers(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Expects an array of peers and returns an array of peers with duplicates
|
||||
// removed.
|
||||
// This function is used by getPeers()
|
||||
_removeDuplicatePeers (arr) {
|
||||
// https://stackoverflow.com/questions/2218999/how-to-remove-all-duplicates-from-an-array-of-objects
|
||||
return arr.filter((v, i, a) => a.findIndex((t) => t === v) === i)
|
||||
}
|
||||
|
||||
// Get data about the known Circuit Relays. Hydrate with data from peers list.
|
||||
getRelays () {
|
||||
try {
|
||||
const relayData = this.ipfsCoordAdapter.ipfsCoord.thisNode.relayData
|
||||
const peerData = this.ipfsCoordAdapter.ipfsCoord.thisNode.peerData
|
||||
// console.log(`relayData: ${JSON.stringify(relayData, null, 2)}`)
|
||||
// console.log(`peerData: ${JSON.stringify(peerData, null, 2)}`)
|
||||
|
||||
for (let i = 0; i < relayData.length; i++) {
|
||||
const thisRelay = relayData[i]
|
||||
|
||||
// Find the peer that corresponds to this relay.
|
||||
const thisPeer = peerData.filter((x) =>
|
||||
x.from.includes(thisRelay.ipfsId)
|
||||
)
|
||||
// console.log('thisPeer: ', thisPeer)
|
||||
|
||||
// If the peer couldn't be found, skip.
|
||||
if (!thisPeer.length) {
|
||||
thisRelay.name = ''
|
||||
thisRelay.description = ''
|
||||
continue
|
||||
}
|
||||
|
||||
thisRelay.name = thisPeer[0].data.jsonLd.name
|
||||
thisRelay.description = thisPeer[0].data.jsonLd.description
|
||||
}
|
||||
|
||||
const v1Relays = this.config.v1Relays
|
||||
|
||||
return { v2Relays: relayData, v1Relays }
|
||||
} catch (err) {
|
||||
console.error('Error in getRelays(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IPFS
|
||||
export default IPFS
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const IpfsCoord = require('ipfs-coord')
|
||||
const BCHJS = require('@psf/bch-js')
|
||||
const publicIp = require('public-ip')
|
||||
// import IpfsCoord from 'ipfs-coord-esm'
|
||||
import IpfsCoord from 'helia-coord'
|
||||
|
||||
// import BCHJS from '@psf/bch-js';
|
||||
import SlpWallet from 'minimal-slp-wallet'
|
||||
import { publicIpv4 } from 'public-ip'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
// const JSONRPC = require('../../controllers/json-rpc/')
|
||||
|
||||
let _this
|
||||
@@ -28,9 +32,10 @@ class IpfsCoordAdapter {
|
||||
// Encapsulate dependencies
|
||||
this.IpfsCoord = IpfsCoord
|
||||
this.ipfsCoord = {}
|
||||
this.bchjs = new BCHJS()
|
||||
// this.bchjs = new BCHJS()
|
||||
this.wallet = new SlpWallet()
|
||||
this.config = config
|
||||
this.publicIp = publicIp
|
||||
this.publicIp = publicIpv4
|
||||
|
||||
// Properties of this class instance.
|
||||
this.isReady = false
|
||||
@@ -41,10 +46,13 @@ class IpfsCoordAdapter {
|
||||
async start () {
|
||||
const circuitRelayInfo = {}
|
||||
|
||||
// Wait for the BCH wallet to create the wallet.
|
||||
await this.wallet.walletInfoPromise
|
||||
|
||||
// If configured as a Circuit Relay, get the public IP addresses for this node.
|
||||
if (this.config.isCircuitRelay) {
|
||||
try {
|
||||
const ip4 = await this.publicIp.v4()
|
||||
const ip4 = await this.publicIp()
|
||||
// const ip6 = await publicIp.v6()
|
||||
|
||||
circuitRelayInfo.ip4 = ip4
|
||||
@@ -57,23 +65,28 @@ class IpfsCoordAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
const nullLog = () => {}
|
||||
|
||||
const ipfsCoordOptions = {
|
||||
ipfs: this.ipfs,
|
||||
type: 'node.js',
|
||||
// type: 'browser',
|
||||
bchjs: this.bchjs,
|
||||
privateLog: console.log, // Default to console.log
|
||||
wallet: this.wallet,
|
||||
// privateLog: console.log, // Default to console.log
|
||||
privateLog: nullLog,
|
||||
isCircuitRelay: this.config.isCircuitRelay,
|
||||
circuitRelayInfo,
|
||||
apiInfo: this.config.apiInfo,
|
||||
announceJsonLd: this.config.announceJsonLd,
|
||||
debugLevel: this.config.debugLevel
|
||||
debugLevel: this.config.debugLevel,
|
||||
v1Relays: this.config.v1Relays,
|
||||
tcpPort: this.config.ipfsTcpPort
|
||||
}
|
||||
|
||||
// Production env uses external go-ipfs node.
|
||||
if (this.config.isProduction) {
|
||||
ipfsCoordOptions.nodeType = 'external'
|
||||
}
|
||||
// if (this.config.isProduction) {
|
||||
// ipfsCoordOptions.nodeType = 'external'
|
||||
// }
|
||||
|
||||
this.ipfsCoord = new this.IpfsCoord(ipfsCoordOptions)
|
||||
|
||||
@@ -100,12 +113,15 @@ class IpfsCoordAdapter {
|
||||
|
||||
// Subscribe to the chat pubsub channel
|
||||
async subscribeToChat () {
|
||||
// TODO: Allow user to replace nullog with their own log handler at startup.
|
||||
const nullLog = () => {}
|
||||
|
||||
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
|
||||
this.config.chatPubSubChan,
|
||||
console.log,
|
||||
nullLog,
|
||||
this.ipfsCoord.thisNode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IpfsCoordAdapter
|
||||
export default IpfsCoordAdapter
|
||||
|
||||
+227
-82
@@ -9,94 +9,95 @@
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
// const IPFS = require('ipfs')
|
||||
// const IPFS = require('@chris.troutner/ipfs')
|
||||
const IPFSembedded = require('ipfs')
|
||||
const IPFSexternal = require('ipfs-http-client')
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
import { createHelia } from 'helia'
|
||||
import fs from 'fs'
|
||||
import { FsBlockstore } from 'blockstore-fs'
|
||||
import { FsDatastore } from 'datastore-fs'
|
||||
import { createLibp2p } from 'libp2p'
|
||||
import { tcp } from '@libp2p/tcp'
|
||||
import { noise } from '@chainsafe/libp2p-noise'
|
||||
import { yamux } from '@chainsafe/libp2p-yamux'
|
||||
// import { bootstrap } from '@libp2p/bootstrap'
|
||||
// import { identifyService } from 'libp2p/identify'
|
||||
import { identify } from '@libp2p/identify'
|
||||
// import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
|
||||
import { circuitRelayServer } from '@libp2p/circuit-relay-v2'
|
||||
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
|
||||
import { webSockets } from '@libp2p/websockets'
|
||||
import { publicIpv4 } from 'public-ip'
|
||||
import { multiaddr } from '@multiformats/multiaddr'
|
||||
// import { webRTC } from '@libp2p/webrtc'
|
||||
import { keychain } from '@libp2p/keychain'
|
||||
import { defaultLogger } from '@libp2p/logger'
|
||||
import { unixfs } from '@helia/unixfs'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
import config from '../../../config/index.js'
|
||||
import JsonFiles from '../json-files.js'
|
||||
|
||||
const IPFS_DIR = './.ipfsdata/ipfs'
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
// console.log('__dirname: ', __dirname)
|
||||
|
||||
const ROOT_DIR = `${__dirname}../../../`
|
||||
const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs`
|
||||
|
||||
class IpfsAdapter {
|
||||
constructor (localConfig) {
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
|
||||
// Choose the IPFS constructor based on the config settings.
|
||||
this.IPFS = IPFSembedded // default
|
||||
if (this.config.isProduction) {
|
||||
this.IPFS = IPFSexternal
|
||||
}
|
||||
this.fs = fs
|
||||
this.createLibp2p = createLibp2p
|
||||
this.publicIp = publicIpv4
|
||||
this.multiaddr = multiaddr
|
||||
this.jsonFiles = new JsonFiles()
|
||||
this.keychain = keychain
|
||||
this.createHelia = createHelia
|
||||
|
||||
// Properties of this class instance.
|
||||
this.isReady = false
|
||||
|
||||
this.fs = fs
|
||||
// Bind 'this' object to all subfunctions
|
||||
this.start = this.start.bind(this)
|
||||
this.createNode = this.createNode.bind(this)
|
||||
this.stop = this.stop.bind(this)
|
||||
this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
|
||||
this.getSeed = this.getSeed.bind(this)
|
||||
this.getKeychain = this.getKeychain.bind(this)
|
||||
}
|
||||
|
||||
// Start an IPFS node.
|
||||
async start () {
|
||||
try {
|
||||
// Ipfs Options
|
||||
const ipfsOptionsEmbedded = {
|
||||
repo: IPFS_DIR,
|
||||
start: true,
|
||||
config: {
|
||||
relay: {
|
||||
enabled: true, // enable circuit relay dialer and listener
|
||||
hop: {
|
||||
enabled: config.isCircuitRelay // enable circuit relay HOP (make this node a relay)
|
||||
}
|
||||
},
|
||||
pubsub: true, // enable pubsub
|
||||
Swarm: {
|
||||
ConnMgr: {
|
||||
HighWater: 30,
|
||||
LowWater: 10
|
||||
}
|
||||
},
|
||||
Addresses: {
|
||||
Swarm: [
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`
|
||||
]
|
||||
},
|
||||
Datastore: {
|
||||
StorageMax: '2GB',
|
||||
StorageGCWatermark: 50,
|
||||
GCPeriod: '15m'
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure the directory structure exists that is needed by the IPFS node to store data.
|
||||
this.ensureBlocksDir()
|
||||
|
||||
const ipfsOptionsExternal = {
|
||||
host: this.config.ipfsHost,
|
||||
port: this.config.ipfsApiPort,
|
||||
agent: http.Agent({ keepAlive: true, maxSockets: 2000 })
|
||||
}
|
||||
// Create an IPFS node
|
||||
const ipfs = await this.createNode()
|
||||
// console.log('ipfs: ', ipfs)
|
||||
|
||||
let ipfsOptions = ipfsOptionsEmbedded
|
||||
if (this.config.isProduction) {
|
||||
ipfsOptions = ipfsOptionsExternal
|
||||
}
|
||||
this.id = ipfs.libp2p.peerId.toString()
|
||||
console.log('IPFS ID: ', this.id)
|
||||
|
||||
// Create a new IPFS node.
|
||||
this.ipfs = await this.IPFS.create(ipfsOptions)
|
||||
// Attempt to guess our ip4 IP address.
|
||||
const ip4 = await this.publicIp()
|
||||
let detectedMultiaddr = `/ip4/${ip4}/tcp/${this.config.ipfsTcpPort}/p2p/${this.id}`
|
||||
detectedMultiaddr = this.multiaddr(detectedMultiaddr)
|
||||
|
||||
// Set the 'server' profile so the node does not scan private networks.
|
||||
await this.ipfs.config.profiles.apply('server')
|
||||
// Get the multiaddrs for the node.
|
||||
const multiaddrs = ipfs.libp2p.getMultiaddrs()
|
||||
multiaddrs.push(detectedMultiaddr)
|
||||
console.log('Multiaddrs: ', multiaddrs)
|
||||
|
||||
// Debugging: Display IPFS config settings.
|
||||
// const configSettings = await this.ipfs.config.getAll()
|
||||
// console.log(`configSettings: ${JSON.stringify(configSettings, null, 2)}`)
|
||||
this.multiaddrs = multiaddrs
|
||||
|
||||
// Signal that this adapter is ready.
|
||||
this.isReady = true
|
||||
|
||||
this.ipfs = ipfs
|
||||
|
||||
return this.ipfs
|
||||
} catch (err) {
|
||||
console.error('Error in ipfs.js/start()')
|
||||
@@ -110,31 +111,175 @@ class IpfsAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async getKeychain (datastore) {
|
||||
const keychainInit = {
|
||||
pass: await this.getSeed()
|
||||
}
|
||||
|
||||
const chain = this.keychain(keychainInit)({
|
||||
datastore,
|
||||
logger: defaultLogger()
|
||||
})
|
||||
|
||||
return chain
|
||||
}
|
||||
|
||||
// This function creates an IPFS node using Helia.
|
||||
// It returns the node as an object.
|
||||
async createNode () {
|
||||
try {
|
||||
// Create block and data stores.
|
||||
const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
|
||||
const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
|
||||
|
||||
// const keychainInit = {
|
||||
// pass: await this.getSeed()
|
||||
// }
|
||||
|
||||
// Create an identity
|
||||
let peerId
|
||||
// console.log('this.keychain: ', this.keychain)
|
||||
// const chain = this.keychain(keychainInit)({
|
||||
// datastore,
|
||||
// logger: defaultLogger()
|
||||
// })
|
||||
const chain = await this.getKeychain(datastore)
|
||||
try {
|
||||
peerId = await chain.exportPeerId('myKey')
|
||||
} catch (err) {
|
||||
await chain.createKey('myKey', 'Ed25519', 4096)
|
||||
peerId = await chain.exportPeerId('myKey')
|
||||
}
|
||||
|
||||
// Configure services
|
||||
const services = {
|
||||
identify: identify(),
|
||||
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
|
||||
}
|
||||
if (this.config.isCircuitRelay) {
|
||||
console.log('Helia (IPFS) node IS configured as Circuit Relay')
|
||||
services.relay = circuitRelayServer({ // makes the node function as a relay server
|
||||
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
|
||||
advertise: true,
|
||||
reservations: {
|
||||
maxReservations: 15, // how many peers are allowed to reserve relay slots on this server
|
||||
reservationClearInterval: 300 * 1000, // how often to reclaim stale reservations
|
||||
applyDefaultLimit: true, // whether to apply default data/duration limits to each relayed connection
|
||||
defaultDurationLimit: 2 * 60 * 1000, // the default maximum amount of time a relayed connection can be open for
|
||||
defaultDataLimit: BigInt(2 << 7), // the default maximum number of bytes that can be transferred over a relayed connection
|
||||
maxInboundHopStreams: 32, // how many inbound HOP streams are allow simultaneously
|
||||
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('Helia (IPFS) node IS NOT configured as Circuit Relay')
|
||||
}
|
||||
|
||||
const transports = [
|
||||
tcp(),
|
||||
webSockets()
|
||||
// circuitRelayTransport({
|
||||
// discoverRelays: 3,
|
||||
// reservationConcurrency: 3
|
||||
// }),
|
||||
// webRTC()
|
||||
]
|
||||
|
||||
// libp2p is the networking layer that underpins Helia
|
||||
const libp2p = await this.createLibp2p({
|
||||
peerId,
|
||||
datastore,
|
||||
addresses: {
|
||||
listen: [
|
||||
'/ip4/127.0.0.1/tcp/0',
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
|
||||
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`
|
||||
// '/webrtc'
|
||||
]
|
||||
},
|
||||
transports,
|
||||
connectionEncryption: [
|
||||
noise()
|
||||
],
|
||||
streamMuxers: [
|
||||
yamux()
|
||||
],
|
||||
services
|
||||
})
|
||||
|
||||
// create a Helia node
|
||||
const helia = await this.createHelia({
|
||||
blockstore,
|
||||
datastore,
|
||||
libp2p
|
||||
})
|
||||
|
||||
// Attach IPFS file system.
|
||||
const fs = unixfs(helia)
|
||||
helia.fs = fs
|
||||
|
||||
return helia
|
||||
} catch (err) {
|
||||
console.error('Error creating Helia node: ', err)
|
||||
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async stop () {
|
||||
await this.ipfs.stop()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Remove the '/blocks' directory that is used to store IPFS data.
|
||||
// Dev Note: It's assumed this node is not pinning any data and that
|
||||
// everything in this directory is transient. This folder will regularly
|
||||
// fill up and prevent IPFS from starting.
|
||||
// rmBlocksDir () {
|
||||
// try {
|
||||
// const dir = `${IPFS_DIR}/blocks`
|
||||
// console.log(`Deleting ${dir} directory...`)
|
||||
//
|
||||
// this.fs.rmdirSync(dir, { recursive: true })
|
||||
//
|
||||
// console.log(`${dir} directory is deleted!`)
|
||||
//
|
||||
// return true // Signal successful execution.
|
||||
// } catch (err) {
|
||||
// console.log('Error in rmBlocksDir()')
|
||||
// throw err
|
||||
// }
|
||||
// }
|
||||
// Ensure that the directories exist to store blocks from the IPFS network.
|
||||
// This function is called at startup, before the IPFS node is started.
|
||||
ensureBlocksDir () {
|
||||
try {
|
||||
!this.fs.existsSync(`${ROOT_DIR}.ipfsdata`) && this.fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
|
||||
|
||||
!this.fs.existsSync(`${IPFS_DIR}`) && this.fs.mkdirSync(`${IPFS_DIR}`)
|
||||
|
||||
!this.fs.existsSync(`${IPFS_DIR}/blockstore`) && this.fs.mkdirSync(`${IPFS_DIR}/blockstore`)
|
||||
|
||||
!this.fs.existsSync(`${IPFS_DIR}/datastore`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore`)
|
||||
|
||||
// !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs.js/ensureBlocksDir(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// This function opens the seed used to generate the key for this IPFS peer.
|
||||
// The seed is stored in a JSON file. If it doesn't exist, a new one is created.
|
||||
async getSeed () {
|
||||
try {
|
||||
let seed
|
||||
|
||||
const filename = `${IPFS_DIR}/seed.json`
|
||||
|
||||
try {
|
||||
// Try to read the JSON file containing the seed.
|
||||
seed = await this.jsonFiles.readJSON(filename)
|
||||
} catch (err) {
|
||||
const seedNum = Math.floor(Math.random() * 1000000000000000000000)
|
||||
seed = seedNum.toString()
|
||||
|
||||
// Save the newly generated seed
|
||||
await this.jsonFiles.writeJSON(seed, filename)
|
||||
}
|
||||
|
||||
// console.log('getSeed() seed: ', seed)
|
||||
|
||||
return seed
|
||||
} catch (err) {
|
||||
console.error('Error in adapters/ipfs/ipfs.js/getSeed()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IpfsAdapter
|
||||
export default IpfsAdapter
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
A utility file for reading and writing JSON files.
|
||||
*/
|
||||
'use strict'
|
||||
const fs = require('fs')
|
||||
|
||||
import fs from 'fs'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -75,4 +75,4 @@ class JsonFiles {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JsonFiles
|
||||
export default JsonFiles
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Load Mongoose models.
|
||||
const Users = require('./models/users')
|
||||
const Entries = require('./models/entry')
|
||||
const Order = require('./models/order')
|
||||
const Offer = require('./models/offer')
|
||||
import Users from './models/users.js'
|
||||
import Entries from './models/entry.js'
|
||||
import Order from './models/order.js'
|
||||
import Offer from './models/offer.js'
|
||||
|
||||
class LocalDB {
|
||||
constructor () {
|
||||
@@ -18,4 +18,4 @@ class LocalDB {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LocalDB
|
||||
export default LocalDB
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const Entry = new mongoose.Schema({
|
||||
entry: { type: String },
|
||||
@@ -10,4 +10,4 @@ const Entry = new mongoose.Schema({
|
||||
merit: { type: Number }
|
||||
})
|
||||
|
||||
module.exports = mongoose.model('entry', Entry)
|
||||
export default mongoose.model('entry', Entry)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const Offer = new mongoose.Schema({
|
||||
|
||||
@@ -6,7 +6,7 @@ const Offer = new mongoose.Schema({
|
||||
tokenId: { type: String },
|
||||
utxoTxid: { type: String },
|
||||
utxoVout: { type: Number },
|
||||
ticker: { type: String },
|
||||
ticker: { type: String, default: '' },
|
||||
tokenType: { type: Number },
|
||||
|
||||
// Trade data
|
||||
@@ -27,6 +27,9 @@ const Offer = new mongoose.Schema({
|
||||
timestamp: { type: Number },
|
||||
globalTimestamp: { type: String },
|
||||
localTimestamp: { type: String },
|
||||
displayCategory: { type: String },
|
||||
nsfw: { type: Boolean, default: false },
|
||||
flags: { type: Array },
|
||||
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
@@ -35,4 +38,4 @@ const Offer = new mongoose.Schema({
|
||||
|
||||
})
|
||||
|
||||
module.exports = mongoose.model('offer', Offer)
|
||||
export default mongoose.model('offer', Offer)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
See the dev-docs/specification.md for details on each property.
|
||||
*/
|
||||
|
||||
const mongoose = require('mongoose')
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const Order = new mongoose.Schema({
|
||||
// Token data
|
||||
@@ -40,4 +40,4 @@ const Order = new mongoose.Schema({
|
||||
messageClass: { type: Number }
|
||||
})
|
||||
|
||||
module.exports = mongoose.model('order', Order)
|
||||
export default mongoose.model('order', Order)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
const mongoose = require('mongoose')
|
||||
const bcrypt = require('bcryptjs')
|
||||
const config = require('../../../../config')
|
||||
const jwt = require('jsonwebtoken')
|
||||
// Global npm libraries
|
||||
import mongoose from 'mongoose'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
// Local libraries
|
||||
import config from '../../../../config/index.js'
|
||||
|
||||
const User = new mongoose.Schema({
|
||||
type: { type: String, default: 'user' },
|
||||
@@ -51,4 +54,4 @@ User.methods.generateToken = function generateToken () {
|
||||
}
|
||||
|
||||
// export default mongoose.model('user', User)
|
||||
module.exports = mongoose.model('user', User)
|
||||
export default mongoose.model('user', User)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
orders from the datatabase.
|
||||
*/
|
||||
|
||||
const Order = require('./models/order')
|
||||
import Order from './models/order.js'
|
||||
|
||||
// let _this
|
||||
|
||||
@@ -39,4 +39,4 @@ class OrderPagination {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OrderPagination
|
||||
export default OrderPagination
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
const lineReader = require('line-reader')
|
||||
const fs = require('fs')
|
||||
import lineReader from 'line-reader'
|
||||
import fs from 'fs'
|
||||
|
||||
const config = require('../../config')
|
||||
import config from '../../config/index.js'
|
||||
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
|
||||
let _this
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
class LogsApi {
|
||||
constructor () {
|
||||
@@ -169,4 +174,4 @@ class LogsApi {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LogsApi
|
||||
export default LogsApi
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
const nodemailer = require('nodemailer')
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
const config = require('../../config')
|
||||
|
||||
const { wlogger } = require('./wlogger')
|
||||
import config from '../../config/index.js'
|
||||
import wlogger from './wlogger.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -150,4 +149,4 @@ class NodeMailer {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NodeMailer
|
||||
export default NodeMailer
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const axios = require('axios')
|
||||
const { Write, Read } = require('p2wdb/index')
|
||||
import axios from 'axios'
|
||||
|
||||
import P2WDB from 'p2wdb-esm'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../config')
|
||||
import config from '../../config/index.js'
|
||||
const { Write, Read } = P2WDB
|
||||
|
||||
class P2wdbAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency injection
|
||||
this.bchjs = localConfig.bchjs
|
||||
if (!this.bchjs) {
|
||||
throw new Error('Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
|
||||
this.bchWallet = localConfig.bchWallet
|
||||
if (!this.bchWallet) {
|
||||
throw new Error('Must pass an instance of minimal-slp-wallet as bchWallet when instantiating p2wdb.js adapter.')
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
@@ -22,9 +24,14 @@ class P2wdbAdapter {
|
||||
this.config = config
|
||||
this.Write = Write
|
||||
this.Read = Read
|
||||
this.bchjs = this.bchWallet.bchjs
|
||||
|
||||
// Allow the localConfig to overwrite the config file values.
|
||||
this.p2wdbURL = localConfig.p2wdbURL || config.p2wdbUrl
|
||||
|
||||
// Bind the 'this' object to all functions
|
||||
this.write = this.write.bind(this)
|
||||
this.checkForSufficientFunds = this.checkForSufficientFunds.bind(this)
|
||||
}
|
||||
|
||||
// Write some data to the P2WDB
|
||||
@@ -51,6 +58,10 @@ class P2wdbAdapter {
|
||||
// settings.
|
||||
instantiateWriteLib (wif) {
|
||||
try {
|
||||
if (!wif) {
|
||||
throw new Error('WIF input required when calling instantiateWriteLib()')
|
||||
}
|
||||
|
||||
// Object used to configure the Write library.
|
||||
let configObj = {}
|
||||
|
||||
@@ -62,7 +73,8 @@ class P2wdbAdapter {
|
||||
wif,
|
||||
serverURL: this.p2wdbURL,
|
||||
restURL: this.bchjs.restURL,
|
||||
apiToken: this.bchjs.apiToken
|
||||
apiToken: this.bchjs.apiToken,
|
||||
bchWallet: this.bchWallet
|
||||
}
|
||||
} else {
|
||||
// Use web 3 infrastructure.
|
||||
@@ -72,7 +84,8 @@ class P2wdbAdapter {
|
||||
wif,
|
||||
serverURL: this.p2wdbURL,
|
||||
interface: 'consumer-api',
|
||||
restURL: this.config.consumerUrl
|
||||
restURL: this.config.consumerUrl,
|
||||
bchWallet: this.bchWallet
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +117,4 @@ class P2wdbAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = P2wdbAdapter
|
||||
export default P2wdbAdapter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
koa-passport is an authorization library used for different authentication schemes.
|
||||
*/
|
||||
|
||||
const passport = require('koa-passport')
|
||||
import passport from 'koa-passport'
|
||||
|
||||
let _this
|
||||
class Passport {
|
||||
@@ -32,4 +32,4 @@ class Passport {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Passport
|
||||
export default Passport
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
Wallet adapter. This library instantiates minimal-slp-wallet based on different
|
||||
configuration settings. That single instance of minimal-slp-wallet is then
|
||||
easily passed around the rest of this application.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
|
||||
// Local libraries
|
||||
import config from '../../config/index.js'
|
||||
import JsonFiles from './json-files.js'
|
||||
|
||||
class Wallet {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
this.BchWallet = BchWallet
|
||||
this.jsonFiles = new JsonFiles()
|
||||
|
||||
// Bind 'this' object to all subfunctions
|
||||
this.instanceWalletWithoutInitialization = this.instanceWalletWithoutInitialization.bind(this)
|
||||
}
|
||||
|
||||
// This is used for initializing the wallet, without waiting to update the wallet
|
||||
// UTXOs from the blockchain.
|
||||
// This is useful when the wallet is simply needed to make calls to the blockchain,
|
||||
// and there is no need to hydrate it with UTXO data.
|
||||
async instanceWalletWithoutInitialization (walletData = {}, advancedConfig = {}) {
|
||||
try {
|
||||
// Use the apiToken from the config settings, if one is not passed-in at
|
||||
// run-time.
|
||||
if (!advancedConfig.apiToken) {
|
||||
advancedConfig.apiToken = this.config.apiToken
|
||||
}
|
||||
|
||||
// If an authentication password is passed in the config, add it.
|
||||
if (this.config.authPass) {
|
||||
advancedConfig.authPass = this.config.authPass
|
||||
}
|
||||
|
||||
// Detect and configure different blockchain infrastructure settings.
|
||||
if (this.config.walletInterface === 'web2') {
|
||||
advancedConfig.interface = 'rest-api'
|
||||
advancedConfig.restURL = this.config.apiServer
|
||||
} else {
|
||||
// By default use the web3 Cash Stack (https://cashstack.info)
|
||||
advancedConfig.interface = 'consumer-api'
|
||||
advancedConfig.restURL = this.config.apiServer
|
||||
}
|
||||
|
||||
// console.log('advancedConfig setting when creating wallet: ', advancedConfig)
|
||||
|
||||
// Instantiate minimal-slp-wallet.
|
||||
if (walletData.mnemonic) {
|
||||
// Instance the wallet using the mnemonic passed in to this function.
|
||||
console.log('Mnemonic provided to wallet library from calling function.')
|
||||
this.bchWallet = await this._instanceWallet(walletData.mnemonic, advancedConfig)
|
||||
} else if (this.config.mnemonic) {
|
||||
// Otherwise use the mnemonic in the config setting or passed as an environment variable.
|
||||
console.log('Mnemonic retrieved from config file or environment variable.')
|
||||
this.bchWallet = await this._instanceWallet(this.config.mnemonic, advancedConfig)
|
||||
} else {
|
||||
// If no mnemonic is provided, then generate a new mnemonic to create the wallet.
|
||||
console.log('New mnemonic generated.')
|
||||
this.bchWallet = await this._instanceWallet(undefined, advancedConfig)
|
||||
}
|
||||
|
||||
// Wait for wallet to initialize.
|
||||
await this.bchWallet.walletInfoPromise
|
||||
console.log('BCH wallet initialized.')
|
||||
console.log(`Wallet address: ${this.bchWallet.walletInfo.cashAddress}`)
|
||||
console.log(`Wallet mnemonic: ${this.bchWallet.walletInfo.mnemonic}`)
|
||||
|
||||
return this.bchWallet
|
||||
} catch (err) {
|
||||
console.error('Error in instanceWalletWithoutInitialization()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used for easier mocking of unit tests.
|
||||
async _instanceWallet (mnemonic, config) {
|
||||
const wallet = new this.BchWallet(mnemonic, config)
|
||||
await wallet.walletInfoPromise
|
||||
|
||||
return wallet
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
// Does not instance the wallet. The output of this function is expected to
|
||||
// be passed to instanceWallet().
|
||||
async openWallet (advancedConfig = {}) {
|
||||
try {
|
||||
let walletData
|
||||
|
||||
// Try to open the wallet.json file.
|
||||
try {
|
||||
walletData = await this.jsonFiles.readJSON(this.config.walletFile)
|
||||
walletData = walletData.wallet
|
||||
} catch (err) {
|
||||
// Create a new wallet file if one does not already exist.
|
||||
console.log('Wallet file not found. Creating new wallet.json file.')
|
||||
|
||||
// Create a new wallet.
|
||||
// No-Update flag creates wallet without making any network calls.
|
||||
const walletInstance = await this.instanceWalletWithoutInitialization({}, advancedConfig)
|
||||
|
||||
walletData = walletInstance.walletInfo
|
||||
|
||||
// Add the nextAddress property
|
||||
walletData.nextAddress = 1
|
||||
|
||||
// Write the wallet data to the JSON file.
|
||||
await this.jsonFiles.writeJSON({ wallet: walletData }, this.config.walletFile)
|
||||
}
|
||||
|
||||
// console.log('walletData: ', walletData)
|
||||
return walletData
|
||||
} catch (err) {
|
||||
console.error('Error in openWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Create an instance of minimal-slp-wallet. Same as
|
||||
// instanceWalletWithoutInitialization(), but waits for the wallet to initialize
|
||||
// its UTXOs (wallet balance and tokens).
|
||||
async instanceWallet (walletData = {}, advancedConfig = {}) {
|
||||
try {
|
||||
// Instance the wallet without initialization.
|
||||
await this.instanceWalletWithoutInitialization(walletData, advancedConfig)
|
||||
|
||||
// Initialize the wallet
|
||||
await this.bchWallet.initialize()
|
||||
|
||||
return this.bchWallet
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/instanceWallet()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Increments the 'nextAddress' property in the wallet file. This property
|
||||
// indicates the HD index that should be used to generate a key pair for
|
||||
// storing funds for Offers.
|
||||
// This function opens the wallet file, increments the nextAddress property,
|
||||
// then saves the change to the wallet file.
|
||||
async incrementNextAddress () {
|
||||
try {
|
||||
const walletData = await this.openWallet()
|
||||
|
||||
await this.instanceWalletWithoutInitialization(walletData)
|
||||
|
||||
walletData.nextAddress++
|
||||
// console.log('walletData finish: ', walletData)
|
||||
|
||||
await this.jsonFiles.writeJSON({ wallet: walletData }, this.config.walletFile)
|
||||
|
||||
// Update the working instance of the wallet.
|
||||
this.bchWallet.walletInfo.nextAddress++
|
||||
// console.log('this.bchWallet.walletInfo: ', this.bchWallet.walletInfo)
|
||||
|
||||
return walletData.nextAddress
|
||||
} catch (err) {
|
||||
console.error('Error in incrementNextAddress()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// This method returns an object that contains a private key WIF, public address,
|
||||
// and the index of the HD wallet that the key pair was generated from.
|
||||
// TODO: Allow input integer. If input is used, use that as the index. If no
|
||||
// input is provided, then call incrementNextAddress().
|
||||
async getKeyPair (hdIndex = 0) {
|
||||
try {
|
||||
if (!hdIndex) {
|
||||
// Increment the HD index and generate a new key pair.
|
||||
hdIndex = await this.incrementNextAddress()
|
||||
}
|
||||
|
||||
const mnemonic = this.bchWallet.walletInfo.mnemonic
|
||||
|
||||
// root seed buffer
|
||||
const rootSeed = await this.bchWallet.bchjs.Mnemonic.toSeed(mnemonic)
|
||||
const masterHDNode = this.bchWallet.bchjs.HDNode.fromSeed(rootSeed)
|
||||
|
||||
// HDNode of BIP44 account
|
||||
// const account = this.bchWallet.bchjs.HDNode.derivePath(masterHDNode, "m/44'/245'/0'")
|
||||
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
|
||||
const cashAddress = this.bchWallet.bchjs.HDNode.toCashAddress(childNode)
|
||||
console.log('Generating a new key pair for cashAddress: ', cashAddress)
|
||||
|
||||
const wif = this.bchWallet.bchjs.HDNode.toWIF(childNode)
|
||||
|
||||
const outObj = {
|
||||
cashAddress,
|
||||
wif,
|
||||
hdIndex
|
||||
}
|
||||
return outObj
|
||||
} catch (err) {
|
||||
console.error('Error in getKeyPair()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Optimize the wallet by consolidating the UTXOs.
|
||||
async optimize () {
|
||||
const UTXO_THREASHOLD = 7
|
||||
// Do a dry-run first to see if there are enough UTXOs worth consolidating.
|
||||
const dryRunOut = await this.bchWallet.optimize(true)
|
||||
if (dryRunOut.bchUtxoCnt > UTXO_THREASHOLD) {
|
||||
// Consolidate BCH UTXOs if the count is above the threashold.
|
||||
const txids = await this.bchWallet.optimize()
|
||||
console.log(`Wallet optimized with these return values: ${JSON.stringify(txids, null, 2)}`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get the balance of the wallet in sats and PSF tokens.
|
||||
// This function is called by the GET /entry/balance controller.
|
||||
async getBalance () {
|
||||
const balance = await this.bchWallet.getBalance()
|
||||
// console.log('balance: ', balance)
|
||||
const tokens = await this.bchWallet.listTokens()
|
||||
// console.log('tokens: ', tokens)
|
||||
// Find the array entry for the PSF token
|
||||
const psfTokens = tokens.find(x => x.tokenId === '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0')
|
||||
// console.log('psfTokens: ', psfTokens)
|
||||
let psfBalance = 0
|
||||
if (psfTokens) {
|
||||
psfBalance = psfTokens.qty
|
||||
}
|
||||
const outObj = {
|
||||
satBalance: balance,
|
||||
psfBalance,
|
||||
success: true
|
||||
}
|
||||
return outObj
|
||||
}
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
+66
-8
@@ -3,26 +3,41 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const BchWallet = require('minimal-slp-wallet/index')
|
||||
const bitcoinJs = require('bitcoincashjs-lib')
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
import bitcoinJs from 'bitcoincashjs-lib'
|
||||
|
||||
// Local libraries
|
||||
const JsonFiles = require('./json-files')
|
||||
const config = require('../../config')
|
||||
import JsonFiles from './json-files.js'
|
||||
|
||||
import config from '../../config/index.js'
|
||||
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
const WALLET_FILE = `${__dirname.toString()}/../../wallet.json`
|
||||
// const PROOF_OF_BURN_QTY = 0.01
|
||||
// const P2WDB_TOKEN_ID =
|
||||
// '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const advancedConfig = {}
|
||||
|
||||
class WalletAdapter {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.jsonFiles = new JsonFiles()
|
||||
this.WALLET_FILE = WALLET_FILE
|
||||
this.BchWallet = BchWallet
|
||||
this.config = config
|
||||
this.bitcoinJs = bitcoinJs
|
||||
this.BchWallet = BchWallet
|
||||
this.bchWallet = {} // Will be replaced when initialized.
|
||||
// this.advancedConfig = localConfig.advancedConfig
|
||||
|
||||
// Bind the 'this' object
|
||||
this.moveTokens = this.moveTokens.bind(this)
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
@@ -76,7 +91,8 @@ class WalletAdapter {
|
||||
throw new Error('Wallet data is not formatted correctly. Can not read mnemonic in wallet file!')
|
||||
}
|
||||
|
||||
const advancedConfig = {}
|
||||
// const advancedConfig = {}
|
||||
console.log(`Using FullStack.cash: ${this.config.useFullStackCash}`)
|
||||
if (this.config.useFullStackCash) {
|
||||
advancedConfig.interface = 'rest-api'
|
||||
advancedConfig.restURL = this.config.apiServer
|
||||
@@ -91,6 +107,7 @@ class WalletAdapter {
|
||||
|
||||
// Wait for wallet to initialize.
|
||||
await this.bchWallet.walletInfoPromise
|
||||
await this.bchWallet.initialize()
|
||||
console.log(`BCH wallet initialized. Wallet address: ${this.bchWallet.walletInfo.cashAddress}`)
|
||||
// console.log(`this.bchWallet.walletInfo: ${JSON.stringify(this.bchWallet.walletInfo, null, 2)}`)
|
||||
|
||||
@@ -347,7 +364,7 @@ class WalletAdapter {
|
||||
}
|
||||
|
||||
// Update the UTXO store of the wallet.
|
||||
await this.bchWallet.getUtxos()
|
||||
await this.bchWallet.initialize()
|
||||
|
||||
// Get the token type of the token being moved.
|
||||
// Combine Fungible and NFT token UTXOs.
|
||||
@@ -499,6 +516,47 @@ class WalletAdapter {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Given an Order database object, this function sends the tokens from the
|
||||
// HD index where they are stored, back to the root address of the wallet.
|
||||
async reclaimTokens (orderData) {
|
||||
try {
|
||||
// Get the key pair from the Order.
|
||||
const keyPair = await this.getKeyPair(orderData.hdIndex)
|
||||
console.log('keyPair: ', keyPair)
|
||||
|
||||
// Instantiate the wallet using the private key of the Order.
|
||||
// const tempWallet = new this.BchWallet(keyPair.wif, advancedConfig)
|
||||
// await tempWallet.initialize()
|
||||
|
||||
// Send the tokens from the Order to the root address.
|
||||
// const receiver = {
|
||||
// address: this.bchWallet.walletInfo.address,
|
||||
// tokenId: orderData.tokenId,
|
||||
// qty: orderData.numTokens
|
||||
// }
|
||||
// console.log('receiver: ', receiver)
|
||||
//
|
||||
// const txid = await tempWallet.sendTokens(receiver)
|
||||
|
||||
// Sweep the tokens to the root address.
|
||||
const sweeper = new BchTokenSweep(
|
||||
keyPair.wif,
|
||||
this.bchWallet.walletInfo.privateKey,
|
||||
this.bchWallet,
|
||||
550,
|
||||
this.bchWallet.walletInfo.cashAddress
|
||||
)
|
||||
await sweeper.populateObjectFromNetwork()
|
||||
const hex = await sweeper.sweepTo(this.bchWallet.walletInfo.cashAddress)
|
||||
const txid = await this.bchWallet.ar.sendTx(hex)
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/reclaimTokens()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WalletAdapter
|
||||
export default WalletAdapter
|
||||
|
||||
+44
-3
@@ -2,8 +2,9 @@
|
||||
The (Clean Architecture) Adapter for manageing a webhook connection to P2WDB.
|
||||
*/
|
||||
|
||||
const config = require('../../config')
|
||||
const axios = require('axios')
|
||||
import config from '../../config/index.js'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -59,6 +60,46 @@ class WebHook {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise that will not resolve until a webhook has been successfully
|
||||
// created with the P2WDB.
|
||||
// Dev Note: This was created because establishing a webhook between bch-dex
|
||||
// and the P2WDB at startup is critical. If it's not established, then bch-dex
|
||||
// can not 'see' new Offers and Counter Offers.
|
||||
async waitUntilSuccess (url) {
|
||||
try {
|
||||
let success = false
|
||||
|
||||
do {
|
||||
try {
|
||||
// Delete an old webhook if it exists.
|
||||
await this.deleteWebhook(this.config.webhookTarget)
|
||||
} catch (err) {
|
||||
/* exit quietly */
|
||||
// console.log('err deleting webhook: ', err)
|
||||
}
|
||||
|
||||
try {
|
||||
await this.createWebhook(this.config.webhookTarget)
|
||||
console.log('Webhook created')
|
||||
success = true
|
||||
} catch (err) {
|
||||
const now = new Date()
|
||||
console.log(`${now.toLocaleString()}: Error trying to create webhook with P2WDB. Trying again...`)
|
||||
await sleep(2000)
|
||||
}
|
||||
} while (!success)
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in webhook.js/waitUntilSuccess()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebHook
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export default WebHook
|
||||
|
||||
+12
-4
@@ -6,10 +6,17 @@
|
||||
|
||||
'use strict'
|
||||
|
||||
const winston = require('winston')
|
||||
require('winston-daily-rotate-file')
|
||||
// Global npm libraries
|
||||
import winston from 'winston'
|
||||
import 'winston-daily-rotate-file'
|
||||
|
||||
const config = require('../../config')
|
||||
// Local libraries
|
||||
import config from '../../config/index.js'
|
||||
|
||||
// Hack to get __dirname back.
|
||||
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
||||
import * as url from 'url'
|
||||
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
||||
|
||||
class Wlogger {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -70,4 +77,5 @@ logger.outputToConsole()
|
||||
|
||||
const wlogger = logger.wlogger
|
||||
|
||||
module.exports = { wlogger, Wlogger }
|
||||
export { wlogger as default, Wlogger }
|
||||
// export default wlogger
|
||||
|
||||
@@ -7,18 +7,20 @@
|
||||
// Public npm libraries.
|
||||
|
||||
// Local libraries
|
||||
const Adapters = require('../adapters')
|
||||
const JSONRPC = require('./json-rpc')
|
||||
const UseCases = require('../use-cases')
|
||||
const RESTControllers = require('./rest-api')
|
||||
const TimerControllers = require('./timer-controllers')
|
||||
import Adapters from '../adapters/index.js'
|
||||
import JSONRPC from './json-rpc/index.js'
|
||||
import UseCases from '../use-cases/index.js'
|
||||
import RESTControllers from './rest-api/index.js'
|
||||
import TimerControllers from './timer-controllers.js'
|
||||
import config from '../../config/index.js'
|
||||
|
||||
class Controllers {
|
||||
constructor (localConfig = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.adapters = new Adapters()
|
||||
this.useCases = new UseCases({ adapters: this.adapters })
|
||||
|
||||
this.timerControllers = new TimerControllers({ adapters: this.adapters, useCases: this.useCases })
|
||||
this.config = config
|
||||
}
|
||||
|
||||
// Spin up any adapter libraries that have async startup needs.
|
||||
@@ -45,8 +47,13 @@ class Controllers {
|
||||
|
||||
// Attach any other controllers other than REST API controllers.
|
||||
async attachControllers (app) {
|
||||
if (this.config.useIpfs) {
|
||||
// Attach JSON RPC controllers
|
||||
this.attachRPCControllers()
|
||||
}
|
||||
|
||||
// this.attachRPCControllers()
|
||||
// Attach and start the timer controllers
|
||||
this.timerControllers.startTimers()
|
||||
}
|
||||
|
||||
// Add the JSON RPC router to the ipfs-coord adapter.
|
||||
@@ -63,4 +70,4 @@ class Controllers {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Controllers
|
||||
export default Controllers
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
import jsonrpc from 'jsonrpc-lite'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../../config')
|
||||
import config from '../../../../config/index.js'
|
||||
|
||||
class AboutRPC {
|
||||
constructor (localConfig) {
|
||||
@@ -48,4 +48,4 @@ class AboutRPC {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AboutRPC
|
||||
export default AboutRPC
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
import jsonrpc from 'jsonrpc-lite'
|
||||
|
||||
// Local libraries
|
||||
// const AuthLib = require('../../lib/auth')
|
||||
// const UserLib = require('../../../use-cases/user')
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
const RateLimit = require('../rate-limit')
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
import RateLimit from '../rate-limit.js'
|
||||
|
||||
class AuthRPC {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -173,4 +174,4 @@ class AuthRPC {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthRPC
|
||||
export default AuthRPC
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
import jsonrpc from 'jsonrpc-lite'
|
||||
|
||||
// Local support libraries
|
||||
const { wlogger } = require('../../adapters/wlogger')
|
||||
const UserController = require('./users')
|
||||
const AuthController = require('./auth')
|
||||
const AboutController = require('./about')
|
||||
import wlogger from '../../adapters/wlogger.js'
|
||||
|
||||
import UserController from './users/index.js'
|
||||
import AuthController from './auth/index.js'
|
||||
import AboutController from './about/index.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -190,4 +191,4 @@ class JSONRPC {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JSONRPC
|
||||
export default JSONRPC
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/* eslint no-useless-catch: 0 */
|
||||
|
||||
// Local libraries
|
||||
const RateLimitLib = require('koa2-ratelimit').RateLimit
|
||||
import { RateLimit as RateLimitLib } from 'koa2-ratelimit'
|
||||
|
||||
class RateLimit {
|
||||
constructor (options) {
|
||||
@@ -80,4 +80,4 @@ class RateLimit {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RateLimit
|
||||
export default RateLimit
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const jsonrpc = require('jsonrpc-lite')
|
||||
import jsonrpc from 'jsonrpc-lite'
|
||||
|
||||
// Local libraries
|
||||
// const UserLib = require('../../../use-cases/user')
|
||||
const Validators = require('../validators')
|
||||
const RateLimit = require('../rate-limit')
|
||||
import Validators from '../validators.js'
|
||||
|
||||
import RateLimit from '../rate-limit.js'
|
||||
|
||||
class UserRPC {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -533,4 +534,4 @@ class UserRPC {
|
||||
// TODO create deleteUser()
|
||||
}
|
||||
|
||||
module.exports = UserRPC
|
||||
export default UserRPC
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
/* eslint no-useless-catch: 0 */
|
||||
|
||||
// Public npm libraries
|
||||
const jwt = require('jsonwebtoken')
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
// Local libraries
|
||||
const config = require('../../../config')
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
// const UserModel = require('../../adapters/localdb/models/users')
|
||||
|
||||
class Validators {
|
||||
@@ -93,4 +94,4 @@ class Validators {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Validators
|
||||
export default Validators
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const Passport = require('../../../adapters/passport')
|
||||
import Passport from '../../../adapters/passport.js'
|
||||
const passport = new Passport()
|
||||
|
||||
let _this
|
||||
@@ -96,4 +96,4 @@ class AuthRESTController {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthRESTController
|
||||
export default AuthRESTController
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const AuthRESTController = require('./controller')
|
||||
import AuthRESTController from './controller.js'
|
||||
|
||||
class AuthRouter {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -48,4 +48,4 @@ class AuthRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthRouter
|
||||
export default AuthRouter
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
import ContactLib from '../../../adapters/contact.js'
|
||||
|
||||
const ContactLib = require('../../../adapters/contact')
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
|
||||
const contactLib = new ContactLib()
|
||||
|
||||
let _this
|
||||
@@ -74,4 +74,4 @@ class ContactController {
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = ContactController
|
||||
export default ContactController
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const ContactRESTControllerLib = require('./controller')
|
||||
import ContactRESTControllerLib from './controller.js'
|
||||
|
||||
class ContactRouter {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -53,4 +53,4 @@ class ContactRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ContactRouter
|
||||
export default ContactRouter
|
||||
|
||||
@@ -64,4 +64,4 @@ class EntryRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntryRESTControllerLib
|
||||
export default EntryRESTControllerLib
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const EntryRESTControllerLib = require('./controller')
|
||||
import EntryRESTControllerLib from './controller.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -57,4 +57,4 @@ class UserRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserRouter
|
||||
export default UserRouter
|
||||
|
||||
@@ -6,15 +6,17 @@
|
||||
|
||||
// Public npm libraries.
|
||||
|
||||
// Load the REST API Controllers.
|
||||
const AuthRESTController = require('./auth')
|
||||
const UserRouter = require('./users')
|
||||
const ContactRESTController = require('./contact')
|
||||
const LogsRESTController = require('./logs')
|
||||
const EntryRouter = require('./entry')
|
||||
const OfferRouter = require('./offer')
|
||||
const OrderRouter = require('./order')
|
||||
const P2WDBRouter = require('./p2wdb')
|
||||
// Local libraries
|
||||
import AuthRESTController from './auth/index.js'
|
||||
import UserRouter from './users/index.js'
|
||||
import ContactRESTController from './contact/index.js'
|
||||
import LogsRESTController from './logs/index.js'
|
||||
import IpfsRESTController from './ipfs/index.js'
|
||||
import config from '../../../config/index.js'
|
||||
import EntryRouter from './entry/index.js'
|
||||
import OfferRouter from './offer/index.js'
|
||||
import OrderRouter from './order/index.js'
|
||||
import P2WDBRouter from './p2wdb/index.js'
|
||||
|
||||
class RESTControllers {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -32,7 +34,8 @@ class RESTControllers {
|
||||
)
|
||||
}
|
||||
|
||||
// console.log('Controllers localConfig: ', localConfig)
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
}
|
||||
|
||||
attachRESTControllers (app) {
|
||||
@@ -41,13 +44,15 @@ class RESTControllers {
|
||||
useCases: this.useCases
|
||||
}
|
||||
|
||||
// Attach the REST API Controllers associated with the /auth route
|
||||
const authRESTController = new AuthRESTController(dependencies)
|
||||
authRESTController.attach(app)
|
||||
if (!this.config.noMongo) {
|
||||
// Attach the REST API Controllers associated with the /auth route
|
||||
const authRESTController = new AuthRESTController(dependencies)
|
||||
authRESTController.attach(app)
|
||||
|
||||
// Attach the REST API Controllers associated with the /user route
|
||||
const userRouter = new UserRouter(dependencies)
|
||||
userRouter.attach(app)
|
||||
// Attach the REST API Controllers associated with the /user route
|
||||
const userRouter = new UserRouter(dependencies)
|
||||
userRouter.attach(app)
|
||||
}
|
||||
|
||||
// Attach the REST API Controllers associated with the /contact route
|
||||
const contactRESTController = new ContactRESTController(dependencies)
|
||||
@@ -57,6 +62,10 @@ class RESTControllers {
|
||||
const logsRESTController = new LogsRESTController(dependencies)
|
||||
logsRESTController.attach(app)
|
||||
|
||||
// Attach the REST API Controllers associated with the /ipfs route
|
||||
const ipfsRESTController = new IpfsRESTController(dependencies)
|
||||
ipfsRESTController.attach(app)
|
||||
|
||||
// Attach the REST API Controllers associated with the /entry route
|
||||
const entryRouter = new EntryRouter(dependencies)
|
||||
entryRouter.attach(app)
|
||||
@@ -72,4 +81,4 @@ class RESTControllers {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RESTControllers
|
||||
export default RESTControllers
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
REST API Controller library for the /ipfs route
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
|
||||
// Local libraries
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
class IpfsRESTControllerLib {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating /ipfs REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating /ipfs REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
// this.UserModel = this.adapters.localdb.Users
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
// Bind 'this' object to all subfunctions
|
||||
this.getStatus = this.getStatus.bind(this)
|
||||
this.getPeers = this.getPeers.bind(this)
|
||||
this.getRelays = this.getRelays.bind(this)
|
||||
this.handleError = this.handleError.bind(this)
|
||||
this.connect = this.connect.bind(this)
|
||||
this.getThisNode = this.getThisNode.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /ipfs Get status on IPFS infrastructure
|
||||
* @apiPermission public
|
||||
* @apiName GetIpfsStatus
|
||||
* @apiGroup REST BCH
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs
|
||||
*
|
||||
*/
|
||||
async getStatus (ctx) {
|
||||
try {
|
||||
const status = await this.adapters.ipfs.getStatus()
|
||||
|
||||
ctx.body = { status }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in ipfs/controller.js/getStatus(): ')
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Return information on IPFS peers this node is connected to.
|
||||
async getPeers (ctx) {
|
||||
try {
|
||||
const showAll = ctx.request.body.showAll
|
||||
|
||||
const peers = await this.adapters.ipfs.getPeers(showAll)
|
||||
|
||||
ctx.body = { peers }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in ipfs/controller.js/getPeers(): ')
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get data about the known Circuit Relays. Hydrate with data from peers list.
|
||||
async getRelays (ctx) {
|
||||
try {
|
||||
const relays = await this.adapters.ipfs.getRelays()
|
||||
|
||||
ctx.body = { relays }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in ipfs/controller.js/getRelays(): ')
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
async connect (ctx) {
|
||||
try {
|
||||
const multiaddr = ctx.request.body.multiaddr
|
||||
const getDetails = ctx.request.body.getDetails
|
||||
|
||||
// console.log('this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs: ', this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs)
|
||||
const result = await this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.adapters.ipfs.connectToPeer({ multiaddr, getDetails })
|
||||
// console.log('result: ', result)
|
||||
|
||||
ctx.body = result
|
||||
} catch (err) {
|
||||
wlogger.error('Error in ipfs/controller.js/connect():', err)
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /ipfs/node Get a copy of the thisNode object from helia-coord
|
||||
* @apiPermission public
|
||||
* @apiName GetThisNode
|
||||
* @apiGroup REST BCH
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5001/ipfs/node
|
||||
*
|
||||
*/
|
||||
async getThisNode (ctx) {
|
||||
try {
|
||||
const thisNode = this.adapters.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
|
||||
|
||||
ctx.body = { thisNode }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in ipfs/controller.js/getThisNode(): ')
|
||||
// ctx.throw(422, err.message)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
if (err.status) {
|
||||
if (err.message) {
|
||||
ctx.throw(err.status, err.message)
|
||||
} else {
|
||||
ctx.throw(err.status)
|
||||
}
|
||||
} else {
|
||||
// By default use a 422 error if the HTTP status is not specified.
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// module.exports = IpfsRESTControllerLib
|
||||
export default IpfsRESTControllerLib
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
REST API library for the /ipfs route.
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
import IPFSRESTControllerLib from './controller.js'
|
||||
import Validators from '../middleware/validators.js'
|
||||
|
||||
// let _this
|
||||
|
||||
class IpfsRouter {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating IPFS REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating IPFS REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
const dependencies = {
|
||||
adapters: this.adapters,
|
||||
useCases: this.useCases
|
||||
}
|
||||
|
||||
// Encapsulate dependencies.
|
||||
this.ipfsRESTController = new IPFSRESTControllerLib(dependencies)
|
||||
this.validators = new Validators()
|
||||
|
||||
// Instantiate the router and set the base route.
|
||||
const baseUrl = '/ipfs'
|
||||
this.router = new Router({ prefix: baseUrl })
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
attach (app) {
|
||||
if (!app) {
|
||||
throw new Error(
|
||||
'Must pass app object when attaching REST API controllers.'
|
||||
)
|
||||
}
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.get('/', this.ipfsRESTController.getStatus)
|
||||
this.router.post('/peers', this.ipfsRESTController.getPeers)
|
||||
this.router.post('/relays', this.ipfsRESTController.getRelays)
|
||||
this.router.post('/connect', this.ipfsRESTController.connect)
|
||||
this.router.get('/node', this.ipfsRESTController.getThisNode)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
app.use(this.router.allowedMethods())
|
||||
}
|
||||
}
|
||||
|
||||
// module.exports = BchRouter
|
||||
export default IpfsRouter
|
||||
@@ -1,4 +1,4 @@
|
||||
const LogsApiLib = require('../../../adapters/logapi')
|
||||
import LogsApiLib from '../../../adapters/logapi.js'
|
||||
const logsApiLib = new LogsApiLib()
|
||||
let _this
|
||||
|
||||
@@ -62,4 +62,4 @@ class LogsApi {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LogsApi
|
||||
export default LogsApi
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const LogsRESTControllerLib = require('./controller')
|
||||
import LogsRESTControllerLib from './controller.js'
|
||||
|
||||
class LogsRouter {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -52,4 +52,4 @@ class LogsRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LogsRouter
|
||||
export default LogsRouter
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function errorMiddleware () {
|
||||
export default function errorMiddleware () {
|
||||
return async (ctx, next) => {
|
||||
try {
|
||||
await next()
|
||||
@@ -8,4 +8,4 @@ module.exports = function errorMiddleware () {
|
||||
ctx.app.emit('error', err, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
/*
|
||||
REST API validator middleware.
|
||||
|
||||
These are a series of functions that ensure the user making a REST API
|
||||
matches a user in the database (or not). In can do fine-grain user control
|
||||
such as telling the difference between an admin, a normal user, and an
|
||||
anonymous user.
|
||||
|
||||
This middleware is used to gatekeep access to different REST API resources.
|
||||
|
||||
CT 9/17/22:
|
||||
This library was lightly refactored to make it work with the new unit tests.
|
||||
This not and the commented code below can be deleted once it is verified that
|
||||
this refactor did not result in any breaking changes.
|
||||
*/
|
||||
|
||||
const User = require('../../../adapters/localdb/models/users')
|
||||
const config = require('../../../../config')
|
||||
const jwt = require('jsonwebtoken')
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
import User from '../../../adapters/localdb/models/users.js'
|
||||
|
||||
import config from '../../../../config/index.js'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -20,12 +33,10 @@ class Validators {
|
||||
|
||||
async ensureUser (ctx, next) {
|
||||
try {
|
||||
// console.log(`getToken: ${typeof (getToken)}`)
|
||||
const token = _this.getToken(ctx)
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
ctx.throw(401)
|
||||
throw new Error('Token could not be retrieved from header')
|
||||
}
|
||||
|
||||
let decoded = null
|
||||
@@ -34,20 +45,23 @@ class Validators {
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
throw new Error('Could not verify JWT')
|
||||
}
|
||||
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
// console.log('Err: Could not find user.')
|
||||
throw new Error('Could not find user')
|
||||
}
|
||||
|
||||
// return next()
|
||||
return true
|
||||
} catch (error) {
|
||||
ctx.throw(401)
|
||||
// console.log('Ensure user error: ', error)
|
||||
// console.log('ctx: ', ctx)
|
||||
ctx.status = 401
|
||||
ctx.throw(401, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +74,8 @@ class Validators {
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
ctx.throw(401)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Token could not be retrieved from header')
|
||||
}
|
||||
|
||||
let decoded = null
|
||||
@@ -70,22 +85,26 @@ class Validators {
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Could not verify JWT')
|
||||
}
|
||||
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Could not find user')
|
||||
}
|
||||
|
||||
if (ctx.state.user.type !== 'admin') {
|
||||
ctx.throw(401, 'not admin')
|
||||
// ctx.throw(401, 'not admin')
|
||||
throw new Error('User is not an admin')
|
||||
}
|
||||
|
||||
// return next()
|
||||
return true
|
||||
} catch (error) {
|
||||
ctx.status = 401
|
||||
ctx.throw(401, error.message)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +120,8 @@ class Validators {
|
||||
|
||||
if (!token) {
|
||||
// console.log(`Err: Token not provided.`)
|
||||
ctx.throw(401)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Token could not be retrieved from header')
|
||||
}
|
||||
|
||||
// The user ID targeted in this API call.
|
||||
@@ -114,14 +134,16 @@ class Validators {
|
||||
// console.log(`config: ${JSON.stringify(config, null, 2)}`)
|
||||
decoded = _this.jwt.verify(token, config.token)
|
||||
} catch (err) {
|
||||
// console.log(`Err: Token could not be decoded: ${err}`)
|
||||
ctx.throw(401)
|
||||
console.log(`Err: Token could not be decoded: ${err}`)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Could not verify JWT')
|
||||
}
|
||||
|
||||
ctx.state.user = await _this.User.findById(decoded.id, '-password')
|
||||
if (!ctx.state.user) {
|
||||
// console.log(`Err: Could not find user.`)
|
||||
ctx.throw(401)
|
||||
// ctx.throw(401)
|
||||
throw new Error('Could not find user')
|
||||
}
|
||||
// console.log('ctx.state.user: ', ctx.state.user)
|
||||
|
||||
@@ -135,7 +157,8 @@ class Validators {
|
||||
|
||||
// If they don't match, then the calling user better be an admin.
|
||||
if (ctx.state.user.type !== 'admin') {
|
||||
ctx.throw(401, 'not admin')
|
||||
// ctx.throw(401, 'not admin')
|
||||
throw new Error('User is not an admin')
|
||||
} else {
|
||||
wlogger.verbose("It's ok. The user is an admin.")
|
||||
}
|
||||
@@ -144,6 +167,8 @@ class Validators {
|
||||
// return next()
|
||||
return true
|
||||
} catch (error) {
|
||||
// console.log('Error in ensureTargetUserOrAdmin(): ', error)
|
||||
ctx.status = 401
|
||||
ctx.throw(401, error.message)
|
||||
}
|
||||
}
|
||||
@@ -166,4 +191,4 @@ class Validators {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Validators
|
||||
export default Validators
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
REST API Controller library for the /offer route
|
||||
*/
|
||||
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -49,14 +49,47 @@ class OfferRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list
|
||||
// curl -X GET http://localhost:5700/offer/list/all/0
|
||||
async listOffers (ctx) {
|
||||
try {
|
||||
const offers = await _this.useCases.offer.listOffers()
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listOffers REST API handler.')
|
||||
console.log('Error in listOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list/nft/0
|
||||
async listNftOffers (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listNftOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listNftOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/offer/list/fungible/0
|
||||
async listFungibleOffers (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listFungibleOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in Fungible REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
@@ -95,4 +128,4 @@ class OfferRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferRESTControllerLib
|
||||
export default OfferRESTControllerLib
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const OfferRESTControllerLib = require('./controller')
|
||||
import OfferRESTControllerLib from './controller.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -50,8 +50,10 @@ class OfferRouter {
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.offerRESTController.createOffer)
|
||||
this.router.get('/list', _this.offerRESTController.listOffers)
|
||||
this.router.post('/take', _this.offerRESTController.takeOffer)
|
||||
this.router.get('/list/all/:page', _this.offerRESTController.listOffers)
|
||||
this.router.get('/list/nft/:page', _this.offerRESTController.listNftOffers)
|
||||
this.router.get('/list/fungible/:page', _this.offerRESTController.listFungibleOffers)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
@@ -59,4 +61,4 @@ class OfferRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferRouter
|
||||
export default OfferRouter
|
||||
|
||||
@@ -35,6 +35,7 @@ class OrderRESTControllerLib {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const orderObj = ctx.request.body.order
|
||||
console.log('orderObj: ', orderObj)
|
||||
|
||||
const hash = await _this.useCases.order.createOrder(orderObj)
|
||||
|
||||
@@ -47,6 +48,41 @@ class OrderRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
// curl -X GET http://localhost:5700/order/list/all/0
|
||||
async listOrders (ctx) {
|
||||
try {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.order.listOrders(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listOrders REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an existing order by returning the token to the root address of
|
||||
// the DEX wallet.
|
||||
async deleteOrder (ctx) {
|
||||
try {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const p2wdbHash = ctx.request.body.p2wdbHash
|
||||
// console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
const txid = await _this.useCases.order.deleteOrder(p2wdbHash)
|
||||
|
||||
ctx.body = { txid }
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log('err: ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
console.log('err', err)
|
||||
@@ -65,4 +101,4 @@ class OrderRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OrderRESTControllerLib
|
||||
export default OrderRESTControllerLib
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const OrderRESTControllerLib = require('./controller')
|
||||
import OrderRESTControllerLib from './controller.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -50,6 +50,8 @@ class OrderRouter {
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.orderRESTController.createOrder)
|
||||
this.router.get('/list/all/:page', _this.orderRESTController.listOrders)
|
||||
this.router.post('/delete', _this.orderRESTController.deleteOrder)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
@@ -57,4 +59,4 @@ class OrderRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OrderRouter
|
||||
export default OrderRouter
|
||||
|
||||
@@ -45,12 +45,23 @@ class P2WDBRESTControllerLib {
|
||||
const counterOffer = ctx.request.body
|
||||
|
||||
await _this.useCases.offer.acceptCounterOffer(counterOffer)
|
||||
|
||||
//
|
||||
} else if (dataType.includes('offer')) {
|
||||
// Detect and handle new Offers
|
||||
console.log('offer data detected')
|
||||
|
||||
const offerObj = ctx.request.body
|
||||
await _this.useCases.offer.createOffer(offerObj)
|
||||
|
||||
//
|
||||
} else if (dataType.includes('flag')) {
|
||||
// Detect and handle data generated by users flagging NSFW Offers.
|
||||
const flagData = ctx.request.body
|
||||
console.log('Flag type data received: ', flagData)
|
||||
|
||||
// const p2wdbHash = flagData.p2wdbHash
|
||||
await _this.useCases.offer.flagOffer(flagData)
|
||||
} else {
|
||||
console.log('Could not route P2WDB webhook data.')
|
||||
}
|
||||
@@ -90,4 +101,4 @@ class P2WDBRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = P2WDBRESTControllerLib
|
||||
export default P2WDBRESTControllerLib
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const P2WDBRESTControllerLib = require('./controller')
|
||||
import P2WDBRESTControllerLib from './controller.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -59,4 +59,4 @@ class P2WDBRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = P2WDBRouter
|
||||
export default P2WDBRouter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
REST API Controller library for the /user route
|
||||
*/
|
||||
|
||||
const { wlogger } = require('../../../adapters/wlogger')
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -36,7 +36,7 @@ class UserRESTControllerLib {
|
||||
* @apiGroup REST Users
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5001/users
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5010/users
|
||||
*
|
||||
* @apiParam {Object} user User object (required)
|
||||
* @apiParam {String} user.email Email
|
||||
@@ -124,7 +124,7 @@ class UserRESTControllerLib {
|
||||
|
||||
ctx.body = { users }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in users/controller.js/getUsers(): '.err)
|
||||
wlogger.error('Error in users/controller.js/getUsers(): ', err)
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class UserRESTControllerLib {
|
||||
* @apiGroup REST Users
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5000/users/56bd1da600a526986cf65c80
|
||||
* curl -H "Content-Type: application/json" -X GET localhost:5010/users/56bd1da600a526986cf65c80
|
||||
*
|
||||
* @apiSuccess {Object} users User object
|
||||
* @apiSuccess {ObjectId} users._id User id
|
||||
@@ -281,4 +281,4 @@ class UserRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserRESTControllerLib
|
||||
export default UserRESTControllerLib
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
import Router from 'koa-router'
|
||||
|
||||
// Local libraries.
|
||||
const UserRESTControllerLib = require('./controller')
|
||||
const Validators = require('../middleware/validators')
|
||||
import UserRESTControllerLib from './controller.js'
|
||||
|
||||
import Validators from '../middleware/validators.js'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -85,4 +86,4 @@ class UserRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserRouter
|
||||
export default UserRouter
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
kicked off periodicially.
|
||||
*/
|
||||
|
||||
// Used to retain scope of 'this', when the scope is lost.
|
||||
let _this
|
||||
import config from '../../config/index.js'
|
||||
|
||||
class TimerControllers {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -23,22 +22,39 @@ class TimerControllers {
|
||||
}
|
||||
|
||||
this.debugLevel = localConfig.debugLevel
|
||||
this.config = config
|
||||
|
||||
_this = this
|
||||
// Bind 'this' object to all subfunctions
|
||||
this.startTimers = this.startTimers.bind(this)
|
||||
this.gcOrders = this.gcOrders.bind(this)
|
||||
this.gcOffers = this.gcOffers.bind(this)
|
||||
this.checkDupOffers = this.checkDupOffers.bind(this)
|
||||
|
||||
// State
|
||||
this.gcOrdersInt = null
|
||||
this.gcOffersInt = null
|
||||
this.checkDupOffersInt = null
|
||||
|
||||
this.startTimers()
|
||||
}
|
||||
|
||||
// Start all the time-based controllers.
|
||||
startTimers () {
|
||||
setInterval(this.gcOrders, 60000 * 5)
|
||||
setInterval(this.gcOffers, 60000 * 5)
|
||||
this.gcOrdersInt = setInterval(this.gcOrders, 60000 * 5)
|
||||
this.gcOffersInt = setInterval(this.gcOffers, 60000 * 5)
|
||||
this.checkDupOffersInt = setInterval(this.checkDupOffers, 60000 * 4.5)
|
||||
}
|
||||
|
||||
stopTimers () {
|
||||
clearInterval(this.gcOrdersInt)
|
||||
clearInterval(this.gcOffersInt)
|
||||
clearInterval(this.checkDupOffers)
|
||||
}
|
||||
|
||||
// Garbage Collect the Orders.
|
||||
gcOrders () {
|
||||
try {
|
||||
_this.useCases.order.removeStaleOrders()
|
||||
this.useCases.order.removeStaleOrders()
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/gcOrders(): ', err)
|
||||
@@ -48,12 +64,22 @@ class TimerControllers {
|
||||
// Garbage Collect the Offers.
|
||||
gcOffers () {
|
||||
try {
|
||||
_this.useCases.offer.removeStaleOffers()
|
||||
this.useCases.offer.removeStaleOffers()
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/gcOffers(): ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicate Offers
|
||||
checkDupOffers () {
|
||||
try {
|
||||
this.useCases.offer.removeDuplicateOffers()
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/checkDupOffers(): ', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimerControllers
|
||||
export default TimerControllers
|
||||
|
||||
@@ -38,4 +38,4 @@ class Entry {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Entry
|
||||
export default Entry
|
||||
|
||||
@@ -67,9 +67,9 @@ class OfferEntity {
|
||||
if (!makerAddr || typeof makerAddr !== 'string') {
|
||||
throw new Error("Property 'makerAddr' must be a string.")
|
||||
}
|
||||
if (!ticker || typeof ticker !== 'string') {
|
||||
throw new Error("Property 'ticker' must be a string.")
|
||||
}
|
||||
// if (!ticker || typeof ticker !== 'string') {
|
||||
// throw new Error("Property 'ticker' must be a string.")
|
||||
// }
|
||||
if (!tokenType || typeof tokenType !== 'number') {
|
||||
throw new Error("Property 'tokenType' must be a number.")
|
||||
}
|
||||
@@ -103,4 +103,4 @@ class OfferEntity {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferEntity
|
||||
export default OfferEntity
|
||||
|
||||
+11
-9
@@ -7,7 +7,10 @@
|
||||
The Order tracks the hdIndex address used to hold tokens or BCH for sale.
|
||||
*/
|
||||
class Order {
|
||||
validate (data) {
|
||||
// TODO: Create a fullValidate() function that validates a fully-hydrated
|
||||
// order model.
|
||||
|
||||
inputValidate (data) {
|
||||
const {
|
||||
messageType,
|
||||
messageClass,
|
||||
@@ -16,8 +19,7 @@ class Order {
|
||||
rateInBaseUnit,
|
||||
minUnitsToExchange,
|
||||
numTokens,
|
||||
makerAddr,
|
||||
ticker
|
||||
makerAddr
|
||||
} = data
|
||||
|
||||
// Input Validation
|
||||
@@ -45,9 +47,9 @@ class Order {
|
||||
if (!makerAddr || typeof makerAddr !== 'string') {
|
||||
throw new Error("Property 'makerAddr' must be a string.")
|
||||
}
|
||||
if (!ticker || typeof ticker !== 'string') {
|
||||
throw new Error("Property 'ticker' must be a string.")
|
||||
}
|
||||
// if (!ticker || typeof ticker !== 'string') {
|
||||
// throw new Error("Property 'ticker' must be a string.")
|
||||
// }
|
||||
|
||||
const offerData = {
|
||||
messageType,
|
||||
@@ -57,12 +59,12 @@ class Order {
|
||||
rateInBaseUnit,
|
||||
minUnitsToExchange,
|
||||
numTokens,
|
||||
makerAddr,
|
||||
ticker
|
||||
makerAddr
|
||||
// ticker
|
||||
}
|
||||
|
||||
return offerData
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Order
|
||||
export default Order
|
||||
|
||||
@@ -21,4 +21,4 @@ class User {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User
|
||||
export default User
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
|
||||
const EntryEntiy = require('../entities/entry')
|
||||
// local libraries
|
||||
import wlogger from '../adapters/wlogger.js'
|
||||
import EntryEntiy from '../entities/entry.js'
|
||||
|
||||
class EntryLib {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('User localConfig: ', localConfig)
|
||||
@@ -63,4 +65,4 @@ class EntryLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntryLib
|
||||
export default EntryLib
|
||||
|
||||
+5
-11
@@ -4,10 +4,10 @@
|
||||
https://troutsblog.com/blog/clean-architecture
|
||||
*/
|
||||
|
||||
const UserUseCases = require('./user')
|
||||
const EntryUseCases = require('./entry')
|
||||
const OfferUseCases = require('./offer')
|
||||
const OrderUseCases = require('./order')
|
||||
import UserUseCases from './user.js'
|
||||
import EntryUseCases from './entry.js'
|
||||
import OfferUseCases from './offer/index.js'
|
||||
import OrderUseCases from './order.js'
|
||||
|
||||
class UseCases {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -28,16 +28,10 @@ class UseCases {
|
||||
|
||||
// Run any startup Use Cases at the start of the app.
|
||||
async start () {
|
||||
// try {
|
||||
console.log('Async Use Cases have been started.')
|
||||
|
||||
return true
|
||||
// } catch (err) {
|
||||
// console.error('Error in use-cases/index.js/start()')
|
||||
// // console.log(err)
|
||||
// throw err
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UseCases
|
||||
export default UseCases
|
||||
|
||||
+266
-30
@@ -13,9 +13,17 @@
|
||||
a partially signed transaction.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import axios from 'axios'
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
|
||||
// Local libraries
|
||||
const OfferEntity = require('../../entities/offer')
|
||||
const config = require('../../../config')
|
||||
import OfferEntity from '../../entities/offer.js'
|
||||
import config from '../../../config/index.js'
|
||||
|
||||
const DEFAULT_ENTRIES_PER_PAGE = 20
|
||||
const NFT_ENTRIES_PER_PAGE = 6
|
||||
const FUNGIBLE_ENTRIES_PER_PAGE = 20
|
||||
|
||||
class OfferUseCases {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -35,9 +43,13 @@ class OfferUseCases {
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.config = config
|
||||
|
||||
this.axios = axios
|
||||
this.offerEntity = new OfferEntity()
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
|
||||
// Bind 'this' object to functions
|
||||
this.detectNsfw = this.detectNsfw.bind(this)
|
||||
}
|
||||
|
||||
// This method is called by the POST /offer REST API controller, which is
|
||||
@@ -65,14 +77,15 @@ class OfferUseCases {
|
||||
|
||||
// Verify that UTXO in offer is unspent. If it is spent, then ignore the
|
||||
// offer.
|
||||
const txid = offerObj.data.utxoTxid
|
||||
const vout = offerObj.data.utxoVout
|
||||
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
txid,
|
||||
vout
|
||||
)
|
||||
const utxo = {
|
||||
tx_hash: offerObj.data.utxoTxid,
|
||||
tx_pos: offerObj.data.utxoVout
|
||||
}
|
||||
// const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
const utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
if (utxoStatus === null) return false
|
||||
// if (utxoStatus === null) return false
|
||||
if (!utxoStatus) return false
|
||||
|
||||
// A new offer gets a status of 'posted'
|
||||
offerObj.data.offerStatus = 'posted'
|
||||
@@ -80,6 +93,24 @@ class OfferUseCases {
|
||||
const offerEntity = this.offerEntity.validate(offerObj)
|
||||
console.log('offerEntity: ', offerEntity)
|
||||
|
||||
// Get data about the token.
|
||||
const tokenId = offerEntity.tokenId
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
// Generate a 'display category' for the token. This will allow the
|
||||
// front end UI to figure out how to display the token.
|
||||
const displayCategory = this.categorizeToken(offerEntity, tokenData)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
|
||||
// Detect if user set the NSFW flag.
|
||||
let nsfw = false
|
||||
// nsfw = await this.detectNsfw(tokenData)
|
||||
nsfw = await this.retryQueue.addToQueue(this.detectNsfw, tokenData)
|
||||
offerEntity.nsfw = nsfw
|
||||
|
||||
// Add offer to the local database.
|
||||
const offerModel = new this.OfferModel(offerEntity)
|
||||
await offerModel.save()
|
||||
@@ -91,15 +122,146 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
async listOffers () {
|
||||
// By default this function returns false, to indicate the NFT is safe for work.
|
||||
// If the user who created the token sets the nsfw property in the mutable data,
|
||||
// this function will return true.
|
||||
async detectNsfw (tokenData) {
|
||||
try {
|
||||
return this.OfferModel.find({}).sort('-timestamp')
|
||||
let nsfw = false
|
||||
|
||||
const mutableCid = tokenData.mutableData
|
||||
|
||||
// If there is no mutable IPFS CID, then skip this token.
|
||||
if (!mutableCid.includes('ipfs://')) return nsfw
|
||||
|
||||
// Revove the ipfs:// prefix.
|
||||
const cid = mutableCid.substring(7)
|
||||
|
||||
// Retrieve the mutable data from Filecoin/IPFS.
|
||||
// const url = `https://${cid}.ipfs.w3s.link/data.json`
|
||||
const url = `${this.config.ipfsGateway}${cid}/data.json`
|
||||
const result = await axios.get(url)
|
||||
const mutableData = result.data
|
||||
console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
|
||||
// Logical tests
|
||||
const hasNsfw = !!mutableData.nsfw
|
||||
const nsfwSetTrue = mutableData.nsfw === true
|
||||
const nsfwStringTrue = mutableData.nsfw === 'true'
|
||||
const nsfwDetected = hasNsfw && (nsfwSetTrue || nsfwStringTrue)
|
||||
|
||||
if (nsfwDetected) {
|
||||
console.log('NSFW flag set as true')
|
||||
nsfw = true
|
||||
}
|
||||
|
||||
return nsfw
|
||||
} catch (err) {
|
||||
console.error('Error in detectNsfw(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Categorize the token for display purposes. This will categorize a token
|
||||
// into one of these categories:
|
||||
// - nft
|
||||
// - group
|
||||
// - fungible
|
||||
// - simple-nft
|
||||
//
|
||||
// The first three are easy to categorize. The simple-nft is a fungible token
|
||||
// with a quantity of 1, decimals of 0, and no minting baton. Categorizing this
|
||||
// type of token is the main reason why this function exists.
|
||||
categorizeToken (offerData, tokenData) {
|
||||
try {
|
||||
// console.log(`categorizeToken(): ${JSON.stringify(offerData, null, 2)}`)
|
||||
|
||||
// const tokenId = offerData.tokenId
|
||||
//
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
if (tokenData.genesisData.type === 65) {
|
||||
return 'nft'
|
||||
}
|
||||
|
||||
// Create a set of checks to detect a simple NFT
|
||||
const isType1 = tokenData.genesisData.type === 1
|
||||
const hasNoMintingBaton = !tokenData.genesisData.mintBatonIsActive
|
||||
const hasNoDecimals = !tokenData.genesisData.decimals
|
||||
const hasQtyOfOne = parseInt(tokenData.genesisData.tokensInCirculationStr) === 1
|
||||
|
||||
if (isType1 && hasNoMintingBaton && hasNoDecimals && hasQtyOfOne) {
|
||||
return 'simple-nft'
|
||||
}
|
||||
|
||||
if (isType1) return 'fungible'
|
||||
|
||||
throw new Error(`Unknown token type: ${tokenData.genesisData.type}`)
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/offer/index.js categorizeToken(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async listOffers (page = 0) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({})
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * DEFAULT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(DEFAULT_ENTRIES_PER_PAGE)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async listNftOffers (page = 0, nsfw = false) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({
|
||||
displayCategory: { $ne: 'fungible' },
|
||||
nsfw
|
||||
})
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * NFT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(NFT_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listNftOffers() returning this data: ', data)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listNftOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async listFungibleOffers (page = 0) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({ displayCategory: 'fungible' })
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listFungibleOffers() returning this data: ', data)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listFungibleOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Generate phase 2 of 3 - take the other side of an Offer.
|
||||
// Based on this example:
|
||||
// https://github.com/Permission=less-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js
|
||||
@@ -122,15 +284,14 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// Verify that UTXO for sale is unspent. Abort if it's been spent.
|
||||
const txid = offerInfo.utxoTxid
|
||||
const vout = offerInfo.utxoVout
|
||||
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
txid,
|
||||
vout
|
||||
)
|
||||
const utxo = {
|
||||
tx_hash: offerInfo.utxoTxid,
|
||||
tx_pos: offerInfo.utxoVout
|
||||
}
|
||||
const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
if (utxoStatus === null) {
|
||||
console.log(`utxo txid: ${txid}, vout: ${vout}`)
|
||||
if (!utxoStatus) {
|
||||
console.log(`utxo txid: ${offerInfo.utxoTxid}, vout: ${offerInfo.utxoVout}`)
|
||||
|
||||
// TODO: Mark this Offer as 'dead'
|
||||
|
||||
@@ -145,7 +306,7 @@ class OfferUseCases {
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
// Calculate amount of sats to generate a counter offer.
|
||||
let satsToMove = offerInfo.numTokens * parseInt(offerInfo.rateInBaseUnit)
|
||||
let satsToMove = Math.ceil(offerInfo.numTokens * parseInt(offerInfo.rateInBaseUnit))
|
||||
if (isNaN(satsToMove)) {
|
||||
throw new Error('Could not calculate the amount of BCH to generate counter offer')
|
||||
}
|
||||
@@ -212,6 +373,9 @@ class OfferUseCases {
|
||||
await this.adapters.wallet.bchWallet.walletInfoPromise
|
||||
// console.log(`utxos: ${JSON.stringify(this.adapters.wallet.bchWallet.utxos.utxoStore, null, 2)}`)
|
||||
|
||||
// Refresh the wallet UTXOs
|
||||
await this.adapters.wallet.bchWallet.initialize()
|
||||
|
||||
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||
const canWriteToP2WDB = await this.adapters.p2wdb.checkForSufficientFunds(wif)
|
||||
@@ -221,7 +385,7 @@ class OfferUseCases {
|
||||
// Sell Offer
|
||||
|
||||
// Calculate the sats needed
|
||||
const satsNeeded = offerEntity.numTokens * parseInt(offerEntity.rateInBaseUnit)
|
||||
const satsNeeded = Math.ceil(offerEntity.numTokens * parseInt(offerEntity.rateInBaseUnit))
|
||||
if (isNaN(satsNeeded)) {
|
||||
throw new Error('Could not calculate sats needed!')
|
||||
}
|
||||
@@ -303,7 +467,7 @@ class OfferUseCases {
|
||||
console.log(`txObj: ${JSON.stringify(txObj, null, 2)}`)
|
||||
|
||||
// Ensure the 3rd output (vout=2) contains the required amount of BCH.
|
||||
const satsToReceive = orderData.numTokens * parseInt(orderData.rateInBaseUnit)
|
||||
const satsToReceive = Math.ceil(orderData.numTokens * parseInt(orderData.rateInBaseUnit))
|
||||
if (isNaN(satsToReceive)) {
|
||||
throw new Error('Could not calculate the amount of BCH offered in the Counter Offer')
|
||||
}
|
||||
@@ -333,6 +497,45 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Write unit tests for this function, then add it to the Timer Controllers
|
||||
// Looks for duplicate offers and removes the duplicate.
|
||||
// Duplicates are checked for when an offer is created, and so should not
|
||||
// exist. But emperical testing shows that they do. This function is called
|
||||
// periodically by a timer, to clean up any duplicates that slipped through
|
||||
// the cracks.
|
||||
async removeDuplicateOffers () {
|
||||
try {
|
||||
const now = new Date()
|
||||
console.log(`Starting removeDuplicateOffers() at ${now.toLocaleString()}`)
|
||||
|
||||
let duplicateFound = false
|
||||
|
||||
// Get all Offers in the database.
|
||||
const offers = await this.OfferModel.find({})
|
||||
// console.log('offers: ', offers)
|
||||
|
||||
const offerZcids = []
|
||||
|
||||
for (let i = 0; i < offers.length; i++) {
|
||||
const thisOffer = offers[i]
|
||||
|
||||
if (offerZcids.includes(thisOffer.p2wdbHash)) {
|
||||
await thisOffer.remove()
|
||||
duplicateFound = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the zcid to the array.
|
||||
offerZcids.push(thisOffer.p2wdbHash)
|
||||
}
|
||||
|
||||
return duplicateFound
|
||||
} catch (err) {
|
||||
console.error('Error in removeDuplicateOffers()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called by the garbage collection timer controller. It
|
||||
// checks the UTXO associated with each Offer in the database. If the UTXO
|
||||
// has been spent, the Offer is deleted from the database.
|
||||
@@ -352,10 +555,14 @@ class OfferUseCases {
|
||||
let utxoStatus = null
|
||||
try {
|
||||
// Get the status of the UTXO associate with this Offer.
|
||||
utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
thisOffer.utxoTxid,
|
||||
thisOffer.utxoVout
|
||||
)
|
||||
const utxo = {
|
||||
tx_hash: thisOffer.utxoTxid,
|
||||
tx_pos: thisOffer.utxoVout
|
||||
}
|
||||
// console.log(`Checking this UTXO: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
// utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Offer model.
|
||||
@@ -364,7 +571,7 @@ class OfferUseCases {
|
||||
await thisOffer.remove()
|
||||
continue
|
||||
} else if (err.isAxiosError) {
|
||||
console.log('Error trying to contact api.fullstack.cash')
|
||||
console.log('Error trying to contact wallet service: ', err)
|
||||
continue
|
||||
} else {
|
||||
throw err
|
||||
@@ -372,7 +579,8 @@ class OfferUseCases {
|
||||
}
|
||||
|
||||
// If the Offer UTXO is spent, delete the Offer model.
|
||||
if (utxoStatus === null) {
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Offer: ${JSON.stringify(thisOffer, null, 2)}`)
|
||||
await thisOffer.remove()
|
||||
}
|
||||
@@ -391,6 +599,34 @@ class OfferUseCases {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async flagOffer (flagData) {
|
||||
console.log(`flagData: ${JSON.stringify(flagData, null, 2)}`)
|
||||
|
||||
const p2wdbHash = flagData.data.p2wdbHash
|
||||
|
||||
// Get the offer from the database.
|
||||
const offer = await this.findOfferByHash(p2wdbHash)
|
||||
console.log(`Flagging this offer: ${JSON.stringify(offer, null, 2)}`)
|
||||
|
||||
if (!offer) {
|
||||
throw new Error(`Offer ${p2wdbHash} not found in the database.`)
|
||||
}
|
||||
|
||||
// Add the raw flag data to the database model.
|
||||
offer.flags.push(flagData)
|
||||
|
||||
// If flag count is 3 or more, mark the Offer as NSFW
|
||||
const flagCnt = offer.flags.length
|
||||
if (flagCnt >= 3) {
|
||||
offer.nsfw = true
|
||||
}
|
||||
|
||||
// Save the updated offer data to the database.
|
||||
await offer.save()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferUseCases
|
||||
export default OfferUseCases
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
pay-to-write-access-controller.js depends on this library.
|
||||
*/
|
||||
|
||||
const PQueue = require('p-queue').default
|
||||
const pRetry = require('p-retry')
|
||||
// Global npm libraries
|
||||
import PQueue from 'p-queue'
|
||||
import pRetry from 'p-retry'
|
||||
|
||||
let _this
|
||||
|
||||
@@ -103,4 +104,4 @@ class RetryQueue {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RetryQueue
|
||||
export default RetryQueue
|
||||
|
||||
+89
-26
@@ -2,10 +2,15 @@
|
||||
Order use-case library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
|
||||
// Local libraries
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
const OrderEntity = require('../entities/order')
|
||||
const config = require('../../config')
|
||||
import wlogger from '../adapters/wlogger.js'
|
||||
import OrderEntity from '../entities/order.js'
|
||||
import config from '../../config/index.js'
|
||||
|
||||
const DEFAULT_ENTRIES_PER_PAGE = 20
|
||||
|
||||
class OrderLib {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -22,45 +27,55 @@ class OrderLib {
|
||||
this.OrderModel = this.adapters.localdb.Order
|
||||
this.bch = this.adapters.bch
|
||||
this.config = config
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
|
||||
// Bind subfunctions to the 'this' object.
|
||||
this.ensureFunds = this.ensureFunds.bind(this)
|
||||
}
|
||||
|
||||
// Create a new order model and add it to the Mongo database.
|
||||
async createOrder (entryObj) {
|
||||
try {
|
||||
console.log('createOrder(entryObj): ', entryObj)
|
||||
// if (!entryObj) return
|
||||
if (!entryObj) return false
|
||||
|
||||
if (!entryObj.tokenId) throw new Error('entry does not contain required properties')
|
||||
|
||||
// Specify the address to send payment.
|
||||
entryObj.makerAddr = this.adapters.wallet.bchWallet.walletInfo.cashAddress
|
||||
console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
|
||||
// Get Ticker for token ID.
|
||||
// TODO: Move this below the orderEntity.validate() call.
|
||||
const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId])
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
entryObj.ticker = tokenData[0].tokenTicker
|
||||
|
||||
// Input Validation
|
||||
// TODO: Remove ticker from validation.
|
||||
// TODO: Rename validate() to inputValidate(). Create fullValidate() that
|
||||
// validates a fully hydrated Order entity.
|
||||
const orderEntity = this.orderEntity.validate(entryObj)
|
||||
const orderEntity = this.orderEntity.inputValidate(entryObj)
|
||||
console.log('orderEntity: ', orderEntity)
|
||||
|
||||
// Optimize the wallet to speed up working with it.
|
||||
console.log('Optimizing wallet before creating new order.')
|
||||
await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.optimize, {})
|
||||
|
||||
// Ensure sufficient tokens exist to create the order.
|
||||
await this.ensureFunds(orderEntity)
|
||||
// await this.ensureFunds(orderEntity)
|
||||
await this.retryQueue.addToQueue(this.ensureFunds, orderEntity)
|
||||
|
||||
// Get Ticker for token ID.
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId])
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTxData, [entryObj.tokenId])
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
orderEntity.ticker = tokenData[0].tokenTicker
|
||||
|
||||
// Move the tokens to holding address.
|
||||
const moveObj = {
|
||||
tokenId: orderEntity.tokenId,
|
||||
qty: orderEntity.numTokens
|
||||
}
|
||||
const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
// const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
const utxoInfo = await this.retryQueue.addToQueue(this.adapters.wallet.moveTokens, moveObj)
|
||||
console.log('utxoInfo: ', utxoInfo)
|
||||
|
||||
// Update the UTXO store for the wallet.
|
||||
await this.adapters.wallet.bchWallet.bchjs.Util.sleep(3000)
|
||||
await this.adapters.wallet.bchWallet.getUtxos()
|
||||
// await this.adapters.wallet.bchWallet.getUtxos()
|
||||
await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.initialize, {})
|
||||
|
||||
// Update the order with the new UTXO information.
|
||||
orderEntity.utxoTxid = utxoInfo.txid
|
||||
@@ -76,7 +91,8 @@ class OrderLib {
|
||||
data: orderEntity,
|
||||
appId: this.config.p2wdbAppId
|
||||
}
|
||||
const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
// const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
const hash = await this.retryQueue.addToQueue(this.adapters.p2wdb.write, p2wdbObj)
|
||||
// console.log('hash: ', hash)
|
||||
|
||||
// Create a MongoDB model to hold the Order
|
||||
@@ -105,6 +121,7 @@ class OrderLib {
|
||||
|
||||
// Update the wallet balance
|
||||
await this.adapters.wallet.bchWallet.getUtxos()
|
||||
// console.log(`wallet UTXOs: ${JSON.stringify(this.adapters.wallet.bchWallet.utxos.utxoStore, null, 2)}`)
|
||||
|
||||
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||
@@ -113,7 +130,7 @@ class OrderLib {
|
||||
|
||||
// Get UTXOs.
|
||||
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
if (orderEntity.buyOrSell.includes('sell')) {
|
||||
// Sell Order
|
||||
@@ -196,10 +213,18 @@ class OrderLib {
|
||||
let utxoStatus = null
|
||||
try {
|
||||
// Get the status of the UTXO associate with this Order.
|
||||
utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
thisOrder.utxoTxid,
|
||||
thisOrder.utxoVout
|
||||
)
|
||||
// utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||
// thisOrder.utxoTxid,
|
||||
// thisOrder.utxoVout
|
||||
// )
|
||||
|
||||
// Get the status of the UTXO associate with this Order.
|
||||
const utxo = {
|
||||
tx_hash: thisOrder.utxoTxid,
|
||||
tx_pos: thisOrder.utxoVout
|
||||
}
|
||||
// utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
// Handle corner case of bad-data in the Order model.
|
||||
@@ -208,7 +233,7 @@ class OrderLib {
|
||||
await thisOrder.remove()
|
||||
continue
|
||||
} else if (err.isAxiosError) {
|
||||
console.log('Error trying to contact api.fullstack.cash')
|
||||
console.log('Error trying to contact wallet service: ', err)
|
||||
continue
|
||||
} else {
|
||||
throw err
|
||||
@@ -216,7 +241,8 @@ class OrderLib {
|
||||
}
|
||||
|
||||
// If the Order UTXO is spent, delete the Order model.
|
||||
if (utxoStatus === null) {
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Order: ${JSON.stringify(thisOrder, null, 2)}`)
|
||||
await thisOrder.remove()
|
||||
}
|
||||
@@ -226,6 +252,43 @@ class OrderLib {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async listOrders (page = 0) {
|
||||
try {
|
||||
const data = await this.OrderModel.find({})
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * DEFAULT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(DEFAULT_ENTRIES_PER_PAGE)
|
||||
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/order/listOrders()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Delete an Order model by sending the token back to the root address. This
|
||||
// will allow the garbage collectors to delete the Order and the Offer.
|
||||
async deleteOrder (p2wdbHash) {
|
||||
try {
|
||||
console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
// Find the order by the given hash.
|
||||
const order = await this.findOrderByHash(p2wdbHash)
|
||||
console.log('order: ', order)
|
||||
|
||||
// Reclaim the tokens
|
||||
const txid = await this.adapters.wallet.reclaimTokens(order)
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
console.error('Error in use-cases/order/deleteOrder()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OrderLib
|
||||
export default OrderLib
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
functions are called by the /user REST API endpoints.
|
||||
*/
|
||||
|
||||
const UserEntity = require('../entities/user')
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
import UserEntity from '../entities/user.js'
|
||||
|
||||
import wlogger from '../adapters/wlogger.js'
|
||||
|
||||
class UserLib {
|
||||
constructor (localConfig = {}) {
|
||||
@@ -181,4 +182,4 @@ class UserLib {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UserLib
|
||||
export default UserLib
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const axios = require('axios').default
|
||||
import { assert } from 'chai'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
// const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
const config = require('../../../config')
|
||||
const Server = require('../../../bin/server')
|
||||
const testUtils = require('../../utils/test-utils')
|
||||
const AdminLib = require('../../../src/adapters/admin')
|
||||
import config from '../../../config/index.js'
|
||||
import Server from '../../../bin/server.js'
|
||||
import testUtils from '../../utils/test-utils.js'
|
||||
import AdminLib from '../../../src/adapters/admin.js'
|
||||
const adminLib = new AdminLib()
|
||||
|
||||
// const request = supertest.agent(app.listen())
|
||||
@@ -21,105 +23,107 @@ const context = {}
|
||||
|
||||
const LOCALHOST = `http://localhost:${config.port}`
|
||||
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
const app = new Server()
|
||||
if (!config.noMongo) {
|
||||
describe('Auth', () => {
|
||||
before(async () => {
|
||||
const app = new Server()
|
||||
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
// This should be the first instruction. It starts the REST API server.
|
||||
await app.startServer()
|
||||
|
||||
// Stop the IPFS node for the rest of the e2e tests.
|
||||
// await app.controllers.adapters.ipfs.stop()
|
||||
// Stop the IPFS node for the rest of the e2e tests.
|
||||
// await app.controllers.adapters.ipfs.stop()
|
||||
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
// Delete all previous users in the database.
|
||||
await testUtils.deleteAllUsers()
|
||||
|
||||
// Create a new admin user.
|
||||
await adminLib.createSystemUser()
|
||||
// Create a new admin user.
|
||||
await adminLib.createSystemUser()
|
||||
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass',
|
||||
name: 'test'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log('TestUser: ', testUser)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
const userObj = {
|
||||
email: 'test@test.com',
|
||||
password: 'pass',
|
||||
name: 'test'
|
||||
}
|
||||
const testUser = await testUtils.createUser(userObj)
|
||||
// console.log('TestUser: ', testUser)
|
||||
|
||||
context.user = testUser.user
|
||||
context.token = testUser.token
|
||||
})
|
||||
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
describe('POST /auth', () => {
|
||||
it('should throw 401 if credentials are incorrect', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
|
||||
const result = await axios(options)
|
||||
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
console.log(
|
||||
`result stringified: ${JSON.stringify(result.data, null, 2)}`
|
||||
)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
it('should throw 401 if email is wrong format', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'wrongEmail',
|
||||
password: 'wrongpassword'
|
||||
}
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
await axios(options)
|
||||
assert(false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
assert(err.response.status === 401, 'Error code 401 expected.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should auth user', async () => {
|
||||
try {
|
||||
const options = {
|
||||
method: 'post',
|
||||
url: `${LOCALHOST}/auth`,
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
const result = await axios(options)
|
||||
// console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
|
||||
|
||||
assert(result.status === 200, 'Status Code 200 expected.')
|
||||
assert(
|
||||
result.data.user.email === 'test@test.com',
|
||||
'Email of test expected'
|
||||
)
|
||||
assert(
|
||||
result.data.user.password === undefined,
|
||||
'Password expected to be omited'
|
||||
)
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'Error authenticating test user: ' + JSON.stringify(err, null, 2)
|
||||
)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user