Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to develop cryptocurrency applications with Node.js and NoSQL

2025-02-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces "how to use Node.js and NoSQL to develop cryptocurrency applications". In daily operation, I believe many people have doubts about how to use Node.js and NoSQL to develop cryptocurrency applications. Xiaobian consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "how to develop cryptocurrency applications with Node.js and NoSQL". Next, please follow the editor to study!

We need to make a change here. So far, we have completed the account-oriented operation in the NoSQL database. Another important aspect is trading. For example, maybe user X deposits some US dollars for BTC and user Y makes withdrawals. We need to store and query the transaction information.

The API endpoint function will hold the transaction data, but we can still query it.

GetAccountBalance (account) {var statement = "SELECT SUM (tx.satoshis) AS balance FROM" + this.bucket._name + "AS tx WHERE tx.type = 'transaction' AND tx.account = $account"; var query = Couchbase.N1qlQuery.fromString (statement) Return new Promise ((resolve, reject) = > {this.bucket.query (query, {"account": account}, (error, result) = > {if (error) {reject ({"code": error.code, "message": error.message});} resolve ({"balance": result [0] .balance});})

Given an account, we want to get the account balance of a specific user.

Wait a minute, let's take a step back, because haven't we already created some account balance functions? Technically, we did, but these functions are used to check the wallet balance, not the account balance.

This is where some of my experience has become a gray area. Every time Bitcoin is sent, a fee is charged, sometimes quite expensive. When you deposit money, it is not cost-effective to transfer the money to your wallet because it will charge miners' fees. Then you will be charged for withdrawal or even transfer. By then you have lost most of your bitcoin.

Instead, I think the exchange has a holding account similar to the money market account of the stock exchange. There should be a record of funds in your account, but technically, it's not in your wallet. If you want to transfer money, you need to transfer money from the application address rather than your user address. When you quit, it's just subtracted.

Again, I don't know if this really works, but this is the way I try to avoid charging everywhere.

Go back to our getAccountBalance function. We are dealing with the sum of each transaction. Deposits have positive values, while transfers and withdrawals have negative values. Putting this information together can provide you with accurate figures, not including the balance of your wallet. We will get a wallet balance account later.

Given how little we know about the account balance, we can try to create a transaction from our wallet:

CreateTransactionFromAccount (account, source, destination, amount) {return new Promise ((resolve, reject) = > {this.getAddressBalance (source) .then (sourceAddress = > {if (sourceAddress.balanceSat)

< amount) { return reject({ "message": "Not enough funds in account." }); } this.getPrivateKeyFromAddress(account, source).then(keypair =>

{this.getAddressUtxo (source) .then (utxo = > {var transaction = new Bitcore.Transaction (); for (var I = 0; I)

< utxo.length; i++) { transaction.from(utxo[i]); } transaction.to(destination, amount); this.addAddress(account).then(change =>

{transaction.change (change.address); transaction.sign (keypair.secret); resolve (transaction);}, error = > reject (error));}, error = > reject (error)) });}

If the source address, destination address, and amount are provided, we can create and sign a transaction to broadcast later on the Bitcoin network.

First, we get the balance of the source address in question. We need to make sure that it has enough UTXO to meet delivery expectations. Note that in this example, we are performing a single address transaction. If you want to be complex, you can send it from multiple addresses in a single transaction. We won't do that here. If our single address has enough money, we will get its private key and UTXO data. Using UTXO data, we can create a bitcoin transaction, apply the destination address and change the address, and then sign the transaction using our private key. The response can be broadcast.

Similarly, suppose we want to transfer bitcoin from our holding account:

CreateTransactionFromMaster (account, destination, amount) {return new Promise ((resolve, reject) = > {this.getAccountBalance (account) .then (accountBalance = > {if (accountBalance.balance)

< amount) { reject({ "message": "Not enough funds in account." }); } var mKeyPairs = this.getMasterKeyPairs(); var masterAddresses = mKeyPairs.map(a =>

A.address); this.getMasterAddressWithMinimum (masterAddresses, amount) .then (funds = > {this.getAddressUtxo (funds.address) .then (utxo = > {var transaction = new Bitcore.Transaction (); for (var I = 0; I)

< utxo.length; i++) { transaction.from(utxo[i]); } transaction.to(destination, amount); var change = helper.getMasterChangeAddress(); transaction.change(change.address); for(var j = 0; j < mKeyPairs.length; j ++) { if(mKeyPairs[j].address == funds.address) { transaction.sign(mKeyPairs[j].secret); } } var tx = { account: account, satoshis: (amount * -1), timestamp: (new Date()).getTime(), status: "transfer", type: "transaction" }; this.insert(tx).then(result =>

{resolve (transaction);}, error = > reject (error));}, error = > reject (error));});}

Let's assume that our exchange address is full of crazy bitcoins to meet demand.

The first step is to make sure that we have funds in our holding account. We can execute a query that summarizes each transaction to get a valid number. If we have enough, we can get all 10 master key pairs and addresses. We need to check which address has enough funds to send. Keep in mind that there may be more single-address transactions here.

If the address has enough money, we will get the UTXO data and start the transaction. Instead of our wallets as the source address this time, we use swapped wallets. After we get the signed transaction, we want to create a transaction in the database to subtract the value we are transferring.

Before we get to the API endpoint, I want to retry a few things:

I assume that the popular exchange has a holding account to avoid charging for wallet addresses.

In this example, we use single-address transactions instead of aggregating what we have.

I should be encrypting the key data in the account document.

I didn't broadcast any deals, just created them.

Now let's focus on our API endpoint, which is a simple part.

Using Express Framework to design RESTful API endpoints

Keep in mind that, as we configured at the beginning, our endpoints will be divided into three files that act as groups. We will start with the smallest and simplest set of endpoints, which are more practical than any other endpoint.

Open the routes/utility.js file for the project and contain the following:

Const Bitcore = require ("bitcore-lib"); const Mnemonic = require ("bitcore-mnemonic"); module.exports = (app) = > {app.get ("/ mnemonic", (request, response) = > {response.send ({"mnemonic": (new Mnemonic (Mnemonic.Words.ENGLISH)). ToString ()}) App.get ("/ balance/value", (request, response) = > {Request ("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market = > {response.send ({" value ":" $"+ (JSON.parse (market) [0] .price _ usd * request.query.balance) .tofixed (2)}, error = > {response.status (500) .send (error)) });})

Here we have two endpoints, one for generating mnemonic seeds and the other for obtaining the legal value of the bitcoin balance. Neither of these is really necessary, but it might be good to generate seed values for later saving in our configuration file on first startup.

Now open the project's routes/account.js file so that we can process the account information:

Const Request = require ("request-promise"); const Joi = require ("joi"); const helper = require (".. / app"). Helper;module.exports = (app) = > {app.post ("/ account", (request, response) = > {}); app.put ("/ account/address/:id", (request, response) = > {}); app.get ("/ account/addresses/:id", (request, response) = > {}) App.get ("/ addresses", (request, response) = > {}); app.get ("/ account/balance/:id", (request, response) = > {}); app.get ("/ address/balance/:id", (request, response) = > {});}

Note that we are extracting the helper program class from the app.js file that has not been started. Using it with it now will make sense in the future, although it's nothing special.

When creating an account, we have the following:

App.post ("/ account", (request, response) = > {var model = Joi.object (). Keys ({firstname: Joi.string (). Required (), lastname: Joi.string (). Required (), type: Joi.string (). Forbidden (). Default ("account")}) Joi.validate (request.body, model, {stripUnknown: true}, (error, value) = > {if (error) {return response.status (500) .send (error);} helper.createAccount (value) .then (result = > {response.send (value);}, error = > {response.status (500) .send (error);});})

Using Joi, we can validate the request body and throw an error when an error occurs. Assuming that the request body is correct, we can call the createAccount function to save a new account in the database.

After creating the account, we can add some addresses:

App.put ("/ account/address/:id", (request, response) = > {helper.addAddress (request.params.id) .then (result = > {response.send (result);}, error = > {return response.status (500) .send (error);});})

Using the sent account ID, we can call our addAddress function to use subdocument operations on our documents.

Not bad, huh?

To get all the addresses for a particular account, we might have the following:

App.get ("/ account/addresses/:id", (request, response) = > {helper.getAddresses (request.params.id) .then (result = > {response.send (result);}, error = > {response.status (500) .send (error);});})

Or, if we do not provide id, we can use the following endpoint function to get all addresses from all accounts:

App.get ("/ addresses", (request, response) = > {helper.getAddresses () .then (result = > {response.send (result);}, error = > {response.status (500) .send (send);});})

Now it's probably the trickiest endpoint function. Suppose we want to get the account balance, which includes the holding account and the address of each wallet. We can do the following:

App.get ("/ account/balance/:id", (request, response) = > {helper.getAddresses (request.params.id) .then (addresses = > helper.getWalletBalance (addresses)) .then (balance = > {helper.getAccountBalance (request.params.id) .then (result = > {response.send ({"balance": balance.balance + result.balance})) }, error = > {response.status (500) .send ({"code": error.code, "message": error.message});}, error = > {response.status (500) .send ({"code": error.code, "message": error.message});});})

The above will call our two functions to get the balance and add the results together to get a large balance.

Account endpoints are not particularly interesting. Creating deals is more exciting.

Open the routes/transaction.js file for the project and contain the following:

Const Request = require ("request-promise"); const Joi = require ("joi"); const Bitcore = require ("bitcore-lib"); const helper = require (".. / app"). Helper;module.exports = (app) = > {app.post ("/ withdraw", (request, response) = > {}); app.post ("/ deposit", (request, response) = > {}); app.post ("/ transfer", (request, response) = > {});

We have three different types of transactions. We can deposit bitcoin in legal tender, withdraw bitcoin for legal tender, and transfer bitcoin to a new wallet address.

Let's take a look at the deposit endpoint:

App.post ("/ deposit", (request, response) = > {var model = Joi.object (). Keys ({usd: Joi.number (). Required (), id: Joi.string (). Required ()}); Joi.validate (request.body, model, {stripUnknown: true}, (error, value) = > {if (error) {return response.status (500) .send (error)) } Request ("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market = > {var btc = value.usd / JSON.parse (market) [0] .price _ usd) Var transaction = {account: value.id, usd: value.usd, satoshis: Bitcore.Unit.fromBTC (btc). ToSatoshis (), timestamp: (new Date ()). GetTime (), status: "deposit", type: "transaction"} Helper.insert (transaction) .then (result = > {response.send (result);}, error = > {response.status (500) .send (error);});}, error = > {response.status (500) .send (error);});})

After we validate the input, we use CoinMarketCap to check the current value of US dollar bitcoin. Using the data in the response, we can calculate how much bitcoin we should get based on the amount of dollars deposited.

After creating the database transaction, we can save it because it is a positive number and it will return a positive balance when querying.

Now let's say we want to withdraw money from Bitcoin:

App.post ("/ withdraw", (request, response) = > {var model = Joi.object (). Keys ({satoshis: Joi.number (). Required (), id: Joi.string (). Required ()}); Joi.validate (request.body, model, {stripUnknown: true}, (error, value) = > {if (error) {return response.status (500) .send (error)) } helper.getAccountBalance (value.id) .then (result = > {if (result.balance = = null | | (result.balance-value.satoshis))

< 0) { return response.status(500).send({ "message": "There are not `" + value.satoshis + "` satoshis available for withdrawal" }); } Request("https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market =>

{var usd = (Bitcore.Unit.fromSatoshis (value.satoshis). ToBTC () * JSON.parse (market) [0] .price _ usd) .tofixed (2) Var transaction = {account: value.id, satoshis: (value.satoshis *-1), usd: parseFloat (usd), timestamp: (new Date ()) .getTime (), status: "withdrawal" Type: "transaction"} Helper.insert (transaction) .then (result = > {response.send (result);}, error = > {response.status (500) .send (error);});}, error = > {response.status (500) .send (error);}) }, error = > {return response.status (500.send (error);})

A similar incident is happening here. After verifying the request subject, we obtain the account balance and make sure that the amount we withdraw is less than or equal to our balance. If so, we can make another transaction according to the current price of CoinMarketCap. We will create a transaction with a negative value and save it to the database.

In both cases, we rely on CoinMarketCap, which has been negatively controversial in the past. You may want to choose different resources for the deal.

Finally, we have a transfer:

App.post ("/ transfer", (request, response) = > {var model = Joi.object (). Keys ({amount: Joi.number (). Required (), sourceaddress: Joi.string (). Optional (), destinationaddress: Joi.string (). Required (), id: Joi.string (). Required ()}) Joi.validate (request.body, model, {stripUnknown: true}, (error, value) = > {if (error) {return response.status (500) .send (error);} if (value.sourceaddress) {helper.createTransactionFromAccount (value.id, value.sourceaddress, value.destinationaddress, value.amount) .then (result = > {response.send (result)) }, error = > {response.status (500) .send (error);} else {helper.createTransactionFromMaster (value.id, value.destinationaddress, value.amount) .then (result = > {response.send (result);}, error = > {response.status (500) .send (error);}) }))

If the request contains the source address, we will transfer money from our own wallet, otherwise we will transfer money from the wallet managed by the exchange.

All of this is based on the features we created earlier.

Through endpoints, we can focus on guiding our application and draw conclusions.

Boot the Express Framework application

Now we have two files that remain unaffected by the example. We haven't added configuration or driver logic to guide our endpoints.

Open the config.json file for the project and contain the following:

{"mnemonic": "manage inspire agent october potato thought hospital trim shoulder round tired kangaroo", "host": "localhost", "bucket": "bitbase", "username": "bitbase", "password": "123456"}

Remember that this file is very sensitive. Consider locking it or even using different methods. If the seed is exposed, each private key of all user accounts and exchange accounts can be easily obtained.

Now open the project's app.js file and contain the following:

Const Express = require ("express"); const BodyParser = require ("body-parser"); const Bitcore = require ("bitcore-lib"); const Mnemonic = require ("bitcore-mnemonic"); const Config = require (". / config"); const Helper = require (". / classes/helper"); var app = Express (); app.use (BodyParser.json ()); app.use (BodyParser.urlencoded ({extended: true})); var mnemonic = new Mnemonic (Config.mnemonic); var master = new Bitcore.HDPrivateKey (mnemonic.toHDPrivateKey ()) Module.exports.helper = new Helper (Config.host, Config.bucket, Config.username, Config.password, master); require (". / routes/account.js") (app); require (". / routes/transaction.js") (app); require (". / routes/utility.js") (app); var server = app.listen (3000, () = > {console.log ("Listening at:" + server.address (). Port + ");})

What we are doing is initializing Express, loading configuration information and linking to our routes. The module.exports.helper variable is our singleton and will be used in every other JavaScript file.

At this point, the study on "how to develop cryptocurrency applications with Node.js and NoSQL" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report