Akawa

ETY001的博客

最近币圈的一个事情闹的动静还是不小的,就是Tron收购Steemit这个事情。这个事情现在还没有结束,我觉得还是要引起想在区块链这个领域做事情的公司关注的。

对于这个事情的大概前因后果,在这篇大佬写的文章里介绍了,这里就不再细说。大概就是Steemit公司自己经营问题导致的一些历史遗留问题,在Tron收购的时候,Ned(Steemit公司CEO)并没有交代清楚,导致Tron的老板孙宇晨在做一些宣传事项的时候,触动了Steem社区的利益集团的神经,因为沟通不是很有效,社区直接封锁了原Steemit公司的账号的资金使用权利,孙老板联合交易所又来了一个猛烈的反击,最终导致对战开始。

这次的事件,我觉得对于想要基于区块链进行创业的团队来说,很具有教科书式的启发。那就是在区块链上,如果引入了社区治理(比如像DPOS这样的),如何才能划清楚公司和社区的责任和财产,如何才能保护好自己?

在现实生活中,国家都有相对比较完备的民事法律体系,来帮助有争端的人来解决争端。那么在区块链世界,用什么来保护呢?

我曾在社区跟另外一个人讨论过,关于口头承诺在区块链世界到底作不作数的问题。我个人倾向于这种东西是不作数的。区块链从比特币开始,我觉得它的本质就是要用客观的方法去解决客观的问题,不要在客观问题的执行上,让人参与。

那么什么是客观的方法呢?代码!代码就是区块链的一切,代码就是区块链里的法律。只有在代码产生前的所有讨论,才是人可以去主观影响的。一旦多数人达成了共识,就应该用代码的方式去落实共识。当遇到纠纷的时候,就是要靠已执行在链上的代码,来保证所有人的权益。这才应该是区块链的意义。

就像这次的事件,核心就是币权归属的问题。Ned在跟社区协商中,对于这些资产达成了共识,但是仅是发布视频和文件在区块链上而已,而实际的币权操作还是他自己说了算的,并没有代码去限制。

也就说,Ned完全可以撕破脸去做他想做的事情,最多就是社区声讨吧。但是对于一个可以最后撕破脸的人来说,你觉得他会怕声讨吗?不过Ned并没有去撕破脸,而是成功套现把锅甩给了之前不明真相的孙老板。

如果一开始,共识达成后,就直接落实为代码,就不会有现在这难解的扣了。

所以说,想要在区块链方向上创业的公司,请一定要把该做的事情做好,哪怕那个时候做起来觉得繁琐没必要,也一定要做。多一份保障就是对自己财产多一份保障。

下面是我针对这次事件的其他吐槽(先声明我不是给孙老板站队)。

这个资产问题在Steem链正式运营的时候就存在,到现在至少两年的时间了,社区的领袖们(头部见证人们)都去做什么了,就跟隐形了一样。为什么在孙老板收购Steemit后,你们却都跳出来了,还直接就封锁了Steemit公司账号的币权?

假如说社区的人多数都认可Ned发表的口头承诺,所以没有用代码限制公司的账号币权,那么为什么孙老板收购Steemit公司后,你们就不再认可Ned的口头承诺了呢?因为这个承诺理论上讲是跟着收购这个事情,一并都算到了新的老板头上的。是不是有点双标了?虽然人都有双标的特性,但是目前的这种双标真的是对整个社区负责吗?我看其实是在对你们自己的自身利益负责吧?

再要说的就是区块链的制度之争。我想说的是,没有什么制度是完美的,关键还是要看参与制度的人。这句话什么意思?就是说,没有绝对的去中心化,只有相对的去中心化。即使是POW制度下的公链,只要钱多,依然可以中心化,所以关键还是要看公链的治理团队的水平。

最后,就是我觉得,如果在整个收购过程中,Ned并没有写明这些细节,比如之前做过的承诺之类的,其实孙老板可以去起诉Ned了。花了泡澡堂子的钱,结果进门却被强制只能泡个脚,这买卖肯定是不能这么干,这瘪肯定不会就这么吃的。我觉得这应该是孙老板联合交易所发起反击前的内心写照了吧。

年前重新开发了我的浏览器扩展“温故知新”。“温故知新”这个扩展的目的就是在你每次打开新网页或者标签页的时候,能够随机从你的收藏夹/书签栏里抽一个出来展示。这样利用了零碎的时间,就把你的书签栏整理了,对于书签数量巨大的用户来说,非常赞。

