一、介绍说明
在开发账号模块之前,我们需要先用cleos工具帮我们创建一个账号,是什么原因需要它去创建和如何创建的请查看“使用cleos管理账号权限”章节的内容,这样我们才好使用开发的钱包项目查看账号详情以及创建账号。
创建账号属于一个交易,若使用RPC接口是非常的繁琐容易出错,这里我们使用eosjs库中封装的交易的接口,使用及其简单,eosjs的使用说明请查看“深入浅出EOSJS:连接到主网、测试网、交易”章节的内容。
本项目在配置eos对象时,获取了钱包里面所有的私钥进行配置,这样简化了用户每次交易都输入秘钥的操作,只需要输入钱包密码即可。
二、项目源码一:获取账号列表
账号模块的主要功能包括:
- 账号列表
- 通过私钥导入账号
- 新建账号
- 查看账号详情
- 查询余额
- 获取公私钥对
- 查询权限配置
这部分源码先介绍获取账号列表的前后端实现。
1. web.js
编辑controllers文件夹下的web.js文件,实现后端返回给前端账号列表的页面。
module.exports = {
......
getAccountHtml:async(ctx) => {
await ctx.render("account.html")
},
}
2. account.js
在controllers文件夹下新建account.js文件,后端实现获取账号列表的功能。
let httpRequest = require("../utils/httpRequest")
let config = require("../config/config")
let {success, fail} = require("../utils/myUtils")
let myUtils = require("../utils/myUtils")
module.exports = {
//通过钱包名称获取账号列表
accountListForWallet: async (ctx) => {
console.log(JSON.stringify(ctx.request.body))
let { wallet, password } = ctx.request.body
//获取钱包管理的所有公私钥对
let res = await httpRequest.postRequest(config.walletGetKeys, [wallet, password])
let accountList = []
if (res.code == 0) {
for (const index in res.data) {
let keys = res.data[index]
console.log(keys[0])
//查询公钥关联的所有账号
let resData = await httpRequest.postRequest(config.accountListForKey, { "public_key": keys[0] })
if (resData.code == 0) {
resData.data.account_names.forEach(account => {
//去重
if (accountList.indexOf(account) < 0) {
accountList.push(account)
}
})
}
};
}
console.log("accountList:", accountList)
res.data = accountList
ctx.body = res
},
}
3. router.js
将获取账号列表的接口绑定到路由。
......
let accountController = require("../controllers/account")
//账号
router.post("/account/listforwallet", accountController.accountListForWallet)
//页面
router.get("/account.html", webController.getAccountHtml)
4. account.html
在views文件夹下新建account.html文件,实现前端账号列表的页面。
<html>
<head>
<title>账号</title>
<script src="js/lib/jquery-3.3.1.min.js"></script>
<script src="/js/lib/jquery.url.js"></script>
<script src="js/account.js"></script>
<link rel="stylesheet" href="css/eoswallet.css">
</head>
<body>
<%include block/nav.html%>
<div id="main">
<h1></h1>
<div>
<a href="/accountnew.html" class="button">去新建账号</a>
</div>
<br>
<form id="account-import-form">
<button type="submit">导入账号</button>
<input type="text" name="wallet" hidden="hidden">
<input type="text" name="privatekey" placeholder="请输入账号私钥">
</form>
<table id="account-list-table">
</table>
</div>
</body>
</html>
5. account.js
在static/js文件夹下新建account.js文件,前端处理账号列表的网络请求与页面的渲染。
function accountInfo(acocunt) {
localStorage.setItem("currentAccount", acocunt)
window.location.href = "/accountinfo.html"
}
function goTransaction(account) {
localStorage.setItem("currentAccount", account)
window.location.href = "/transaction.html"
}
$(document).ready(function () {
let currentwallet = localStorage.getItem("currentwallet")
$("h1").text(currentwallet+" 钱包")
if (!currentwallet) {
return
}
let walletPassword = localStorage.getItem(currentwallet)
$("input[name=wallet][hidden=hidden]").val(currentwallet)
//获取账号列表
let params = {"wallet":currentwallet, "password":walletPassword}
$.post("/account/listforwallet", params, function (res, status) {
console.log(status + JSON.stringify(res))
if (res.code == 0) {
let accountTable = $("#account-list-table")
res.data.forEach(account => {
let accountTr = `
<tr>
<td>${account}</td>
<td><button onclick="accountInfo('${account}')">查看详情</button></td>
<td><button onclick="goTransaction('${account}')">去转账</button></td>
</tr>`
accountTable.append(accountTr)
});
sessionStorage.setItem(`wallet-${currentwallet}-accounts`, JSON.stringify(res.data))
}
})
//导入账户
$("#account-import-form").validate({
rules: {
privatekey: {
required: true,
},
},
messages: {
privatekey: {
required: "请输入要导入的账号的私钥",
},
},
submitHandler: function (form) {
$(form).ajaxSubmit({
url: "/wallet/importkey",
type: "post",
dataType: "json",
success: function (res, status) {
console.log(status + JSON.stringify(res))
alert(JSON.stringify(res.data))
if (res.code == 0) {
window.location.reload()
}
},
error: function (res, status) {
console.log(status + JSON.stringify(res))
}
});
}
})
})
三、项目源码一:新建账号
1. web.js
编辑controllers文件夹下的web.js文件,实现后端返回给前端创建账号的页面。
module.exports = {
......
getAccountCreateHtml:async(ctx) => {
await ctx.render("accountNew.html")
},
}
2. account.js
编辑controllers文件夹下的account.js文件,后端实现创建账号的功能。
module.exports = {
......
//创建账号
accountCreate: async (ctx) => {
console.log(JSON.stringify(ctx.request.body))
let {account, creator, wallet, password, activepubkey, ownerpubkey} = ctx.request.body
//1.获取钱包里面所有的私钥
let privatekeyList = []
let res = await httpRequest.postRequest(config.walletGetKeys, [wallet, password])
if (res.code == 0 && res.data.length > 0) {
for (const index in res.data) {
let keys = res.data[index]
privatekeyList.push(keys[1])
}
//2.设置创建账号默认的公钥
let defaultKey = res.data[0][0]
activepubkey = activepubkey || defaultKey
ownerpubkey = ownerpubkey || defaultKey
}
console.log("privatekeyList:", privatekeyList)
console.log("activepubkey:", activepubkey,"\n ownerpubkey:", ownerpubkey)
//3.配置EOSJS
eos = myUtils.getEOSJS(privatekeyList)
//4.交易(创建账号)
let data = await eos.transaction(tr => {
tr.newaccount({
creator: creator,
name: account,
owner: ownerpubkey,
active: activepubkey
})
tr.buyrambytes({
payer: creator,
receiver: account,
bytes: 8192
})
tr.delegatebw({
from: creator,
receiver: account,
stake_net_quantity: '10.0000 EOS',
stake_cpu_quantity: '10.0000 EOS',
transfer: 0
})
})
// console.log(JSON.stringify(data))
//5.返回给前端执行的状态
let resData
if (data) {
resData = success("创建账号成功")
} else {
resData = fail("创建账号失败")
}
ctx.body = resData
},
}
3. router.js
将账号模块所有功能的接口绑定到路由。
......
//账号
router.post("/account/create", accountController.accountCreate)
//页面
router.get("/accountnew.html", webController.getAccountCreateHtml)
4. accountNew.html
在views文件夹下新建accountNew.html文件,实现前端创建账号的页面。
<html>
<head>
<title>新建账号</title>
<script src="js/lib/jquery-3.3.1.min.js"></script>
<script src="/js/lib/jquery.url.js"></script>
<script src="js/accountNew.js"></script>
<link rel="stylesheet" href="css/eoswallet.css">
</head>
<body>
<%include block/nav.html%>
<div id="main">
<h1></h1>
<h2>新建账号</h2>
<form id="account-create-form">
<input type="text" name="account" placeholder="请输入账号名称">
<label>账号名称</label>
<br><br>
<select name="creator" id="account-create-creator-select">
</select>
<label>创建者名称</label>
<br><br>
<div>以下内容可以选择填写</div>
<button type="button" onclick="createKey()">新建公钥私钥</button>
<br>
<input type="text" name="activepubkey" placeholder="请输入active权限的公钥">
<label>active公钥</label>
<br>
<input type="text" name="ownerpubkey" placeholder="请输入owner权限的公钥">
<label>owner公钥</label>
<br><br>
<input type="text" name="wallet" hidden="hidden">
<input type="text" name="password" hidden="hidden">
<button type="submit">新建账号</button>
</form>
</div>
</body>
</html>
5. accountNew.js
在static/js文件夹下新建accountNew.js文件,前端处理新建账号的网络请求与页面的渲染。
function createKey() {
let currentwallet = localStorage.getItem("currentwallet")
let params = {"wallet": currentwallet, "type":"k1"}
$.post("/wallet/createkey", params, function (res, status) {
console.log(status, JSON.stringify(res))
alert(res.data)
})
}
$(document).ready(function () {
let currentwallet = localStorage.getItem("currentwallet")
$("h1").text(currentwallet+" 钱包")
if (!currentwallet) {
return
}
let walletPassword = localStorage.getItem(currentwallet)
$("input[name=wallet][hidden=hidden]").val(currentwallet)
$("input[name=password][hidden=hidden]").val(walletPassword)
//选择新建者账号列表
let accountList = sessionStorage.getItem(`wallet-${currentwallet}-accounts`)
accountList = JSON.parse(accountList)
console.log("accountList",accountList)
let accountSelectList = $("#account-create-creator-select")
for(let i = 0; accountList && i < accountList.length; i++) {
let account = accountList[i]
let accountOption = `<option value="${account}">${account}</option>`
accountSelectList.append(accountOption)
}
//新建账号
$("#account-create-form").validate({
rules: {
name: {required: true,},
creator: {required: true,},
},
messages: {
name: {required: "请输入要新建的账号名称",},
creator: {required: "该钱包没有可供新建账号的创者账号,可将该钱包的任意一个公钥发送给其它钱包新建该账号,或者导入其他账号的私钥到该钱包再进行新建账号"},
},
submitHandler: function (form) {
$(form).ajaxSubmit({
url: "/account/create",
type: "post",
dataType: "json",
success: function (res, status) {
console.log(status + JSON.stringify(res))
if (res.code == 0) {
alert("账号新建成功")
window.location.href = history.go(-1);
} else {
alert("账号新建失败")
}
},
error: function (res, status) {
console.log(status + JSON.stringify(res))
alert(res.data)
}
});
}
})
})
四、项目源码一:查看账号详情
1. web.js
编辑controllers文件夹下的web.js文件,实现后端返回给前端账号详情的页面。
module.exports = {
......
getAccountInfoHtml:async(ctx) => {
await ctx.render("accountInfo.html")
},
}
2. account.js
编辑controllers文件夹下的account.js文件,后端实现查看账号详情的功能。
module.exports = {
......
accountBalance: async (ctx) => {
let {code, account} = ctx.request.body
let params = {"code":code,"account":account}
let res = await httpRequest.postRequest(config.accountBalance, params)
let currencyList = []
if (res.code == 0) {
for (const index in res.data) {
let currency = res.data[index] //"9996.0000 EOS"
let currencys = currency.split(" ")//currencys[0]=9996.0000, currencys[1]=EOS
currencyList.push({
"symbol":currencys[1],
"amount":currencys[0]
})
}
}
res.data = currencyList
console.log("currencyList:", currencyList)
res.data = currencyList
ctx.body = res
},
accountInfo: async (ctx) =>{
let {account} = ctx.request.body
let res = await httpRequest.postRequest(config.accountInfo, {"account_name":account})
ctx.body = res
},
}
3. router.js
将查看账号详情的接口绑定到路由。
......
//账号
router.post("/account/balance", accountController.accountBalance)
router.post("/account/info", accountController.accountInfo)
//页面
router.get("/accountinfo.html", webController.getAccountInfoHtml)
4. accountInfo.html
在views文件夹下新建accountInfo.html文件,实现前端账号详情的页面。
<html>
<head>
<title>账号详情</title>
<script src="js/lib/jquery-3.3.1.min.js"></script>
<script src="/js/lib/jquery.url.js"></script>
<script src="js/accountInfo.js"></script>
<link rel="stylesheet" href="css/eoswallet.css">
</head>
<body>
<%include block/nav.html%>
<div id="main">
<h1></h1>
<table id="account-balance-table">
<tr>
<th>代币</th>
<th>金额</th>
</tr>
</table>
<table id="account-permission-table">
<tr>
<th>权限</th>
<th>阈值</th>
<th>公钥</th>
<th>私钥</th>
<th>权重</th>
</tr>
</table>
</div>
</body>
</html>
5. accountInfo.js
在static/js文件夹下新建accountInfo.js文件,前端处理查看账号详情的网络请求与页面的渲染。
function getAccountPermissionPrivateKey(publicKey) {
let currentwallet = localStorage.getItem("currentwallet")
let currentPassword = localStorage.getItem(currentwallet)
let params = {"wallet":currentwallet, "password":currentPassword, "publickey":publicKey}
console.log(params)
$.post("/wallet/privatekey", params, function (res, status) {
console.log(status, JSON.stringify(res))
alert(JSON.stringify(res.data))
})
}
$(document).ready(function () {
let currentAccount = localStorage.getItem("currentAccount")
$("h1").text(currentAccount+" 账号")
if (!currentAccount) {
return
}
//账号金额
let params = {"code":"eosio.token","account":currentAccount}
$.post("/account/balance", params, function (res, status) {
console.log(status + JSON.stringify(res))
//后端返回的数据结构如下
//[{"symbol":"EOS", "amout":100}, {"symbol":"SYS", "amount":200}]
if (res.code == 0) {
let balanceTable = $("#account-balance-table")
res.data.forEach(balanceData => {
let balanceTr = `<tr>
<td>${balanceData.symbol}</td>
<td>${balanceData.amount}</td>
</tr>`
balanceTable.append(balanceTr)
});
}
})
//账号权限详情
$.post("/account/info", {"account":currentAccount}, function (res, status) {
console.log(status + JSON.stringify(res))
if (res.code == 0) {
let permissionTable = $("#account-permission-table")
for (const index in res.data.permissions) {
let permission = res.data.permissions[index]
let publicKey = permission.required_auth.keys[0].key
let rowTr = `<tr>
<td>${permission.perm_name}</td>
<td>${permission.required_auth.threshold}</td>
<td>${publicKey}</td>
<td><button onclick="getAccountPermissionPrivateKey('${publicKey}')">点击查看</button></td>
<td>${permission.required_auth.keys[0].weight}</td>
</tr>`
permissionTable.append(rowTr)
for(let i = 1; i < permission.required_auth.keys.length; i++) {
let keyData = permission.required_auth.keys[i]
let rowTr = `<tr>
<td></td>
<td></td>
<td>${keyData.key}</td>
<td><button onclick="getAccountPermissionPrivateKey('${keyData.key}')">点击查看</button></td>
<td>${keyData.weight}</td>
</tr>`
permissionTable.append(rowTr)
};
};
}
})
})
五、项目运行效果
- 获取钱包mywallet1的账号列表如下
- 新建账号的效果如下在mywallet1钱包中使用账号lixu新建账号account1,控制的权限的两个公钥都是一样的,对应的私钥并且在mywallet2钱包中的,所以创建后将会在mywallet2钱包中看到该账号。
- 查看账号详情我们查看使用cleos配置的多个主体共同管理的账号lixu,为了简化前端的代码量,只显示了主体是公钥的数据,并没有显示主体是账号的数据,其中的一个公私钥主体无权获取私钥,因为它没有到当前钱包中。
项目源码Github地址
版权声明:博客中的文章版权归博主所有,未经授权禁止转载,转载请联系作者取得同意并注明出处。
未经授权禁止转载、改编,转载请注明出处!