您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么用Node.js和NoSQL開發加密貨幣應用程序”,在日常操作中,相信很多人在怎么用Node.js和NoSQL開發加密貨幣應用程序問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么用Node.js和NoSQL開發加密貨幣應用程序”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
我們要在這里改變一下。到目前為止,我們已經在NoSQL數據庫中完成了面向帳戶的操作。另一個重要方面是交易。例如,也許用戶X為BTC存入一些美元貨幣,而用戶Y進行提款。我們需要存儲和查詢該交易信息。
API端點函數將保存交易數據,但我們仍然可以查詢它。
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 }); }); }); }
給定一個帳戶,我們希望獲得特定用戶的帳戶余額。
等一下,讓我們退后一步,因為我們不是已經創建了一些帳戶余額功能嗎?從技術上講,我們做了,但這些功能用于檢查錢包余額,而不是帳戶余額。
這是我的一些經驗變成灰色區域的地方。每次發送比特幣時,都會收取費用,有時費用相當昂貴。當你存款時,將錢轉入你的錢包并不符合成本效益,因為這將收取礦工費。然后你將被收取撤回甚至轉賬的費用。那時你已經失去了大部分的比特幣。
相反,我認為交易所有一個類似于證券交易所貨幣市場賬戶的持有賬戶。你的帳戶中應該有資金的記錄,但從技術上講,它不在錢包中。如果你想要轉賬,則需要從應用程序地址而不是你的用戶地址進行轉賬。當你退出時,它只是被減去。
再說一次,我不知道這是否真的如何運作,但這就是我為了避免各處收費而采取的方式。
回到我們的getAccountBalance
函數。我們正在處理每筆交易的總和。存款具有正值,而轉賬和取款具有負值。將這些信息匯總在一起可以為你提供準確的數字,不包括你的錢包余額。稍后我們將獲得一個錢包余額帳戶。
鑒于我們對帳戶余額知之甚少,我們可以嘗試從錢包中創建一個交易:
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)); }, error => reject(error)); }, error => reject(error)); }); }
如果提供了源地址,目的地地址和金額,我們可以創建并簽署一個交易,以便稍后在比特幣網絡上廣播。
首先,我們得到有問題的源地址的余額。我們需要確保它有足夠的UTXO來滿足發送量預期。請注意,在此示例中,我們正在執行單個地址交易。如果你想變得復雜,可以在單個交易中從多個地址發送。我們不會在這里這樣做。如果我們的單個地址有足夠的資金,我們會獲得它的私鑰和UTXO數據。使用UTXO數據,我們可以創建比特幣交易,應用目的地地址和更改地址,然后使用我們的私鑰對交易進行簽名。可以廣播響應。
同樣地,假設我們想從我們的持有賬戶轉賬比特幣:
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)); }, error => reject(error)); }, error => reject(error)); }); }
我們假設我們的交換地址裝滿了瘋狂的比特幣以滿足需求。
第一步是確保我們的持有賬戶中有資金。我們可以執行總結每個交易的查詢以獲得有效數字。如果我們有足夠的,我們可以獲得所有10個主密鑰對和地址。我們需要檢查哪個地址有足夠的資金發送。請記住,這里的單一地址交易可能會有更多。
如果地址有足夠的資金,我們會獲得UTXO數據并開始進行交易。這次代替我們的錢包作為源地址,我們使用交換的錢包。在我們獲得簽名交易之后,我們想在數據庫中創建一個交易來減去我們正在傳輸的值。
在我們進入API端點之前,我想重新嘗試一些事情:
我假設熱門的交易所有一個持有賬戶,以避免對錢包地址征收費用。
我們在此示例中使用單地址交易,而不是聚合我們擁有的內容。
我應該是在加密帳戶文檔中的關鍵數據。
我沒有廣播任何交易,只創建它們。
現在讓我們關注我們的API端點,這是一個簡單的部分。
請記住,正如我們在開始時配置的那樣,我們的端點將分為三個文件,這些文件充當分組。我們將從最小和最簡單的端點組開始,這些端點比其他任何端點都更實用。
打開項目的routes/utility.js
文件并包含以下內容:
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); }); }); }
這里我們有兩個端點,一個用于生成助記符種子,另一個用于獲取比特幣余額的法定值。這兩者都不是真正必要的,但是在第一次啟動時,生成種子值以便稍后保存在我們的配置文件中可能會很好。
現在打開項目的routes/account.js
文件,以便我們處理帳戶信息:
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) => { }); }
請注意,我們正在從尚未啟動的app.js
文件中提取helper程序類。現在就跟它一起使用它以后會有意義,雖然它沒什么特別的。
在創建帳戶時,我們有以下內容:
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); }); }); });
使用Joi
我們可以驗證請求正文并在錯誤時拋出錯誤。假設請求正文是正確的,我們可以調用createAccount
函數在數據庫中保存一個新帳戶。
創建帳戶后,我們可以添加一些地址:
app.put("/account/address/:id", (request, response) => { helper.addAddress(request.params.id).then(result => { response.send(result); }, error => { return response.status(500).send(error); }); });
使用發送的帳戶ID
,我們可以調用我們的addAddress
函數來對我們的文檔使用子文檔操作。
還不錯吧?
要獲取特定帳戶的所有地址,我們可能會有以下內容:
app.get("/account/addresses/:id", (request, response) => { helper.getAddresses(request.params.id).then(result => { response.send(result); }, error => { response.status(500).send(error); }); });
或者,如果我們不提供id
,我們可以使用以下端點函數從所有帳戶獲取所有地址:
app.get("/addresses", (request, response) => { helper.getAddresses().then(result => { response.send(result); }, error => { response.status(500).send(error); }); });
現在可能是最棘手的端點功能。假設我們希望獲得帳戶余額,其中包括持有帳戶以及每個錢包地址。我們可以做到以下幾點:
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 }); }); });
以上將調用我們的兩個函數來獲得余額,并將結果加在一起以獲得一個巨大的余額。
帳戶端點不是特別有趣。創建交易更令人興奮。
打開項目的routes/transaction.js
文件并包含以下內容:
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) => { }); }
我們有三種不同類型的交易。我們可以為比特幣存入法定貨幣,為法定貨幣提取比特幣,并將比特幣轉賬到新的錢包地址。
我們來看看存款端點:
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); }); }); });
在我們驗證輸入后,我們使用CoinMarketCap
檢查美元比特幣的當前值。使用響應中的數據,我們可以根據存入的美元金額計算出應該獲得多少比特幣。
創建數據庫交易后,我們可以保存它,因為它是一個正數,它將在查詢時返回正余額。
現在讓我們說我們想從比特幣中提取資金:
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); }); }); });
類似的事件正在這里發生。在驗證請求主體后,我們獲得帳戶余額并確保我們提取的金額小于或等于我們的余額。如果是,我們可以根據CoinMarketCap
的當前價格進行另一次交易。我們將使用負值創建一個交易并將其保存到數據庫中。
在這兩種情況下,我們都依賴于CoinMarketCap
,它在過去一直存在負面爭議。你可能希望為交易選擇不同的資源。
最后,我們有轉賬:
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); }); } }); });
如果請求包含源地址,我們將從我們自己的錢包轉賬,否則我們將從交換管理的錢包轉賬。
所有這些都基于我們之前創建的功能。
通過端點,我們可以專注于引導我們的應用程序并得出結論。
現在我們有兩個文件保持不受示例的影響。我們還沒有添加配置或驅動邏輯來引導我們的端點。
打開項目的config.json
文件,并包含以下內容:
{ "mnemonic": "manage inspire agent october potato thought hospital trim shoulder round tired kangaroo", "host": "localhost", "bucket": "bitbase", "username": "bitbase", "password": "123456" }
記住這個文件非常敏感。考慮將其鎖定或甚至使用不同的方法。如果種子被暴露,則可以毫不費力地獲得所有用戶帳戶和交換帳戶的每個私鑰。
現在打開項目的app.js
文件并包含以下內容:
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 + "..."); });
我們正在做的是初始化Express
,加載配置信息以及鏈接我們的路由。module.exports.helper
變量是我們的單例,將在每個其他JavaScript文件中使用。
到此,關于“怎么用Node.js和NoSQL開發加密貨幣應用程序”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。