这次重新开发,除了解决老版本中的问题,提升操作体验,增加用户要求的新功能外,最重要的就是优化了数据统计功能。

之前由于对 Google Analytics 不熟悉,所以在事件埋点方面,设置的参照坐标很乱,导致在面板看到的数据意义不大。

这次优化后,只关注几个重要指标,比如获取书签操作、删除书签操作等。下面是2020年2月20日到2月26日的数据报表截图:

photo_2020-02-27_12-08-23.jpg

图中 getbookmark_from_mini 和 getbookmark_from_full 就是获取书签的操作,remove_bookmark 就是删除书签的操作。“事件总数”就是事件发生的次数,“唯一身份事件数”可以看做是用户数。

可以看到在过去的7天,“温故知新”帮助了 285 + 127 位用户,重新回顾了 8888 + 448 个书签,并且有 38 个用户,删除了 95 个对他们来说没用的书签。

我觉得这应该算是一个让我感到振奋的事情!

在这个版本之前,我只能看到有多少用户在用我开发的扩展,而并不能了解到我可以帮助到他们多少?现在有了这些新的指标后,我能够看到我对于新版本付出的努力没有白费。

抛开数字统计,我觉得还有一个很重要的点,那就是我在开发工具时的思路也发生了变化。

在过去,我开发的目的就是玩或者解决自己的问题,但是现在我觉得开发出来的东西帮助到更多人,才是有意思的事情。尽管很多开发者,一起步的时候,就做到了这一点,不过我现在能意识到这一点也不算晚。

总结一下,数字可以帮助开发者量化自己的劳动成果。开发者需要收集各种信息,来看看可以帮助用户再做些什么。

PS:点击【这里】,可以下载安装我的这个扩展,也可以去吐槽这个扩展。

最近为了把新版的**温故知新**扩展上传到各个浏览器,真的是操碎了心了。这篇文章就来说说在通过火狐审核的时候的遇到的最棘手的问题。

由于我的扩展使用了 webpack,代码 build 后,没有可读性,所以火狐要求需要上传源代码,然后会进行审查。审查的步骤就是根据我提供的源代码和编译方法,审核人员编译一次,然后把编译后的代码打包,最后与我上传的压缩包比对,看是否一致。负责审查我的那位审查员,用的是 Beyond Compare 这个软件来检查两个压缩包是否一样。

最开始并没有注意到这里,审查员说他的编译结果跟我的不一样,我以为是我的开发环境对打包环境有污染。所以再次送审的时候,我是直接从 github 上下载的源码,重新来了一遍。结果审查结果依然是不一致。

于是我在本地进行测试,发现即使我在自己本地,两次编译后,用zip打包后的文件 md5 sum 都不一样。

WTF!

经过各种查询,最后确定应该是文件的 metainfo 导致的,看了下 zip 命令的参数,发现 -X 可以移除 metainfo 来打包,于是使用命令 zip -r -X extension.zip dist/ 打包,对编译后的 dist 目录打包了两次,对比了下,发现终于特么的一致了。

既然一致了,就赶紧更新审核包和审核文案吧。就在更新过程中,我突然想到,我应该按照审核员的步骤再操作一遍,于是我又从下载源码开始来了一遍,最后惊奇的发现,这一遍生成的压缩包和上一遍的压缩包 md5 sum 又特么的不一样啊!!!!

经过各种试验后,排除了大部分的可能,最后猜测应该是文件日期导致的,毕竟前后两遍编译,经过各种排除后,只有时间这个变量了。

于是我用 find dist | xargs touch -mt 202002110000 先对编译结果强制修改文件的时间,然后再打压缩包,然后再从源码来一遍后,也修改成这个时间,再打包,最后对比两个压缩包的 md5 sum,终于一样了!!!!

最后提交审核,两天后,终于过审了!!!!!

对于书签巨多的人来说,我这个插件值得你去体验下~~~

Chrome版地址https://chrome.google.com/webstore/detail/review-bookmarks/oacajkekkegmjcnccaeijghfodogjnom

Firefox版地址https://addons.mozilla.org/zh-CN/firefox/addon/review-bookmarks/

Microsoft Edge版地址https://microsoftedge.microsoft.com/addons/detail/pibjmfgfgamgohlaehhhbdkjboaopjkj

反馈https://creatorsdaily.com/9999e88d-0b00-46dc-8ff1-e1d311695324?utm_source=vote

