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

栏目: Node.js · 发布时间: 6年前

内容简介:在我们要在这里改变一下。到目前为止,我们已经在NoSQL数据库中完成了面向帐户的操作。另一个重要方面是交易。例如,也许用户X为BTC存入一些美元货币,而用户Y进行提款。我们需要存储和查询该交易信息。API端点函数将保存交易数据,但我们仍然可以查询它。

使用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开发比特币加密货币应用程序(下)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Applications (Hacking Exposed)

Web Applications (Hacking Exposed)

Joel Scambray、Mike Shema / McGraw-Hill Osborne Media / 2002-06-19 / USD 49.99

Get in-depth coverage of Web application platforms and their vulnerabilities, presented the same popular format as the international bestseller, Hacking Exposed. Covering hacking scenarios across diff......一起来看看 《Web Applications (Hacking Exposed)》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具