Compare commits

..
120 Commits
Author SHA1 Message Date
Chris Troutner 6dbf284876 Merge pull request #35 from Permissionless-Software-Foundation/ct-unstable
fix(webhook): Ensuring startup waits for webhook to be established
2022-07-07 13:46:12 -07:00
Chris Troutner d1e96505c8 fix(order): Handling bad order data 2022-07-07 13:45:01 -07:00
Chris Troutner 1e80a3159e fix(webhook): Ensuring startup waits for webhook to be established 2022-07-07 13:37:16 -07:00
Chris Troutner d3df9546a7 fix(tests): Speeding up e2e tests 2022-07-06 20:56:01 -07:00
Chris Troutner 3422f3146e Merge pull request #34 from Permissionless-Software-Foundation/ct-unstable
feat(sweep-nfts.js): Added script to reclaim NFTs
2022-07-06 18:42:00 -07:00
Chris Troutner 5c4dc21d3e Making e2e tests a little faster 2022-07-06 18:39:45 -07:00
Chris Troutner b74f460a9a feat(sweep-nfts.js): Added script to reclaim NFTs 2022-07-06 18:30:33 -07:00
Chris Troutner 6585ceacc7 Merge pull request #33 from Permissionless-Software-Foundation/ct-unstable
fix(offer): Changing stale threshold from 7 days to 8 weeks
2022-07-06 12:24:01 -07:00
Chris Troutner 942dbedd44 fix(offer): Changing stale threshold from 7 days to 8 weeks 2022-07-06 12:22:15 -07:00
Chris Troutner 825ede0760 Minor text chagne 2022-07-02 13:41:25 -07:00
Chris Troutner 5e4540bd84 Updating docker image 2022-06-29 17:37:37 -07:00
Chris Troutner a3b21d7547 Merge pull request #32 from Permissionless-Software-Foundation/ct-unstable
Using P2WDB v3 + synced with ipfs-service-provider
2022-06-29 17:31:36 -07:00
Chris Troutner 87ece9d6bb fix(production): Improvements to docker files 2022-06-29 17:29:23 -07:00
Chris Troutner 125330f5b0 Fixing broken test 2022-06-29 16:56:12 -07:00
Chris Troutner 6b3fc78fc9 fix(p2wdb): Working with v3 P2WDB 2022-06-29 16:50:36 -07:00
Chris Troutner 3d7d4a57d2 Fixing merge conflicts 2022-06-29 16:29:28 -07:00
Chris Troutner abf07005eb fix(deps): Updating dependencies 2022-06-29 16:03:09 -07:00
Chris Troutner b68a143071 Merge pull request #91 from Permissionless-Software-Foundation/ct-unstable
Added hooks for async startup
2022-06-27 11:33:50 -07:00
Chris Troutner 5c25ebeeca Renaming mock log 2022-06-27 11:32:25 -07:00
Chris Troutner 3b17ba8e8b fix(logapi): Fixing issues with unit tests 2022-06-27 11:29:27 -07:00
Chris Troutner 434a4d7bd6 Refactoring failing unit tests 2022-06-27 10:25:19 -07:00
Chris Troutner dd36ebc7a0 Got test coverage back to 100% 2022-06-27 10:15:35 -07:00
Chris Troutner f117fe15df fix(startup): Added hooks for async startup 2022-06-27 10:09:08 -07:00
Chris Troutner 0333f77f5a Fixing broken link in README 2022-06-20 07:30:10 -07:00
Chris Troutner 3c908d1a4d Merge pull request #90 from Permissionless-Software-Foundation/ct-unstable
Increasing test coverage to 100%
2022-06-20 07:22:10 -07:00
Chris Troutner eb46a4c130 fix(tests): Got to 100% test coverage for entire repo 2022-06-20 07:19:36 -07:00
Chris Troutner d0efdcfa07 fix(ipfs-coord): Increased unit test coverage to 100% 2022-06-20 07:01:43 -07:00
Chris Troutner 62a7fcb16d fix(tests): Got bin/server.js unit test coverage to 100% 2022-06-19 20:21:40 -07:00
Chris Troutner d8addd5b27 Trying to build unit tests for bin/server 2022-06-19 20:00:25 -07:00
Chris Troutner bf6f1cf62d Merge pull request #89 from Permissionless-Software-Foundation/ct-unstable
Updating dependencies
2022-06-19 13:25:07 -07:00
Chris Troutner b7df7c47a5 Updated ipfs 2022-06-19 13:23:52 -07:00
Chris Troutner ae260db4af Updated some dependencies 2022-06-19 13:19:31 -07:00
Chris Troutner 778098ca12 fix(ipfs-coord): Updating to v7.1.19 2022-05-28 09:29:31 -07:00
Chris Troutner 0c4bb9c4a1 fix(ipfs-coord): Updating to v7.1.18 2022-05-28 09:09:44 -07:00
Chris Troutner 9cb975656d Merge pull request #88 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.17
2022-05-28 08:00:57 -07:00
Chris Troutner 4f6564423e fix(ipfs-coord): Updating to v7.1.17 2022-05-28 07:59:47 -07:00
Chris Troutner 54e7e74ae5 Merge pull request #87 from Permissionless-Software-Foundation/ct-unstable
Subscribing to chat channel at startup
2022-05-26 09:14:50 -07:00
Chris Troutner 324c372119 fix(startup): Subscribing to chat pubsub channel at startup 2022-05-26 09:12:15 -07:00
Chris Troutner da62f1bbe3 linting 2022-05-26 08:24:45 -07:00
Chris Troutner 129047fe88 fix(startup): Announcing config settings at startup 2022-05-26 08:24:30 -07:00
Chris Troutner 3f2a12f6ec Merge pull request #31 from Permissionless-Software-Foundation/ct-unstable
fix(rpi): updating UI docker container
2022-05-17 07:10:37 -07:00
Chris Troutner c1b687efd1 fix(rpi): updating UI docker container 2022-05-17 07:07:43 -07:00
Chris Troutner fb182ced58 Merge pull request #86 from Permissionless-Software-Foundation/ct-unstable
Updating docker-compose file
2022-05-16 12:33:03 -07:00
Chris Troutner bec7c11e46 Updating README 2022-05-16 12:31:53 -07:00
Chris Troutner fb10e10226 fix(production): Updating docker images 2022-05-16 12:30:58 -07:00
Chris Troutner 5b37e47747 Merge pull request #85 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to v6.4.1
2022-05-16 12:15:47 -07:00
Chris Troutner a3cea782ba fix(bch-js): Updating to v6.4.1 2022-05-16 12:14:20 -07:00
Chris Troutner 14ffdae194 Merge pull request #84 from Permissionless-Software-Foundation/dh-test-coverage
feat(coverage): Got ipfs-coord.js to 100% test coverage
2022-05-16 12:09:18 -07:00
Chris Troutner faf5a83eb6 Merge pull request #30 from Permissionless-Software-Foundation/ct-unstable
Several small tweeks
2022-05-10 10:54:53 -07:00
Chris Troutner 92a35b220f fix(RPi): Updating docker images 2022-05-09 17:38:48 -07:00
Chris Troutner 58e934eeab fix(docker): Uploaded new PC images 2022-05-09 12:04:05 -07:00
Chris Troutner d5de76729d fix(production): Switching p2wdb to debug level 1 2022-05-09 09:35:31 -07:00
Chris Troutner 805cac97d9 fix(startup): Listing web3 server at startup 2022-05-09 09:33:39 -07:00
Chris Troutner 91fa460158 fix(production scripts): Improvement to sweep-wallet.js 2022-05-09 09:26:17 -07:00
Chris Troutner 2d6055b537 Merge pull request #29 from Permissionless-Software-Foundation/ct-unstable
Updating docker images
2022-05-08 16:19:42 -07:00
Chris Troutner 72e6a37ed1 fix(docker): Updating image files 2022-05-08 16:17:20 -07:00
Chris Troutner 9d18a24c7e Restoring 20s time delay on bch-dex startup 2022-05-08 13:45:01 -07:00
Chris Troutner feacf6e571 Merge pull request #28 from Permissionless-Software-Foundation/ct-unstable
feat(NFT): Adding support for NFTs
2022-05-08 13:24:14 -07:00
Chris Troutner c51f7cdf3a feat(NFT): Adding support for NFTs 2022-05-08 10:45:21 -07:00
Chris Troutner 07fbe8c654 Merge pull request #26 from Permissionless-Software-Foundation/ct-unstable
Updating docker images
2022-05-07 13:13:25 -07:00
Chris Troutner a65ec7494f fix(production): Updating docker images of PC 2022-05-07 13:10:18 -07:00
Chris Troutner 59fca7903f Merge branch 'ct-unstable' of https://github.com/Permissionless-Software-Foundation/bch-dex into ct-unstable 2022-05-07 12:12:59 -07:00
Chris Troutner f8948f12fa fix(rpi): Updating to v1.6.7 of bch-dex Docker container 2022-05-07 12:12:30 -07:00
Chris Troutner b79163cdf7 Merge pull request #25 from Permissionless-Software-Foundation/ct-unstable
Improved startup and error handling
2022-05-07 11:41:52 -07:00
Chris Troutner 2d537994ef Increasing startup time to 20 seconds 2022-05-04 17:00:46 -07:00
Chris Troutner 2ae963ee62 fix(removeStaleOrders): handling network errors 2022-05-04 16:07:24 -07:00
Chris Troutner f5cd59eb12 Merge branch 'ct-unstable' of https://github.com/Permissionless-Software-Foundation/bch-dex into ct-unstable 2022-05-04 16:06:35 -07:00
Chris Troutner d4f9e128b3 fix(removeStaleOrders): Handling network errors 2022-05-04 16:05:58 -07:00
Chris Troutner 3c322ecbd0 Merge pull request #24 from Permissionless-Software-Foundation/ct-unstable
Several minor fixes and enhancements
2022-05-04 06:12:49 -07:00
Chris Troutner 56944176fa fix(sweep-wallet): Got sweeping script working better 2022-05-04 06:03:08 -07:00
Chris Troutner 5f9cdc3f2e fix(startup): Adding 5 sec delay to bch-dex startup 2022-05-03 17:09:19 -07:00
Chris Troutner c20938f9f7 Fixing bug 2022-05-03 16:35:17 -07:00
Chris Troutner 5be3c92a29 fix(order): Better error handling in use-case 2022-05-03 15:33:33 -07:00
Chris Troutner 1476500c61 fix(rpi): Updating docker image to v1.6.6 2022-05-03 15:25:37 -07:00
Chris Troutner 519980eb99 Adding extra error handling to removeStaleOffers() 2022-05-03 14:23:13 -07:00
Chris Troutner 1e4b71aacd fix(production): Switching to newly name UI repo 2022-05-03 07:03:13 -07:00
Chris Troutner 2c1c009b64 fix(removeStaleOffers): Better error handling 2022-05-02 13:31:22 -07:00
Chris Troutner 09cbfd00e8 Merge pull request #23 from Permissionless-Software-Foundation/ct-unstable
Fixing bugs. Updating docker images
2022-05-02 10:54:09 -07:00
Chris Troutner 2c21d79740 fix(rpi): Updating docker images 2022-05-02 10:43:47 -07:00
Chris Troutner 15eb871b1f fix(production): Updating to v1.6.4 of docker images 2022-05-02 09:43:39 -07:00
Chris Troutner f4e80017d6 Merge pull request #22 from Permissionless-Software-Foundation/ct-unstable
fix(orders): Fixing bug with creating orders
2022-05-02 08:32:25 -07:00
Chris Troutner e27b9da2ee fix(orders): Fixing bug with creating orders 2022-05-02 08:29:51 -07:00
Chris Troutner adb210cd60 Merge pull request #21 from Permissionless-Software-Foundation/ct-unstable
Using RPi docker images
2022-05-01 14:57:41 -07:00
Chris Troutner ff7697baf6 fix(RPi): using docker images 2022-05-01 14:39:28 -07:00
Chris Troutner d65c3701cd fix(README): Updating README 2022-05-01 13:13:59 -07:00
Chris Troutner 4303efdb43 fix(README): Updating readme 2022-05-01 13:03:31 -07:00
Chris Troutner 87b1b7f9ca fix(rpi): Updating docker files 2022-05-01 12:45:07 -07:00
Chris Troutner 3dddf21986 Merge pull request #20 from Permissionless-Software-Foundation/ct-unstable
Improvements to production containers
2022-05-01 10:30:45 -07:00
Chris Troutner 238bee3d92 fix(production): Mounting swarm.key with IPFS container 2022-05-01 10:27:54 -07:00
Chris Troutner 04f77c883a fix(production): Mounting wallet file instead of including it in Docker image 2022-05-01 09:27:52 -07:00
Chris Troutner cddf141ce4 Merge branch 'ct-unstable' 2022-05-01 06:14:50 -07:00
Chris Troutner 628d8c01f8 Doing force install in rpi docker container 2022-05-01 06:10:31 -07:00
Chris Troutner 0ed4a03314 Merge pull request #19 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2022-04-30 19:34:42 -07:00
Chris Troutner 5eb8464469 fix(README): Adding link to setup video 2022-04-30 16:49:30 -07:00
Chris Troutner d1dc4dd198 Updating README 2022-04-15 06:42:20 -07:00
Chris Troutner 1ba3633fac Updating README 2022-04-15 06:37:06 -07:00
Chris Troutner 69dd2c2627 Updating README 2022-04-15 06:35:19 -07:00
Chris Troutner 0a838b7fd7 Adding install force 2022-04-14 17:11:54 -07:00
Chris Troutner de9ede9859 fix(ui): Adding new UI to RPi docker 2022-04-12 15:40:22 -07:00
Chris Troutner 8273ae29d0 Switching UI to bch-dex-ui2 2022-04-12 13:24:51 -07:00
Chris Troutner 4d733f0995 Merge pull request #18 from Permissionless-Software-Foundation/ct-unstable
Better Offer UX
2022-04-01 20:39:22 -07:00
Chris Troutner f812580169 fix(production): Added sweep-wallet.js 2022-04-01 20:20:16 -07:00
Chris Troutner ec8dc91d36 feat(offers): Deleting offers older than 7 days 2022-04-01 20:10:38 -07:00
Chris Troutner 03214fd7df feat(offer): Deleting Offer model after broadcasting Counter Offer. 2022-04-01 19:50:33 -07:00
Chris Troutner 9aae6da4a7 feat(offer/list): Returning Offers sorted by newest first 2022-04-01 19:05:31 -07:00
Chris Troutner 6e7ab40dea Merge pull request #17 from Permissionless-Software-Foundation/ct-unstable
fix(webhook): Fixing default webhook URL
2022-03-31 18:51:04 -07:00
Chris Troutner 3b45e3c2c4 fix(domain): Detecting domain for working with back-end 2022-03-31 18:48:05 -07:00
Chris Troutner 756002dd8c fix(webhook): Fixing default webhook URL 2022-03-31 18:04:13 -07:00
Chris Troutner 86da41b6c8 Making instruction steps a numbered list 2022-03-29 11:07:44 -07:00
Chris Troutner c2838b6117 Updating install step 2022-03-29 11:03:11 -07:00
Chris Troutner af33de3f9a Adding links to videos 2022-03-29 09:27:29 -07:00
Chris Troutner 50d7cf137f Merge pull request #16 from Permissionless-Software-Foundation/ct-unstable
fix(createOrder()): Updating wallet UTXOs before checking for funds
2022-03-27 19:42:42 -07:00
Chris Troutner dad9803d69 fix(createOrder()): Updating wallet UTXOs before checking for funds 2022-03-27 19:17:52 -07:00
Chris Troutner a235e57302 Merge pull request #15 from Permissionless-Software-Foundation/ct-unstable
fix(production): Auto-restarting bch-dex Docker container
2022-03-27 13:59:03 -07:00
Chris Troutner bb2f20b322 fix(production): Auto-restarting bch-dex Docker container 2022-03-27 13:47:05 -07:00
Chris Troutner 1f91bb05dd Editing README 2022-03-27 11:49:55 -07:00
Chris Troutner 5e044f3d10 Editing README 2022-03-27 11:42:16 -07:00
Chris Troutner a94f2d32b5 fix(README): Adding installation instructions to README 2022-03-27 11:30:54 -07:00
Chris Troutner 4b66305392 fix(production scripts): Added create-wallet.js to scripts folder 2022-03-27 11:00:29 -07:00
Daniel Gonzalez 7e3ce1564d feat(coverage): Got ipfs-coord.js to 100% test coverage 2022-03-25 22:12:11 -04:00
47 changed files with 15581 additions and 13471 deletions
+62 -21
View File
@@ -2,33 +2,74 @@
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![Join the chat at https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev](https://badges.gitter.im/Permissionless-Software-Foundation/psf-dex-dev.svg)](https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a prototype web service that monitors the [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Prototype](https://github.com/vinarmani/swap-protocol/blob/master/swap-protocol-spec.md).
`bch-dex` is a decentralized exchange for permissionless trading of [SLP tokens](https://github.com/simpleledger/slp-specifications/blob/master/slp-token-type-1.md) and [NFTs](https://github.com/simpleledger/slp-specifications/blob/master/slp-nft-1.md). Trading fees are incredibly small, compared to other DEX platforms. The software is based entirely on open protocols and open source software. It's not a '.com' website that you visit, but software you run on your own computer, in the confort of your own home. So long as you have an internet connection and the technical knowledge to run this software, no one can stop you from trading tokens. `bch-dex` software is maintained by the [Permissionless Software Foundation](https://psfoundation.cash/).
This repository was forked from [ipfs-torlist-service](https://github.com/Permissionless-Software-Foundation/ipfs-torlist-service).
- [High-level Overview of bch-dex](https://youtu.be/LVX8CLi4sHw) (Video)
## Development Notes
This is a prototype web service that monitors the [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Protocol](https://github.com/vinarmani/swap-protocol/blob/master/swap-protocol-spec.md).
This repository contains the back end code. The user interface is contained in the [bch-dex-ui](https://github.com/Permissionless-Software-Foundation/bch-dex-ui) repository.
**Warning**: This repository is under active development. Things will be constantly changing and breaking.
### Road Map:
## Media
- [High-level Overview of bch-dex](https://youtu.be/LVX8CLi4sHw) (Video)
- [Developer walk-through for installing and hacking on bch-dex](https://youtu.be/T5XI43-SWJo) (Video)
- This video is out of date. Follow the instructions below.
- Phase 1: MVP (single user)
- Move tokens or BCH for sale to temporary address.
- Accept input from user:
- Submit Signal message to P2WDB for new buy/sell order.
- Check UTXO status before submitting Payment message.
- React to webhook when new Offers come in.
- Add and track new Offers in MongoDB.
- Process Payment signals:
- Checking transaction for compatibility
- Broadcast/close trades managed by this app.
- Phase 2: Build single-user web app
- _Make_ buy or sell orders.
- Browse Offers on the market.
- _Take_ buy or sell orders.
- Phase 3: Multi-user
- Configure REST API as an IPFS Service that can offer services to multiple users.
- Update web app to display and select available SWaP services over IPFS.
## Participate
This is an open source project, and we encourage other JavaScript developers to participate in its creation and maintenance. We have two chat rooms for the community:
- [Telegram Channel](https://t.me/psf_dex_dev)
- [Gitter Channel](https://gitter.im/Permissionless-Software-Foundation/psf-dex-dev)
## Installation
Running the DEX requires composition of these different software packages:
- bch-dex - This repository is the back end software that tracks trade data on the network, generates [Offers and Counter Offers](https://github.com/Permissionless-Software-Foundation/bch-dex/tree/ct-unstable/dev-docs#definitions), and finalizes trades by accepting Counter Offers.
- [bch-dex-ui](https://github.com/Permissionless-Software-Foundation/bch-dex-ui) is a [Gatsby](https://www.gatsbyjs.com/) web app and user interface (UI) for bch-dex.
- [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) is a censorship-resistant database used to communicate trade data between peers running bch-dex.
- [IPFS](https://ipfs.io/) is a censorship-resistant network for communicating data over the internet.
- [MongoDB](https://www.mongodb.com/) is a database used by both P2WDB and bch-dex to store and manage local data.
The above software is orchestrated using [Docker](https://www.docker.com/) and Docker Compose. The target operating system is Ubuntu 20+, and the target hardware is amd64 (normal desktop PCs) and the arm64 (Raspberry Pi 4). Trying to operate this software on other operating systems or hardware is possible, but not supported.
The Raspberry Pi 4 is the primary hardware target, because this software functions best when it is always running. The Pi 4 makes a great appliance for monitoring trade signals and finalizing trades.
Instructions for setting up Node.js, Docker, and Docker Compose can be found in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd). Walk-through videos can also be found on the [PSF Videos page](https://psfoundation.cash/video/).
Here are the steps involved in setting up bch-dex:
1. Follow the direction in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd) to install Node.js, Docker, and Docker Compose.
1. Clone the repository with `git clone https://github.com/Permissionless-Software-Foundation/bch-dex` and enter it with `cd bch-dex`.
1. Install dependencies by running `npm install`
1. Create a wallet:
- `cd production/scripts`
- `node create-wallet.js`
1. Change directory to the `production/docker` or `production/rpi-docker` folder depending on your hardware target.
1. Pull the Docker images down from Docker Hub: `docker-compose pull`
1. Start the Docker containers with `docker-compose up -d`
1. Wait for the P2WDB to sync and populate bch-dex with trade data. You can monitor it with `docker logs --tail 20 -f p2wdb`.
1. Open a web browser and navigate the `http://localhost:4500`. You'll be able to see new Offers as they come in and are detected by bch-dex.
1. To take the other side of the trade, click the `Take` button in the UI.
1. You can add the 12-word mnemonic from the `wallet.json` file to the the web wallet, which will mirror your wallet in the UI, and allow you to perform basic wallet functions (send and receive BCH and tokens).
## Blockchain Infrastructure
The P2WDB and bch-dex containers requires a 'Global Back End' (as described in [CashStack.info](https://cashstack.info)) in order to connect to the blockchain. By default, the Docker containers connect to [free-bch.fullstack.cash](https://free-bch.fullstack.cash/). Several community-provided Global Back End servers are provided and can be [viewed here](https://gist.github.com/christroutner/63c5513782181f8b8ea3eb89f7cadeb6). The back end can be changed by setting the `CONSUMER_URL` environment variable in the `docker-compose.yml` file.
### Applying Software Updates
As this is an active project, software updates will happen frequently. To apply a software update, perform these steps.
1. Enter the `docker` or `rpi-docker` folder, depending on your hardware target.
1. Bring down the Docker containers with `docker-compose down`.
1. Pull in the software updates with `git pull`
1. Update dependencies with `npm install`
1. Pull new Docker images with `docker-compose pull`
1. Clean up disk space by deleting old Docker images with `./cleanup-images.sh`
1. Start the Docker containers with `docker-compose up -d`
Sometimes it may be necessary to delete the databases before applying a software update. This can be done by stopping the Docker containers and deleting the `production/data` directory. When the Docker containers are restarted, they will recreate that directory. The P2WDB will re-sync and bch-dex will be populated with fresh trade data.
## License
+61 -37
View File
@@ -24,39 +24,48 @@ const config = require('../config') // this first.
const AdminLib = require('../src/adapters/admin')
// const adminLib = new AdminLib()
const WebHookLib = require('../src/adapters/webhook')
const webhookLib = new WebHookLib()
// 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')
class Server {
constructor () {
// Encapsulate dependencies
this.adminLib = new AdminLib()
this.controllers = new Controllers()
this.mongoose = mongoose
this.config = config
this.process = process
}
async startServer () {
try {
// Create a Koa instance.
const app = new Koa()
app.keys = [config.session]
app.keys = [this.config.session]
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
this.mongoose.Promise = global.Promise
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
console.log(
`Connecting to MongoDB with this connection string: ${config.database}`
`Connecting to MongoDB with this connection string: ${this.config.database}`
)
await mongoose.connect(config.database, {
await this.mongoose.connect(this.config.database, {
useUnifiedTopology: true,
useNewUrlParser: true
})
console.log(`Starting environment: ${config.env}`)
console.log(`Debug level: ${config.debugLevel}`)
console.log(`Starting environment: ${this.config.env}`)
console.log(`Debug level: ${this.config.debugLevel}\n`)
console.log(`Using this web3 CashStack service: ${this.config.consumerUrl}`)
console.log('Alternative web services listed here: https://gist.github.com/christroutner/63c5513782181f8b8ea3eb89f7cadeb6')
console.log('This app is built on top of the CashStack. Find out more: https://CashStack.info\n')
// MIDDLEWARE START
@@ -81,33 +90,42 @@ class Server {
// Dev Note: This line must come BEFORE controllers.attachRESTControllers()
app.use(cors({ origin: '*' }))
// Attach REST API and JSON RPC controllers to the app.
const Controllers = require('../src/controllers')
const controllers = new Controllers()
await controllers.attachRESTControllers(app)
// Wait for any adapters to initialize.
await this.controllers.initAdapters()
app.controllers = controllers
// Wait for any use-libraries to initialize.
await this.controllers.initUseCases()
// Attach REST API and JSON RPC controllers to the app.
await this.controllers.attachRESTControllers(app)
app.controllers = this.controllers
// 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
// 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()
await app.listen(config.port)
this.server = await app.listen(config.port)
console.log(`Server started on ${config.port}`)
// Create the system admin user.
@@ -116,10 +134,16 @@ class Server {
// Attach the other IPFS controllers.
// Skip if this is a test environment.
if (config.env !== 'test') {
await controllers.attachControllers(app)
if (this.config.env !== 'test') {
await this.controllers.attachControllers(app)
}
// Display configuration settings
console.log('\nConfiguration:')
console.log(`Circuit Relay: ${this.config.isCircuitRelay}`)
console.log(`IPFS TCP port: ${this.config.ipfsTcpPort}`)
console.log(`IPFS WS port: ${this.config.ipfsWsPort}\n`)
return app
} catch (err) {
console.error('Could not start server. Error: ', err)
@@ -127,14 +151,14 @@ class Server {
console.log(
'Exiting after 5 seconds. Depending on process manager to restart.'
)
await sleep(5000)
process.exit(1)
await this.sleep(5000)
this.process.exit(1)
}
}
sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
}
function sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
module.exports = Server
+8 -6
View File
@@ -35,8 +35,8 @@ module.exports = {
useFullStackCash: process.env.USE_FULLSTACKCASH ? true : false,
consumerUrl: process.env.CONSUMER_URL
? process.env.CONSUMER_URL
// : 'https://free-bch.fullstack.cash',
: 'https://wa-usa-bch-consumer.fullstackcash.nl',
: 'https://free-bch.fullstack.cash',
// : 'https://wa-usa-bch-consumer.fullstackcash.nl',
// 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',
@@ -85,10 +85,10 @@ module.exports = {
webhookService: process.env.WEBHOOKSERVICE
? process.env.WEBHOOKSERVICE
: 'http://localhost:5010/webhook', // P2WDB.
p2wdbAppId: process.env.APP_ID ? process.env.APP_ID : 'bch-dex-test557',
p2wdbAppId: process.env.APP_ID ? process.env.APP_ID : 'bch-dex-001',
webhookTarget: process.env.WEBHOOKTARGET
? process.env.WEBHOOKTARGET
: 'http://localhost:5700/offer',
: 'http://localhost:5700/p2wdb',
// IPFS Ports
ipfsTcpPort: process.env.IPFS_TCP_PORT ? process.env.IPFS_TCP_PORT : 4001,
@@ -97,12 +97,14 @@ module.exports = {
// 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) : 2,
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,
ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost',
ipfsApiPort: process.env.IPFS_API_PORT
? parseInt(process.env.IPFS_API_PORT)
: 5001
: 5001,
chatPubSubChan: 'psf-ipfs-chat-001'
}
+1 -1
View File
@@ -1,6 +1,6 @@
const common = require('./env/common')
const env = process.env.BCH_ENV || 'development'
const env = process.env.BCH_DEX || 'development'
const config = require(`./env/${env}`)
module.exports = Object.assign({}, common, config)
+14610 -13239
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -27,14 +27,14 @@
},
"repository": "Permissionless-Software-Foundation/bch-dex",
"dependencies": {
"@psf/bch-js": "6.2.1",
"axios": "0.21.1",
"bch-message-lib": "2.1.4",
"@psf/bch-js": "6.4.1",
"axios": "0.27.2",
"bch-message-lib": "2.2.0",
"bcryptjs": "2.4.3",
"bitcoincashjs-lib": "3.3.3",
"glob": "7.1.6",
"ipfs": "0.60.0",
"ipfs-coord": "7.1.6",
"ipfs": "0.62.3",
"ipfs-coord": "7.1.19",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
@@ -52,19 +52,19 @@
"koa2-ratelimit": "0.9.1",
"libp2p": "^0.36.2",
"line-reader": "0.4.0",
"minimal-slp-wallet": "4.4.5",
"minimal-slp-wallet": "4.6.5",
"mongoose": "5.13.13",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.4.17",
"p2wdb": "1.3.1",
"nodemailer": "6.7.5",
"p2wdb": "1.4.0",
"passport-local": "1.0.0",
"public-ip": "^4.0.4",
"winston": "3.3.3",
"winston-daily-rotate-file": "4.5.0"
},
"devDependencies": {
"apidoc": "0.26.0",
"bch-token-sweep": "1.5.16",
"apidoc": "0.51.1",
"bch-token-sweep": "2.1.2",
"chai": "4.3.0",
"coveralls": "2.11.4",
"eslint": "7.19.0",
@@ -75,9 +75,9 @@
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "4.3.8",
"mocha": "8.4.0",
"mocha": "10.0.0",
"nyc": "15.1.0",
"semantic-release": "17.4.4",
"semantic-release": "19.0.3",
"sinon": "9.2.4",
"standard": "16.0.4",
"uuid": "8.3.2"
+1 -1
View File
@@ -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
RUN npm install --force
# Build the site
RUN CI=true npm run build
+2 -2
View File
@@ -64,7 +64,7 @@ RUN npm install
RUN npm run docs
# Copy the wallet files
COPY wallet.json wallet.json
#COPY wallet.json wallet.json
#VOLUME /home/safeuser/keys
@@ -72,7 +72,7 @@ COPY wallet.json wallet.json
#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=production
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
+25 -13
View File
@@ -4,7 +4,7 @@ version: '3.9'
services:
mongo-p2wdb-service:
image: mongo:4.4
image: mongo:4.4.2-bionic
container_name: mongo-p2wdb-service
ports:
- '5666:27017' # <host port>:<container port>
@@ -14,7 +14,7 @@ services:
restart: always
ipfs:
image: ipfs/go-ipfs:v0.12.0
image: ipfs/go-ipfs:v0.13.0
container_name: ipfs
environment:
MY_ENV_VAR: 'placeholder'
@@ -30,7 +30,7 @@ services:
- 172.17.0.1:8080:8080
volumes:
- ../data/go-ipfs/data:/data/ipfs
- ../data/go-ipfs/export:/export
- ../../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:
@@ -46,7 +46,11 @@ services:
build:
context: ./p2wdb/
dockerfile: Dockerfile
#image: christroutner/ipfs-p2wdb-service:v3.0.4
container_name: p2wdb
environment:
CONSUMER_URL: 'https://free-bch.fullstack.cash'
DEBUG_LEVEL: 1
logging:
driver: 'json-file'
options:
@@ -62,13 +66,17 @@ 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
#build:
# context: ./bch-dex/
# dockerfile: Dockerfile
image: christroutner/bch-dex:v1.9.0
container_name: bch-dex
environment:
CONSUMER_URL: 'https://free-bch.fullstack.cash'
logging:
driver: 'json-file'
options:
@@ -80,15 +88,19 @@ services:
- p2wdb
ports:
- '5700:5700' # <host port>:<container port>
# volumes:
# - ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
#restart: always
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
#build:
# context: ./bch-dex-ui/
# dockerfile: Dockerfile
image: christroutner/bch-dex-ui:v1.6.5
container_name: dex-ui
environment:
SERVER: 'http://192.168.2.3'
logging:
driver: 'json-file'
options:
@@ -101,4 +113,4 @@ services:
- '4500:80' # <host port>:<container port>
# volumes:
# - ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
#restart: always
restart: always
+1 -1
View 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"]
+5 -4
View File
@@ -16,7 +16,8 @@ export COORD_NAME=generic-p2wdb-production
#export CR_DOMAIN=subdomain.yourdomain.com
# Debug level. 0 = minimal info. 2 = max info.
export DEBUG_LEVEL=2
# This value set by docker-compose.yml
#export DEBUG_LEVEL=1
# Log-in information for retrieving a JWT token from FullStack.cash.
export FULLSTACKLOGIN=demo@demo.com
@@ -29,8 +30,8 @@ export FULLSTACKPASS=demo
export DBURL=mongodb://172.17.0.1:5666/p2wdb-service-dev
# Configure IPFS ports
export IPFS_TCP_PORT=5668
export IPFS_WS_PORT=5669
#export IPFS_TCP_PORT=5668
#export IPFS_WS_PORT=5669
# Configure REST API port
export PORT=5667
@@ -41,6 +42,6 @@ export IPFS_API_PORT=5001
export IPFS_TCP_PORT=4001
# P2WDB specific env vars
export ORBITDB_NAME=/orbitdb/zdpuAm4fPafpjs5mgbBUJV8Az7kd8Xj1ttD6M1JaAxF8RMRrw/psf-bch-p2wdb-keyvalue-v1.0.0-0004
#export ORBITDB_NAME=/orbitdb/zdpuAm4fPafpjs5mgbBUJV8Az7kd8Xj1ttD6M1JaAxF8RMRrw/psf-bch-p2wdb-keyvalue-v1.0.0-0004
npm start
+1 -1
View File
@@ -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
RUN npm install --force
# Build the site
RUN CI=true npm run build
+1 -1
View File
@@ -64,7 +64,7 @@ RUN npm install
RUN npm run docs
# Copy the wallet files
COPY wallet.json wallet.json
#COPY wallet.json wallet.json
#VOLUME /home/safeuser/keys
+22 -14
View File
@@ -31,7 +31,7 @@ services:
- 172.17.0.1:8080:8080
volumes:
- ../data/go-ipfs/data:/data/ipfs
- ../data/go-ipfs/export:/export
- ../../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:
@@ -44,10 +44,14 @@ services:
restart: always
p2wdb:
build:
context: ./p2wdb/
dockerfile: Dockerfile
#build:
# context: ./p2wdb/
# dockerfile: Dockerfile
image: christroutner/pwd-bch-dex-rpi:v1.7.1
container_name: p2wdb
environment:
CONSUMER_URL: 'https://free-bch.fullstack.cash'
DEBUG_LEVEL: 1
logging:
driver: 'json-file'
options:
@@ -66,10 +70,13 @@ services:
restart: always
bch-dex:
build:
context: ./bch-dex/
dockerfile: Dockerfile
#build:
# context: ./bch-dex/
# dockerfile: Dockerfile
image: christroutner/bch-dex-rpi:v1.7.1
container_name: bch-dex
environment:
CONSUMER_URL: 'https://free-bch.fullstack.cash'
logging:
driver: 'json-file'
options:
@@ -81,14 +88,15 @@ services:
- p2wdb
ports:
- '5700:5700' # <host port>:<container port>
# volumes:
# - ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
#restart: always
volumes:
- ../scripts/wallet.json:/home/safeuser/bch-dex/wallet.json
restart: always
dex-ui:
build:
context: ./bch-dex-ui/
dockerfile: Dockerfile
#build:
# context: ./bch-dex-ui/
# dockerfile: Dockerfile
image: christroutner/bch-dex-ui-rpi:v1.7.3
container_name: dex-ui
logging:
driver: 'json-file'
@@ -102,4 +110,4 @@ services:
- '4500:80' # <host port>:<container port>
# volumes:
# - ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
#restart: always
restart: always
@@ -16,7 +16,8 @@ export COORD_NAME=generic-p2wdb-production
#export CR_DOMAIN=subdomain.yourdomain.com
# Debug level. 0 = minimal info. 2 = max info.
export DEBUG_LEVEL=2
# This value set by docker-compose.yml
#export DEBUG_LEVEL=1
# Log-in information for retrieving a JWT token from FullStack.cash.
export FULLSTACKLOGIN=demo@demo.com
+3 -3
View File
@@ -8,8 +8,8 @@
const axios = require('axios')
// const LOCALHOST = 'http://localhost:5700'
const LOCALHOST = 'http://172.17.0.1:5700'
const LOCALHOST = 'http://localhost:5700'
// const LOCALHOST = 'http://172.17.0.1:5700'
async function start () {
try {
@@ -22,7 +22,7 @@ async function start () {
buyOrSell: 'sell',
rateInBaseUnit: 5000,
minUnitsToExchange: 5000,
numTokens: 1
numTokens: 2
}
const options = {
+32
View File
@@ -0,0 +1,32 @@
/*
Generate a wallet.json file for use with the DEX.
*/
// Public NPM libraries
const BchWallet = require('minimal-slp-wallet/index')
const fs = require('fs').promises
async function createWallet () {
try {
// Configure the minimal-slp-wallet library to use the JSON RPC over IPFS.
const advancedConfig = {
interface: 'consumer-api',
noUpdate: true
}
// Wait for the wallet to be created.
this.bchWallet = new BchWallet(undefined, advancedConfig)
await this.bchWallet.walletInfoPromise
const walletData = this.bchWallet.walletInfo
walletData.nextAddress = 1
// Save the wallet file to disk.
await fs.writeFile('wallet.json', JSON.stringify(walletData, null, 2))
console.log('Generated wallet.json. Copy this file to the docker/bch-dex folder to be used inside the Docker container.')
console.log(`${JSON.stringify(walletData, null, 2)}`)
} catch (err) {
console.error('Error: ', err)
}
}
createWallet()
+139
View File
@@ -0,0 +1,139 @@
/*
This script will sweep the HD wallet, looking for NFTs. When found, it will
send the NFT to the root (index 0) address. It will pay transaction fees from
the root address.
*/
// Global npm libraries
const BchWallet = require('minimal-slp-wallet/index')
// Local libraries
const WalletAdapter = require('../../src/adapters/wallet')
// Constants
const EMTPY_ADDR_CUTOFF = 10
const WEB3_SERVER = 'https://free-bch.fullstack.cash'
async function sweepNfts () {
try {
// Open the wallet files.
const walletLib = new WalletAdapter()
// const walletInfo = await walletLib.openWallet()
const walletInfo = await openWallet(walletLib)
// const walletRoot = await walletLib.instanceWallet(walletInfo)
const walletRoot = new BchWallet(walletInfo.mnemonic, {
interface: 'consumer-api',
restURL: WEB3_SERVER
})
await walletRoot.walletInfoPromise
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 = walletRoot.bchjs
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
let emptyAddrCnt = 0
let hdIndex = 1
do {
// Generate a keypair from the HD wallet.
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
const wifToSweep = bchjs.HDNode.toWIF(childNode)
console.log(`\n${hdIndex}: Sweeping ${cashAddress} with private key ${wifToSweep}`)
const walletChild = new BchWallet(wifToSweep, { interface: 'consumer-api', restURL: WEB3_SERVER })
await walletChild.walletInfoPromise
try {
const tokens = await walletChild.tokens.listTokensFromAddress(cashAddress)
// console.log(`tokens: ${JSON.stringify(tokens, null, 2)}`)
await walletChild.utxos.initUtxoStore(cashAddress)
// console.log(`utxoStore: ${JSON.stringify(utxoStore, null, 2)}`)
for (let i = 0; i < tokens.length; i++) {
const thisToken = tokens[i]
if (thisToken.tokenType === 65) {
console.log(`NFT ${thisToken.ticker} (${thisToken.name}) found. Sweeping to ${rootAddr}`)
// Send BCH to the child address, to pay for transaction fees.
const receiver1 = [{
address: cashAddress,
amountSat: 10000
}]
const txid1 = await walletRoot.send(receiver1)
console.log(`10,000 sats sent to child address. TXID: ${txid1}`)
// Wait for indexers to update.
console.log('Waiting for indexers to update...')
await bchjs.Util.sleep(20000)
// Send NFT to root address.
const receiver2 = [{
address: rootAddr,
tokenId: thisToken.tokenId,
qty: thisToken.qty
}]
const txid2 = await walletChild.sendTokens(receiver2)
console.log(`NFT sent from child address to root address. TXID: ${txid2}`)
// Wait for indexers to update.
console.log('Waiting for indexers to update...')
await bchjs.Util.sleep(3000)
// Refresh the UTXO store.
await walletChild.utxos.initUtxoStore(cashAddress)
// Send the rest of the BCH back to the root address.
const txid3 = await walletChild.sendAll(rootAddr)
console.log(`Leftover BCH in child address sent to root address. TXID: ${txid3}`)
emptyAddrCnt = 0
}
}
// Wait between loop iterations.
await bchjs.Util.sleep(3000)
// emptyAddrCnt++
} catch (err) {
console.log(`error message with index ${hdIndex}: ${err.message}`)
console.log(err)
// emptyAddrCnt++
}
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.log('Error while sweeping NFTs: ', err)
}
}
sweepNfts()
// 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
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
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')
// Local libraries
const WalletAdapter = require('../../src/adapters/wallet')
// Constants
const EMTPY_ADDR_CUTOFF = 10
async function sweepFunds () {
try {
// Open the wallet files.
const walletLib = new WalletAdapter()
// 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}...`)
// 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 = 1
do {
// Generate a keypair from the HD wallet.
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
const cashAddress = bchjs.HDNode.toCashAddress(childNode)
const wifToSweep = bchjs.HDNode.toWIF(childNode)
console.log(`\nSweeping ${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
}
}
+17 -23
View File
@@ -19,26 +19,7 @@ const FullStackJWT = require('./fullstack-jwt')
const BCHAdapter = require('./bch')
const WalletAdapter = require('./wallet')
const P2wdbAdapter = require('./p2wdb-adapter')
//
// // Instantiate adapter libraries.
// const ipfs = new IPFSAdapter()
// const localdb = new LocalDB()
// const logapi = new LogsAPI()
// const passport = new Passport()
// const nodemailer = new Nodemailer()
// const jsonFiles = new JSONFiles()
// const bchjs = new BCHJSAdapter()
//
// module.exports = {
// ipfs,
// localdb,
// logapi,
// passport,
// nodemailer,
// wlogger,
// jsonFiles,
// bchjs
const Webhook = require('./webhook')
const config = require('../../config')
@@ -57,6 +38,7 @@ class Adapters {
this.config = config
this.wallet = new WalletAdapter()
this.p2wdb = new P2wdbAdapter(localConfig)
this.webhook = new Webhook()
// Get a valid JWT API key and instance bch-js.
this.fullStackJwt = new FullStackJWT(config)
@@ -73,14 +55,26 @@ class Adapters {
}
// Start the IPFS node.
// await this.ipfs.start()
// Do not start these adapters if this is an e2e test.
// if (this.config.env !== 'test') {
// await this.ipfs.start()
// }
// Open the wallet file
const walletData = await this.wallet.openWallet()
// console.log('walletData: ', walletData)
// Instance the wallet.
await this.wallet.instanceWallet(walletData, this.bchjs)
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)
}
console.log('Async Adapters have been started.')
return true
} catch (err) {
console.error('Error in adapters/index.js/start()')
throw err
+4
View File
@@ -2,6 +2,7 @@
top-level IPFS library that combines the individual IPFS-based libraries.
*/
// Local libraries
const IpfsAdapter = require('./ipfs')
const IpfsCoordAdapter = require('./ipfs-coord')
@@ -36,6 +37,9 @@ class IPFS {
await this.ipfsCoordAdapter.start()
console.log('ipfs-coord is ready.')
// Subscribe to the chat pubsub channel
await this.ipfsCoordAdapter.subscribeToChat()
return true
} catch (err) {
console.error('Error in adapters/ipfs/index.js/start()')
+9
View File
@@ -97,6 +97,15 @@ class IpfsCoordAdapter {
throw err
}
}
// Subscribe to the chat pubsub channel
async subscribeToChat () {
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
this.config.chatPubSubChan,
console.log,
this.ipfsCoord.thisNode
)
}
}
module.exports = IpfsCoordAdapter
+3 -1
View File
@@ -7,6 +7,7 @@ const Offer = new mongoose.Schema({
utxoTxid: { type: String },
utxoVout: { type: Number },
ticker: { type: String },
tokenType: { type: Number },
// Trade data
buyOrSell: { type: String },
@@ -23,7 +24,8 @@ const Offer = new mongoose.Schema({
sigMsg: { type: String },
// Utility data
timestamp: { type: String },
timestamp: { type: Number },
globalTimestamp: { type: String },
localTimestamp: { type: String },
// SWaP Protocol Properties
+1
View File
@@ -14,6 +14,7 @@ const Order = new mongoose.Schema({
utxoTxid: { type: String },
utxoVout: { type: Number },
ticker: { type: String },
tokenType: { type: Number },
// Trade data
buyOrSell: { type: String },
+3 -1
View File
@@ -135,8 +135,10 @@ class LogsApi {
if (!filename || typeof filename !== 'string') {
throw new Error('filename must be a string')
}
// Throw an error if the file does not exist.
console.log('readLines() filename: ', filename)
// Throw an error if the file does not exist.
if (!_this.fs.existsSync(filename)) {
throw new Error('file does not exist')
}
+32 -7
View File
@@ -208,7 +208,7 @@ class WalletAdapter {
// Get token info on the offered UTXO
const txData = await this.bchWallet.getTxData([offerInfo.utxoTxid])
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
// Construct the UTXO being offered for sale.
const offeredUtxo = {
@@ -222,12 +222,27 @@ class WalletAdapter {
// Build First part of the collaborative Tx a.k.a. Alice
// Generate the OP_RETURN code.
const slpSendObj = bchjs.SLP.TokenType1.generateSendOpReturn(
[offeredUtxo],
offerInfo.numTokens.toString()
)
let slpSendObj
if (offerInfo.tokenType === 65) {
// Type 65 NFT
slpSendObj = bchjs.SLP.NFT1.generateNFTChildSendOpReturn(
[offeredUtxo],
offerInfo.numTokens.toString()
)
console.log(`slpOutputs: ${slpSendObj.outputs}`)
} else if (offerInfo.tokenType === 1) {
// Type 1 fungible token
slpSendObj = bchjs.SLP.TokenType1.generateSendOpReturn(
[offeredUtxo],
offerInfo.numTokens.toString()
)
console.log(`slpOutputs: ${slpSendObj.outputs}`)
} else {
throw new Error(`Unknown token type of ${offerInfo.tokenType}. Can not create Counter Offer.`)
}
const slpData = slpSendObj.script
console.log(`slpOutputs: ${slpSendObj.outputs}`)
// Currently this app only supports a single SLP token UTXO for exact
// token quantities (no token change). e.g. 1 UTXO representing the
@@ -334,12 +349,22 @@ class WalletAdapter {
// Update the UTXO store of the wallet.
await this.bchWallet.getUtxos()
// Get the token type of the token being moved.
// Combine Fungible and NFT token UTXOs.
let tokenUtxos = this.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens.concat(
this.bchWallet.utxos.utxoStore.slpUtxos.nft.tokens)
// Get token UTXOs that match the token in the order.
tokenUtxos = tokenUtxos.filter(
x => x.tokenId === tokenId
)
const txid = await this.bchWallet.sendTokens(receiver, 3)
const utxoInfo = {
txid,
vout: 1,
hdIndex: keyPair.hdIndex
hdIndex: keyPair.hdIndex,
tokenType: tokenUtxos[0].tokenType
}
return utxoInfo
+40
View File
@@ -59,6 +59,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
}
}
}
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
module.exports = WebHook
+2 -1
View File
@@ -49,7 +49,8 @@ class Wlogger {
}
notifyRotation (oldFilename, newFilename) {
this.wlogger.info('Rotating log files')
// this.wlogger.info('Rotating log files')
console.log('Rotating log files')
}
outputToConsole () {
+14 -7
View File
@@ -17,17 +17,18 @@ class Controllers {
constructor (localConfig = {}) {
this.adapters = new Adapters()
this.useCases = new UseCases({ adapters: this.adapters })
this.timerControllers = new TimerControllers({ adapters: this.adapters, useCases: this.useCases })
}
async attachControllers (app) {
// Wait for any startup processes to complete for the Adapters libraries.
// Spin up any adapter libraries that have async startup needs.
async initAdapters () {
await this.adapters.start()
}
// Attach the REST controllers to the Koa app.
// this.attachRESTControllers(app)
// this.attachRPCControllers()
this.timerControllers = new TimerControllers({ adapters: this.adapters, useCases: this.useCases })
// Run any Use Cases to startup the app.
async initUseCases () {
await this.useCases.start()
}
// Top-level function for this library.
@@ -42,6 +43,12 @@ class Controllers {
restControllers.attachRESTControllers(app)
}
// Attach any other controllers other than REST API controllers.
async attachControllers (app) {
// this.attachRPCControllers()
}
// Add the JSON RPC router to the ipfs-coord adapter.
attachRPCControllers () {
const jsonRpcController = new JSONRPC({
+13 -3
View File
@@ -29,7 +29,8 @@ class OfferEntity {
utxoVout,
offerStatus,
makerAddr,
ticker
ticker,
tokenType
} = offerData.data
// Input Validation
@@ -69,6 +70,13 @@ class OfferEntity {
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.")
}
// Convert the timestamp to a number.
let timestamp = new Date(offerData.timestamp)
timestamp = timestamp.getTime()
const validatedOfferData = {
messageType,
@@ -80,13 +88,15 @@ class OfferEntity {
numTokens,
utxoTxid,
utxoVout,
timestamp: offerData.timestamp,
timestamp,
globaltimestamp: offerData.timestamp,
localTimestamp: offerData.localTimeStamp,
txid: offerData.txid,
p2wdbHash: offerData.hash,
offerStatus: offerStatus || this.offerStatus[0],
makerAddr,
ticker
ticker,
tokenType
}
return validatedOfferData
+13
View File
@@ -25,6 +25,19 @@ class UseCases {
localConfig.order = this.order
this.offer = new OfferUseCases(localConfig)
}
// 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
+39 -21
View File
@@ -93,7 +93,7 @@ class OfferUseCases {
async listOffers () {
try {
return this.OfferModel.find({})
return this.OfferModel.find({}).sort('-timestamp')
} catch (error) {
console.error('Error in use-cases/offer/listOffers()')
throw error
@@ -188,6 +188,10 @@ class OfferUseCases {
}
const hash = await this.adapters.p2wdb.write(p2wdbObj)
// Delete the Offer from the database, so that the user doesn't attempt
// to take the offer more than once.
offerInfo.remove()
// Return the P2WDB CID
return hash
@@ -250,25 +254,27 @@ class OfferUseCases {
}
async findOfferByHash (p2wdbHash) {
try {
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
throw new Error('p2wdbHash must be a string')
}
const offer = await this.OfferModel.findOne({ p2wdbHash })
if (!offer) {
throw new Error('offer not found')
}
const offerObject = offer.toObject()
// return this.offerEntity.validateFromModel(offerObject)
return offerObject
} catch (err) {
console.error('Error in findOffer(): ', err)
throw err
// try {
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
throw new Error('p2wdbHash must be a string')
}
const offer = await this.OfferModel.findOne({ p2wdbHash })
if (!offer) {
throw new Error('offer not found')
}
return offer
// const offerObject = offer.toObject()
// return this.offerEntity.validateFromModel(offerObject)
// return offerObject
// } catch (err) {
// // console.error('Error in findOffer(): ', err)
// throw err
// }
}
// This function is called by the P2WDB webhook REST API handler. When a
@@ -353,10 +359,13 @@ class OfferUseCases {
// console.log('utxoStatus: ', utxoStatus)
} catch (err) {
// Handle corner case of bad-data in the Offer model.
if (err.message.includes('txid needs to be a proper transaction ID')) {
if (err.message && err.message.includes('txid needs to be a proper transaction ID')) {
console.log(`Deleting Offer with bad data: ${JSON.stringify(thisOffer, null, 2)}`)
await thisOffer.remove()
continue
} else if (err.isAxiosError) {
console.log('Error trying to contact api.fullstack.cash')
continue
} else {
throw err
}
@@ -367,9 +376,18 @@ class OfferUseCases {
console.log(`Spent UTXO detected. Deleting this Offer: ${JSON.stringify(thisOffer, null, 2)}`)
await thisOffer.remove()
}
// If the Offer is older than 7 days, delete it.
const nowMs = now.getTime()
const eightWeeks = 1000 * 60 * 60 * 24 * 7 * 8
const eightWeeksAgo = nowMs - eightWeeks
if (thisOffer.timestamp < eightWeeksAgo) {
console.log('Offer older than 8 weeks. Deleting.')
await thisOffer.remove()
}
}
} catch (err) {
console.error('Error in removeStaleOffers()')
console.error('Error in removeStaleOffers(): ', err)
throw err
}
}
+19 -6
View File
@@ -28,7 +28,9 @@ class OrderLib {
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
@@ -65,6 +67,7 @@ class OrderLib {
// Update the order with the new UTXO information.
orderEntity.utxoTxid = utxoInfo.txid
orderEntity.utxoVout = utxoInfo.vout
orderEntity.tokenType = utxoInfo.tokenType
// Add P2WDB specific flag for signaling that this is a new offer.
orderEntity.dataType = 'offer'
@@ -81,6 +84,7 @@ class OrderLib {
// Create a MongoDB model to hold the Order
orderEntity.hdIndex = utxoInfo.hdIndex
orderEntity.p2wdbHash = hash
console.log(`creating new order model: ${JSON.stringify(orderEntity, null, 2)}`)
const order = new this.OrderModel(orderEntity)
await order.save()
@@ -88,7 +92,7 @@ class OrderLib {
return hash
} catch (err) {
// console.log("Error in use-cases/entry.js/createEntry()", err.message)
wlogger.error('Error in use-cases/createOrder())')
wlogger.error('Error in use-cases/order.js/createOrder())')
console.log('error entryObj: ', entryObj)
throw err
}
@@ -101,6 +105,9 @@ class OrderLib {
// console.log('this.adapters.wallet: ', this.adapters.wallet.bchWallet)
// console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`)
// Update the wallet balance
await this.adapters.wallet.bchWallet.getUtxos()
// 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)
@@ -113,11 +120,14 @@ class OrderLib {
if (orderEntity.buyOrSell.includes('sell')) {
// Sell Order
// Combine Fungible and NFT token UTXOs.
let tokenUtxos = utxos.slpUtxos.type1.tokens.concat(utxos.slpUtxos.nft.tokens)
// Get token UTXOs that match the token in the order.
const tokenUtxos = utxos.slpUtxos.type1.tokens.filter(
tokenUtxos = tokenUtxos.filter(
x => x.tokenId === orderEntity.tokenId
)
// console.log('tokenUtxos: ', tokenUtxos)
console.log('tokenUtxos: ', tokenUtxos)
// Get the total amount of tokens in the wallet that match the token
// in the order.
@@ -127,7 +137,7 @@ class OrderLib {
// If there are fewer tokens in the wallet than what's in the order,
// throw an error.
if (totalTokenBalance <= orderEntity.numTokens || isNaN(totalTokenBalance)) {
if (totalTokenBalance < orderEntity.numTokens || isNaN(totalTokenBalance)) {
throw new Error(
'App wallet does not have enough tokens to satisfy the SELL order.'
)
@@ -195,10 +205,13 @@ class OrderLib {
// console.log('utxoStatus: ', utxoStatus)
} catch (err) {
// Handle corner case of bad-data in the Order model.
if (err.message.includes('txid needs to be a proper transaction ID')) {
if (err.message && err.message.includes('txid needs to be a proper transaction ID')) {
console.log(`Deleting Order with bad data: ${JSON.stringify(thisOrder, null, 2)}`)
await thisOrder.remove()
continue
} else if (err.isAxiosError) {
console.log('Error trying to contact api.fullstack.cash')
continue
} else {
throw err
}
+1
View File
@@ -7,6 +7,7 @@
// Public npm libraries
const assert = require('chai').assert
const axios = require('axios').default
// const sinon = require('sinon')
// Local support libraries
const config = require('../../../config')
+54
View File
@@ -0,0 +1,54 @@
/*
Unit tests for the adapters index.js library
*/
// Global npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const Adapters = require('../../../src/adapters')
describe('#adapters', () => {
let uut, sandbox
beforeEach(() => {
uut = new Adapters()
sandbox = sinon.createSandbox()
})
afterEach(() => {
sandbox.restore()
})
describe('#start', () => {
it('should start the async adapters', async () => {
// Mock dependencies
uut.config.getJwtAtStartup = true
sandbox.stub(uut.fullStackJwt, 'getJWT').resolves()
sandbox.stub(uut.fullStackJwt, 'instanceBchjs').resolves()
sandbox.stub(uut.ipfs, 'start').resolves()
const result = await uut.start()
assert.equal(result, true)
})
it('should catch and throw an error', async () => {
try {
// Force an error
uut.config.getJwtAtStartup = false
// uut.config.env = 'dev'
sandbox.stub(uut.wallet, 'openWallet').rejects(new Error('test error'))
await uut.start()
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.include(err.message, 'test error')
}
})
})
})
@@ -8,6 +8,7 @@ const sinon = require('sinon')
const IPFSCoordAdapter = require('../../../src/adapters/ipfs/ipfs-coord')
const IPFSMock = require('../mocks/ipfs-mock')
const IPFSCoordMock = require('../mocks/ipfs-coord-mock')
const config = require('../../../config')
describe('#IPFS', () => {
let uut
@@ -61,6 +62,16 @@ describe('#IPFS', () => {
assert.equal(result, true)
})
it('should return a promise that resolves into an instance of IPFS in production mode', async () => {
uut.config.isProduction = true
// Mock dependencies.
uut.IpfsCoord = IPFSCoordMock
const result = await uut.start()
// console.log('result: ', result)
assert.equal(result, true)
config.isProduction = false
})
})
describe('#attachRPCRouter', () => {
@@ -101,4 +112,20 @@ describe('#IPFS', () => {
}
})
})
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
// Mock dependencies
uut.ipfsCoord = {
adapters: {
pubsub: {
subscribeToPubsubChannel: async () => {
}
}
}
}
await uut.subscribeToChat()
})
})
})
+25 -32
View File
@@ -32,19 +32,18 @@ describe('#LogsApiLib', () => {
})
it('should return log', async () => {
try {
const pass = 'test'
const result = await uut.getLogs(pass)
// console.log('result', result)
// Mock dependencies
sandbox.stub(uut, 'generateFileName').returns(`${__dirname.toString()}/../mocks/adapters/fake-log`)
assert.isTrue(result.success)
assert.isArray(result.data)
assert.property(result.data[0], 'message')
assert.property(result.data[0], 'level')
assert.property(result.data[0], 'timestamp')
} catch (err) {
assert(false, 'Unexpected result')
}
const pass = 'test'
const result = await uut.getLogs(pass)
// console.log('result', result)
assert.isTrue(result.success)
assert.isArray(result.data)
assert.property(result.data[0], 'message')
assert.property(result.data[0], 'level')
assert.property(result.data[0], 'timestamp')
})
it('should return false if files are not found!', async () => {
@@ -201,31 +200,25 @@ describe('#LogsApiLib', () => {
})
it('should ignore fileReader callback errors', async () => {
try {
// https://sinonjs.org/releases/latest/stubs/
// About yields
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
// https://sinonjs.org/releases/latest/stubs/
// About yields
sandbox.stub(uut.lineReader, 'eachLine').yieldsRight({}, true)
const fileName = context.fileName
const result = await uut.readLines(fileName)
assert.isArray(result)
} catch (err) {
assert.fail('Unexpected result')
}
const fileName = `${__dirname.toString()}/../mocks/adapters/fake-log`
const result = await uut.readLines(fileName)
assert.isArray(result)
})
it('should return data', async () => {
try {
const fileName = context.fileName
const result = await uut.readLines(fileName)
const fileName = `${__dirname.toString()}/../mocks/adapters/fake-log`
assert.isArray(result)
assert.property(result[1], 'message')
assert.property(result[1], 'level')
assert.property(result[1], 'timestamp')
} catch (err) {
assert.fail('Unexpected result')
}
const result = await uut.readLines(fileName)
assert.isArray(result)
assert.property(result[1], 'message')
assert.property(result[1], 'level')
assert.property(result[1], 'timestamp')
})
})
})
+5 -4
View File
@@ -262,10 +262,11 @@ describe('#wallet', () => {
wif: 'L4qKTMCwjH9jHnYtNh9Vsrxj7Hg6zmoN8E2v7N47UKvNVEjw7FU8',
hdIndex: 6
})
uut.bchWallet = {
sendTokens: async () => 'fake-txid',
getUtxos: async () => {}
}
uut.bchWallet = new MockBchWallet()
// uut.bchWallet = {
// sendTokens: async () => 'fake-txid',
// getUtxos: async () => {}
// }
const inObj = {
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
+3 -1
View File
@@ -254,7 +254,8 @@ describe('#Offer-Entity', () => {
utxoVout: 0,
offerStatus: 'posted',
makerAddr: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
ticker: 'TROUT'
ticker: 'TROUT',
tokenType: 1
},
timestamp: '2021-09-20T17:54:26.395Z',
localTimeStamp: '9/20/2021, 10:54:26 AM',
@@ -279,6 +280,7 @@ describe('#Offer-Entity', () => {
assert.property(result, 'localTimestamp')
assert.property(result, 'txid')
assert.property(result, 'p2wdbHash')
assert.property(result, 'tokenType')
})
})
})
+66
View File
@@ -0,0 +1,66 @@
/*
Unit tests for the bin/server.js file
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const Server = require('../../../bin/server')
describe('#server', () => {
let uut, sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new Server()
})
afterEach(() => sandbox.restore())
describe('#startServer', () => {
it('should start the server', async () => {
// Mock dependencies
sandbox.stub(uut.mongoose, 'connect').resolves()
sandbox.stub(uut.controllers, 'initAdapters').resolves()
sandbox.stub(uut.controllers, 'initUseCases').resolves()
sandbox.stub(uut.controllers, 'attachRESTControllers').resolves()
sandbox.stub(uut.adminLib, 'createSystemUser').resolves(true)
sandbox.stub(uut.controllers, 'attachControllers').resolves()
sandbox.stub(uut, 'sleep').resolves()
uut.config.env = 'dev'
const result = await uut.startServer()
// console.log('result: ', result)
assert.property(result, 'env')
// Turn off the server.
uut.server.close()
// Restor config env
uut.config.env = 'test'
})
it('should exit on failure', async () => {
// Force an error
sandbox.stub(uut.mongoose, 'connect').rejects(new Error('test error'))
// Prevent default behavior of exiting the program.
sandbox.stub(uut, 'sleep').resolves()
sandbox.stub(uut.process, 'exit').returns()
await uut.startServer()
// Not throwing an error is a success
})
})
describe('#sleep', () => {
it('should execute', async () => {
await uut.sleep(1)
})
})
})
+70
View File
@@ -0,0 +1,70 @@
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.907Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.909Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.910Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.912Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:36:43.915Z"}
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.979Z"}
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.980Z"}
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.981Z"}
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.982Z"}
{"level":"error","message":"Error in lib/nodemailer.js/sendEmail()","timestamp":"2022-06-27T17:36:58.983Z"}
{"level":"error","message":"Error in lib/nodemailer.js/validateEmailArray()","timestamp":"2022-06-27T17:36:59.002Z"}
{"level":"error","message":"Error in lib/nodemailer.js/validateEmailArray()","timestamp":"2022-06-27T17:36:59.003Z"}
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.005Z"}
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.006Z"}
{"level":"error","message":"Error in lib/nodemailer.js/getHtmlFromObject()","timestamp":"2022-06-27T17:36:59.008Z"}
{"level":"info","message":"Warning: Can not send JSON RPC response. Can not determine which peer this message came from.","timestamp":"2022-06-27T17:36:59.353Z"}
{"level":"info","message":"Rejecting invalid JSON RPC command.","timestamp":"2022-06-27T17:36:59.354Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: ea442e78-2297-466a-823b-9cbb12c8ddd9, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.355Z"}
{"level":"error","message":"Error in rpc router(): test error","stack":"Error: test error\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/a10-rpc.unit.js:110:49)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:36:59.357Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: 9ef6ad90-7fc3-4cb3-8769-4498e5512b3d, type: request, method: users","timestamp":"2022-06-27T17:36:59.358Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: bd57a1cf-7f2f-4ff2-93d4-6727eec4d778, type: request, method: auth","timestamp":"2022-06-27T17:36:59.359Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: 8fd9edc8-6a45-4042-ac06-9ace60567cbf, type: request, method: about","timestamp":"2022-06-27T17:36:59.361Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: 1b27624a-e698-4bbe-9183-b860a1a0d78d, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.363Z"}
{"level":"info","message":"JSON RPC received from peerA, ID: 40fabddf-59c6-4910-a22c-63da7387f99a, type: request, method: unknownMethod","timestamp":"2022-06-27T17:36:59.365Z"}
{"level":"error","message":"Error in authUser(): Login credential do not match","stack":"Error: Login credential do not match\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:144:18)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.421Z"}
{"level":"error","message":"Error in authUser(): login must be specified","stack":"Error: login must be specified\n at AuthRPC.authUser (/home/trout/work/psf/code/ipfs-service-provider/src/controllers/json-rpc/auth/index.js:78:93)\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:165:34)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.423Z"}
{"level":"error","message":"Error in authUser(): password must be specified","stack":"Error: password must be specified\n at AuthRPC.authUser (/home/trout/work/psf/code/ipfs-service-provider/src/controllers/json-rpc/auth/index.js:78:284)\n at Context.<anonymous> (/home/trout/work/psf/code/ipfs-service-provider/test/unit/controllers/json-rpc/auth.json-rpc.controller.unit.js:185:34)\n at callFn (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:366:21)\n at Test.Runnable.run (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runnable.js:354:5)\n at Runner.runTest (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:666:10)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:789:12\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:581:14)\n at /home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:591:7\n at next (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:474:14)\n at Immediate._onImmediate (/home/trout/work/psf/code/ipfs-service-provider/node_modules/mocha/lib/runner.js:559:5)\n at processImmediate (node:internal/timers:466:21)","timestamp":"2022-06-27T17:37:04.425Z"}
{"level":"error","timestamp":"2022-06-27T17:37:04.466Z"}
{"level":"info","message":"Running server in environment: dev","timestamp":"2022-06-27T17:37:04.487Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.495Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.495Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.496Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.497Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.497Z"}
{"level":"error","message":"Error in lib/users.js/getAllUsers()","timestamp":"2022-06-27T17:37:04.499Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.502Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.503Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.503Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.504Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.505Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:04.506Z"}
{"level":"error","message":"Error in lib/users.js/deleteUser()","timestamp":"2022-06-27T17:37:04.508Z"}
{"level":"info","message":"Running server in environment: test","timestamp":"2022-06-27T17:37:04.514Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:04.638Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.273Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.277Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.280Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:05.286Z"}
{"level":"error","timestamp":"2022-06-27T17:37:05.412Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 1","timestamp":"2022-06-27T17:37:05.441Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.448Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.452Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.457Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.463Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.467Z"}
{"level":"error","message":"Error in lib/users.js/updateUser()","timestamp":"2022-06-27T17:37:05.473Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.478Z"}
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.478Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 1","timestamp":"2022-06-27T17:37:05.600Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d27f, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.603Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d27d","timestamp":"2022-06-27T17:37:05.615Z"}
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.615Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.619Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.622Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.624Z"}
{"level":"error","message":"Error in lib/contact.js/sendEmail()","timestamp":"2022-06-27T17:37:05.627Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac1b4f08950b955d2a6, Target user: Target Id","timestamp":"2022-06-27T17:37:05.754Z"}
{"level":"verbose","message":"Calling user and target user do not match! Calling user: 62b9eac0b4f08950b955d274, Target user: 62b9eac1b4f08950b955d2a6","timestamp":"2022-06-27T17:37:05.756Z"}
{"level":"verbose","message":"It's ok. The user is an admin.","timestamp":"2022-06-27T17:37:05.756Z"}
{"level":"error","message":"Error in lib/users.js/createUser()","timestamp":"2022-06-27T17:37:06.060Z"}
+4 -2
View File
@@ -127,9 +127,11 @@ class MockBchWallet {
]
},
nft: {
groupMintBatons: [],
groupTokens: [],
tokens: []
},
group: {
tokens: [],
mintBatons: []
}
}
}
+2
View File
@@ -8,6 +8,8 @@ class IPFSCoord {
}
async start () {}
async subscribeToChat() {}
}
module.exports = IPFSCoord
@@ -47,4 +47,17 @@ describe('#use-cases', () => {
}
})
})
describe('#start', () => {
it('should initialize async use cases', async () => {
const result = await uut.start()
assert.equal(result, true)
})
// it('should catch and throw errors', async () => {
// // Force an error
// sandbox.stub()
// })
})
})
+2 -1
View File
@@ -96,7 +96,8 @@ describe('#offer-use-case', () => {
'241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87',
utxoVout: 0,
makerAddr: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
ticker: 'TROUT'
ticker: 'TROUT',
tokenType: 1
},
timestamp: '2021-09-20T17:54:26.395Z',
localTimeStamp: '9/20/2021, 10:54:26 AM',
+8 -2
View File
@@ -125,12 +125,18 @@ describe('#order-use-case', () => {
it('should catch and throw an error', async () => {
try {
await uut.createOrder()
await uut.createOrder({ a: 'b' })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot set')
assert.include(err.message, 'entry does not contain required properties')
}
})
it('should exit quietly if the entry is empty', async () => {
const result = await uut.createOrder()
assert.equal(result, false)
})
})
})