欢迎使用,欢迎点赞!!

最近在做浏览器扩展《温故知新》的新版本。其中,最让我头疼的就是用 Google Analytics 统计信息了。

Google 官方提供的 SDK 使用的话,需要外部引入 SDK,并且配置 CSP,而 Firefox 浏览器不允许配置 CSP

无奈,只能自己去写一个简单的封装了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
export class GA {
constructor(ua, cid, debug = false) {
this.ua = ua;
this.cid = cid; // client id
this.gaApi = debug ? 'https://www.google-analytics.com/debug/collect' : 'https://www.google-analytics.com/collect';
this.version = '1';
}
ga(t, ...items) {
let payload = `v=${this.version}&tid=${this.ua}&cid=${this.cid}`;
let params = [];
switch (t) {
case 'pageview': // Pageview hit type
// dh -- Document hostname
// dp -- Page
// dt -- Title
params = ['dh', 'dp', 'dt'];
break;
case 'event':
// ec -- Event Category. Required
// ea -- Event Action. Required
// el -- Event label.
// ev -- Event value.
params = ['ec', 'ea', 'el', 'ev'];
}
if (params === []) return;
payload = `${payload}&t=${t}`;
items.forEach((v, i) => {
payload = `${payload}&${params[i]}=${encodeURIComponent(v)}`;
});
const request = new XMLHttpRequest();
request.open('POST', this.gaApi, true);
request.send(payload);
}
}

const uid = 'xxxx-xxxx-xxx-xxx';
const debug = false;
const gaID = 'UA-xxxxxx-x';
const gaObj = new GA(gaID, uid, debug);
function sendEvent(eventCategory, eventAction, eventLabel = '', eventValue = 1) {
if (store.getters.config.ga === false) return;
gaObj.ga('event', eventCategory, eventAction, eventLabel, eventValue);
}
// dh -- Document hostname, dp -- Page, dt -- Title
function sendPageview(dp, dh = '', dt = '') {
if (store.getters.config.ga === false) return;
gaObj.ga('pageview', dh, dp, dt);
}

这就是我根据官方文档简单写的封装。

这里面需要注意几个问题。

一个是正式环境的地址是 /collect 而测试地址是 /debug/collect。这个在之前的时候,没有注意到还有测试地址,所以绕了弯路,也没有发现提交的参数错误。

另外一个就是在 event 类型中,Event value 必须是整型。

还有个小技巧,就是调试的时候,可以切换到“实时”选项卡,在那下面可以看到发送到 /collect 的实时数据。

我的Chrome扩展重构进度已经60%了,目前又遇到了新问题。

这个问题的缘由得慢慢说来。

我的扩展由于需要用自定义的页面替换新标签页。在我的早期版本的实现是这样的:

1
2
3
4
5
chrome.tabs.onCreated.addListener(function(tab){
if(Mini.get_status()=='off'&&(tab.url=="chrome://newtab/"||tab.url=="chrome://newtab")){
chrome.tabs.update(tab.id, {url:chrome.runtime.getURL('show.html')});
}
});

也就是新建标签页的时候,用我自己的页面 URL 替换掉 Chrome 默认页面。

后来,Chrome 在某个版本后,新建标签页后,地址栏是空的,也就是 tab.url 是没有值,这就导致我之前的代码就失效了。

查看文档,发现正确的方法是在 manifest.json 中增加 chrome_url_overrides 这个选项,具体可以查看这里

这就引入了新的问题。

我的扩展里原来是有一个开关的,这个开关可以控制新标签页是否显示我的自定义页面,也就是上面代码里的那个 Mini.get_status() == 'off'

现在用了 manifest.json 直接替换掉了默认页,而 manifest.json 是无法在扩展里修改的,同时 Chrome 没有提供相关的 API,那么如何实现这个开关就很尴尬了。

这个 BUG 我一直放着没有处理。

这次重构进行到了自定义页面重构后,这个问题就必须要解决了。

最终的解决方案是,监听页面更新,如果 URL 符合 chrome://newtab/,那么就替换成访问 chrome-search://local-ntp/local-ntp.html,代码如下:

1
2
3
4
5
6
7
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (tab.url === 'chrome://newtab/') {
if (store.getters.config.mini === false) {
chrome.tabs.update(tabId, { url: 'chrome-search://local-ntp/local-ntp.html' });
}
}
});

