在使用Node.js和NoSQL开发比特币加密货币应用程序(上)中,我们创建了HD钱包,它可以为给定的种子生成无限量的密钥,每个密钥代表一个用户钱包。我们将根据主种子创建每个包含钱包的用户帐户。下面我们接着来看如何进行交易、查询余额等重要功能如何实现。

我们要在这里改变一下。到目前为止,我们已经在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端点,这是一个简单的部分。

使用Express Framework设计RESTful 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);});}});
});
复制代码

如果请求包含源地址,我们将从我们自己的钱包转账,否则我们将从交换管理的钱包转账。

所有这些都基于我们之前创建的功能。

通过端点,我们可以专注于引导我们的应用程序并得出结论。

引导Express Framework应用程序

现在我们有两个文件保持不受示例的影响。我们还没有添加配置或驱动逻辑来引导我们的端点。

打开项目的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和Couchbase作为NoSQL数据库来构建自己的加密货币交易。我们涵盖了很多,从生成HD钱包到创建具有复杂数据库逻辑的端点。

我不能强调这一点。我是加密货币爱好者,在金融领域没有真正的经验。我分享的东西应该有效,但可以做得更好。不要忘记加密密钥并确保种子安全。测试你的工作,知道自己正在做什么。

如果你想下载此项目,请在GitHub上查看。如果你想分享关于该主题的见解,经验等,请在评论中分享。社区可以努力创造伟大的东西!

如果你是Golang的粉丝,我在之前的教程中创建了一个类似的项目。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是原文使用Node.js和NoSQL开发比特币加密货币应用程序

Node.js和NoSQL开发比特币加密货币应用程序(下)相关推荐

  1. 《Node.js区块链开发》——1.2 什么是加密货币

    本节书摘来自华章计算机<Node.js区块链开发>一书中的第1章,第1.2节,作者 朱志文,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.2 什么是加密货 ...

  2. 《Node.js区块链开发》

    <Node.js区块链开发> [注]基于ES6的最新版,正在筹划中. 特别说明 亿书,让有知识的人富起来.我们的产品,将全部基于P2P网络进行开发设计,面向未来进行技术研发. 纸质书籍:h ...

  3. 《Node.js区块链开发》一3.5 亿书对DPoS机制的改进

    本节书摘来华章计算机<Node.js区块链开发>一书中的第3章 ,第3.5节,朱志文 著 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.5 亿书对DPoS ...

  4. Node.js模块化开发||Node.js中模块化开发规范

    JavaScript开发弊端 a.js b.js JavaScript在使用时存在两大问题,文件依赖和命名冲突. 生活中的模块化开发 软件中的模块化开发 app.j user.一个功能就是一个模块,多 ...

  5. 《Node.js区块链开发》——1.6 参考

    本节书摘来自华章计算机<Node.js区块链开发>一书中的第1章,第1.6节,作者 朱志文,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.6 参考 < ...

  6. 【Node学习】—Node.js中模块化开发的规范

    [Node学习]-Node.js中模块化开发的规范 Node.js 规定一个JavaScript就是一个模块,模块内部定义的变量和函数默认情况下在外部无法得到 模块化内部可以使用exports对象进行 ...

  7. 《Node.js区块链开发》PDF版电子书下载

    <Node.js区块链开发> 内容简介: 开发人员.本书涉及前端.后台的方方面面,无论是前端开发人员,还是后台开发者都可以学习参考. 架构师.区块链本身是分布式.云计算的典范,本书详细描述 ...

  8. Node.js 从零开发web server博客项目--项目初始化

    本篇博客记录了<Node.js 从零开发web server博客项目>的原生开发系列内容. 开篇主要介绍原生项目的搭建,以及初步的项目结构设计. 一.项目初始化 新建项目目录,并进入到项目 ...

  9. Node.js(3)------服务器端开发

    一.Node服务器开发 1.学习服务器开发的目的 (1).可以和后端程序员进行紧密的配合 (2).让业务前置 (3).扩展知识面 2.服务器的主要工作 (1).实现项目的业务逻辑 (2).实现数据的增 ...

最新文章

  1. Silverlight C# 游戏开发:Silverlight开发环境
  2. 管理者如何打造一个有执行力的团队?
  3. Devexpress TreeList控件绑定显示父子节点对像
  4. 分步学习Struts(三) 原理分析
  5. C++读取文本文件中以TAB作为分隔符,且中间字段有为空的情况的方法?
  6. 金属材料手册_不锈钢品种手册简化版
  7. java insert_java获取新insert数据自增id的实现方法
  8. 两个文本框异动任何一个能即时更新计算结果
  9. 杨辉三角(Python)
  10. 视频直播app源码,可拖拽悬浮球
  11. html table 斜线表头,Table表格加斜线表头
  12. 用isprime判断是否为素数(函数法)
  13. 2021-2027中国家具拉手市场现状及未来发展趋势
  14. mysql高可用架构——MMM用群集架构
  15. 在故事板中加载 nib 时 IBOutlet 为 nil
  16. 理想国pytorch入门教程
  17. 用php向新年问候,给客户的新年问候语
  18. Tecentriq (Atezolizumab)将用于多种癌症的治疗
  19. 【学习笔记】上下界网络流
  20. java计算机毕业设计图片分享网站源码+系统+数据库+lw文档+mybatis+运行部署

热门文章

  1. hive sql操作
  2. 【Lv1-Lesson002】He and She
  3. 活动目录最佳实践分析器
  4. shell MAC 地址 校验
  5. 数据结构实践——队列数组
  6. HDU1533 Going Home(最小费用最大流 spfa模版)
  7. 算法导论——所有点对最短路径:稀疏图Johnson算法
  8. 第十七课:js数据缓存系统的原理
  9. mysql进程SHOW PROCESSLIST详解Command命令状态
  10. sae上部署第一个站