之所以用 chrome.tabs.onUpdated,有两个原因:一个是因为 onCreated 拿不到 URL,一个是在当前标签页,如果点击主页按钮的时候,也会进入 chrome://newtab/

这次重构我的Chrome扩展由于引入了很多新的东西,所以遇到的小问题还是挺多的。

比如在 content script 模式中,我的UI样式会被某些网站的 CSS 给影响到,以至于我的插件的 UI 显示不是预期。

在开发文档中,对于 manifest.json 中的 content_scriptsCSS 描述是: Optional. The list of CSS files to be injected into matching pages. These are injected in the order they appear in this array, before any DOM is constructed or displayed for the page.

这就是为啥 Tab 中网页可能会影响我的插件 UI 的原因。

因为我的插件的 CSS 早于 Tab 中网页的载入,这样 Tab 中网页里的 CSS 里面如果有重名的 Class 或者直接对 HTML 标签加样式,就会影响到我。

之前的版本,因为我是自己手写样式,且都用的 div 标签,所以很大程度上避免了冲突。

而这次重构引入了饿了么的 UI,所以增大了冲突的概率。我在 content script 中用的是 notification 这个组件,里面涉及到了 <h2 /><i />这两个标签。有些网站是直接对这两个标签先进行样式全局设置的,这就导致在访问这些网站的时候,我的 UI 会受到影响。

目前我采用的解决方案是,把饿了么的 UI 库克隆到我的账号下,然后自己修改里面的代码,把 <h2 /><i /> 替换成别的标签,重新打包,引入我自己定义的库后,问题得到解决。

《Chrome扩展中使用Vuejs》 文章中,我们提到过 @font-face 我是用的 CDN 的方式来搞定的。但是用 CDN 的缺点就是网络不畅的时候,扩展中使用 @font-face 的地方显示就是个方块了。

为了解决这个问题,我搜索了下,找到了解决方案。

这里面有几个要点,一个是字体文件可以在任意页面访问到,一个是如何获取到扩展ID。

正常情况下,扩展的资源文件是与所有网页隔离开的,扩展相当于是在一个沙盒里面运行,如果想要在 Content Scripts 模式下,让 JS 或者 CSS 文件访问到扩展中的资源文件,那么就需要在 manifest.json 中增加配置项 web_accessible_resources

1
"web_accessible_resources": ["fonts/*"]

就像上面这样增加配置后,我们就可以在 Content Scripts 模式下访问到 fonts/ 目录下的所有资源文件了。

再来说下第二个问题。

我们知道扩展的地址结构是 chrome://[扩展ID],那么我们的 CSS 文件中的 @font-face 的路径中的扩展ID该怎么获取呢?

通过看文档,找到了预设值 __MSG_@@extension_id__,这是出处:https://developer.chrome.com/extensions/i18n#overview-predefined

那么我们去修改下 element-variables.scss 中的 font-path 像这样:

1
$--font-path: 'chrome-extension://__MSG_@@extension_id__/fonts';

这样,最关键的两个问题就解决了,最后只要在 webpack.config.js 中需要复制的操作里,增加复制字体文件的操作即可,就像这样:

1
2
3
4
5
new CopyPlugin([
......
{ from: '../node_modules/element-ui/lib/theme-chalk/fonts', to: 'fonts' },
......
]),

通过这样设置后,我们通过扩展插入到网页中的 CSS 文件就能直接访问到扩展中的资源文件了。

开发的扩展四年多了,这两天终于突破了500用户数。很早就想要再进行开发了,但是无奈由于之前代码写的比较乱,并且用的 jQuery 去操作,很多东西开发起来还是很费劲的。

现在用户数已经 500 个用户了,之前用户呼声很高的功能,得花点心思搞一下了,要不就对不起这些铁杆用户了!

考虑再三,还是要重构。

jQuery 在做一些交互少的功能的时候,还是很不错的选择。不过考虑到接下来要开发的功能的交互的复杂度,我觉得还是要引入 Vuejs 或者 React。鉴于熟练程度,我最终选择了 Vuejs

由于我的 Chrome 扩展中将会使用 Content ScriptPopup PageTab Page这三种形式,这就意味着,如果我要使用 Vuejs 的话,那么我需要用 Webpack 配置三个入口和三个文件。

不过让我修改 Webpack 配置还好,自己写,真的是太蛋疼了。于是,我找到了这个,https://github.com/Kocal/vue-web-extension

这个 Vue 模板真的是太好用了,基本上把大部分的工作都做好了。

我们只需要按照 Kocal/vue-web-extension 库的文档操作,就可以完成基本的部署。

不过没有 Content Script 的支持,我们只需要自己在 src 目录下创建个新的目录,比如叫做 content-script

然后创建一个入口文件 content-script.js,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Vue from 'vue';
import App from './App';
import store from '../store';

global.browser = require('webextension-polyfill');

Vue.prototype.$browser = global.browser;
Vue.use(ElementUI);

/* eslint-disable no-new */
new Vue({
el: '#review-bookmark',
store,
render: h => h(App),
});

再创建一个 App.vue 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<button>测试一下</button>
</div>
</template>

<script>
export default {
data() {
return {};
},
};
</script>

<style lang="scss" scoped></style>

最后创建一个用于初始化的文件 cs-init.js,内容如下:

1
2
3
const reviewBookmarkDiv = document.createElement('div');
reviewBookmarkDiv.id = 'review-bookmark';
document.body.appendChild(reviewBookmarkDiv);

创建好这三个文件后,再打开 webpack.config.js 文件,修改 entry 项如下:

1
2
3
4
5
6
entry: {
background: './background.js',
'popup/popup': './popup/popup.js',
'tab/tab': './tab/tab.js',
'content-script/content-script': './content-script/content-script.js',
}

这样就设置好了多个编译入口,最后编译的时候就会在 dist 文件夹下生成四个编译好的 js 文件。

再修改 webpack.config.jsplugins 项中的 CopyPlugin 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new CopyPlugin([
{ from: 'icons', to: 'icons' },
{ from: '_locales', to: '_locales' },
{ from: 'content-script/cs-init.js', to: 'content-script/cs-init.js' },
{ from: 'popup/popup.html', to: 'popup/popup.html', transform: transformHtml },
{ from: 'tab/tab.html', to: 'tab/tab.html', transform: transformHtml },
{
from: 'manifest.json',
to: 'manifest.json',
transform: content => {
const jsonContent = JSON.parse(content);
jsonContent.version = version;

if (config.mode === 'development') {
jsonContent['content_security_policy'] = "script-src 'self' 'unsafe-eval'; object-src 'self'";
}

return JSON.stringify(jsonContent, null, 2);
},
},
])

这样我们就把扩展需要的必要文件都复制进了 dist 目录。尤其是 cs-init.js。这个文件在下面会讲。

现在打开 manifest.json 文件,调整如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
"name": "__MSG_appname__",
"description": "__MSG_appdesc__",
"version": null,
"manifest_version": 2,
"default_locale": "en",
"author": "ETY001",
"homepage_url": "https://bm.to0l.cn/",
"chrome_url_overrides": {
"newtab": "tab/tab.html"
},
"icons": {
"16": "icons/icon-16.png",
"19": "icons/icon-19.png",
"38": "icons/icon-38.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
"browser_action": {
"default_title": "__MSG_appname__",
"default_popup": "popup/popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["*://*/*"],
"css": ["content-script/content-script.css"],
"js": ["content-script/cs-init.js", "content-script/content-script.js"],
"run_at": "document_end"
}
],
"web_accessible_resources": ["tab/tab.html"],
"permissions": ["activeTab", "notifications", "bookmarks", "tabs", "background", "https://www.google-analytics.com/", "storage"]
}

好了所有的文件都准备好了,接一下原理。

通过看 manifest.jsoncontent_scripts,可以看到我们引入了 cs-init.jscontent-script.js。其中 cs-init.js 文件的作用就是为了能在页面里面插入一个 <div /> 作为模板的插入点,然后 content-script.js 就可以把相关的页面和逻辑就能插入进我们准备好的 <div /> 中去了。

除了 Content Scripts 这个坑需要自己填以外,还有一个字体库的坑也需要自己填一下。

由于我还使用了 Element UI 库,但是在测试的时候,发现 font icon 的库一直引入不了。最后,我用的 cdn 上的字体资源。

具体方法在 《在项目中改变 SCSS 变量》这个文档中可以看到,只需要修改 font-path 这个参数即可,如下:

1
2
3
4
5
6
7
/* 改变主题色变量 */
$--color-primary: teal;

/* 改变 icon 字体路径变量,必需 */
$--font-path: 'https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.13.0/theme-chalk/fonts';

@import '~element-ui/packages/theme-chalk/src/index';

OK,花了两天的时间,把基础框架搭建好了!接下来就可以开始重构工作了,剩下的相对就简单了。

前言

《快速搭建私有单节点 Bitshares Testnet(一)》 中,讲解了如何搭建一个私有测试网络,这一篇将会讲解如何部署 Web 界面用于注册新用户,以及向新用户转账测试币。

部署

拉取代码

1
git clone https://github.com/ety001/bitshares-testnet-for-dapp-developers.git

编译镜像

1
2
cd bitshares-testnet-for-dapp-developers
docker build -t btfdd:latest .

你也可以直接使用我编译好的镜像,docker pull ety001/btfdd:latest

运行

1
2
3
4
5
6
7
8
9
docker run -itd \
--restart always \
--name btfdd \
-p 3000:3000 \
-e PRIV_KEY=5Jxxxxxx \
-e API_URL=ws://192.168.0.10/ws \
-e CHAIN_ID=2d20869f3d925cdeb57da14dec65bbc18261f38db0ac2197327fc3414585b0c5 \
-e CORE_ASSET=TEST \
ety001/btfdd:latest

环境变量说明

  • PRIV_KEY 是你用来作为水龙头的账号的 active 私钥
  • API_URL 是你私有测试网络的地址。这里需要注意下地址是否可访问。
  • CHAIN_ID 是你私有测试网络的 CHAIN_ID
  • CORE_ASSET 是你私有网络的测试币名

访问

如果一切正常,这个时候浏览器访问 http://localhost:3000 即可看到工具页面了。

目前工具只提供创建账号和转账的功能。

公共服务

目前我也搭建了公共服务免费供大家使用,地址: https://testnet.61bts.com

疑问

如果有问题可以给我发邮件 work#akawa.ink 或者提交 issue

整理了一下用 js-sdk 创建新用户的最简单的 Demo 实现代码。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import {PrivateKey, key, FetchChain, TransactionBuilder} from 'bitsharesjs';
import {Apis, ChainConfig} from 'bitsharesjs-ws';

generateKeyFromPassword(accountName, role, password) {
const seed = accountName + role + password;
const privKey = PrivateKey.fromSeed(seed);
const pubKey = privKey.toPublicKey().toString();
return {privKey, pubKey};
}

registerUser(username, password, registrar, referrer) {
const privKey = '这里是用来签名数据的用户active私钥';
const pKey = PrivateKey.fromWif(this.privKey);
const referrerPercent = 0;
const {pubKey: ownerPubkey} = generateKeyFromPassword(
username,
'owner',
password
);
const {pubKey: activePubkey} = generateKeyFromPassword(
username,
'active',
password
);
const {pubKey: memoPubkey} = generateKeyFromPassword(
username,
'memo',
password
);

try {
return Promise.all([
FetchChain("getAccount", registrar),
FetchChain("getAccount", referrer)
]).then((res) => {
const [chainRegistrar, chainReferrer] = res;
const tr = new TransactionBuilder();
tr.add_type_operation("account_create", {
fee: {
amount: 0,
asset_id: 0
},
registrar: chainRegistrar.get("id"),
referrer: chainReferrer.get("id"),
referrer_percent: referrerPercent,
name: username,
owner: {
weight_threshold: 1,
account_auths: [],
key_auths: [[ownerPubkey, 1]],
address_auths: []
},
active: {
weight_threshold: 1,
account_auths: [],
key_auths: [[activePubkey, 1]],
address_auths: []
},
options: {
memo_key: memoPubkey,
voting_account: "1.2.1",
num_witness: 0,
num_committee: 0,
votes: []
}
});
return tr.set_required_fees().then(() => {
tr.add_signer(pKey);
console.log("serialized transaction:", tr.serialize());
tr.broadcast();
return true;
});
}).catch((err) => {
console.log('err:', err);
});
} catch(e) {
console.log('unexpected_error:', e);
}
}

registerUser('新用户名', '新用户的密码', '用来签名的用户的用户名', '推荐用户的用户名');

说明

  • registerUser 函数参数中的 registrar 是用来签发数据的用户的用户名,函数里面的 privKeyregistraractive 私钥
  • referrerPercent 是分成比例,这里注意是基于手续费的 50% 再分成。也就是 referrerPercent 设置为 10000 ,则代表 registrar 分成 0%,referrer 分成 50%。
  • 提交的数据中的 voting_account 是设置投票代理人是谁。
0%