Ultrain准入主网发布,已对接多个行业企业!
R

亲爱的Ultrain社区成员,

你们好!Ultrain团队今天很荣幸地宣布,我们将于2019.1.1.正式开启Ultrain准入制公网的对外商用服务。

Ultrain准入制主网于2018.10.1.进入公测阶段,已完成出块743,248个,其上部署了2个DApp应用的共计5个智能合约,交易峰值达到345TPS,运行时间86天,充分验证了Ultrain网络的可靠性、可用性和高性能。

本次我们对准入制主网进行了如下功能升级:

优化共识机制:我们在今年7月已经在亚马逊云上1000个节点的环境下达到了3000TPS的优异性能,而本次通过对共识机制中随机算法部分的优化和创新,我们成功地在公开互联网环境下将TPS稳定到1000TPS以上(即可以承载8000万日活的应用),并有效地提升了网络服务的稳定性和可用性,作为提供商业服务的准入制主网,我们承诺Ultrain性能最低1000TPS,主网的服务可用性达到99.99%; 经济模型机制:本次升级我们已经完成了Ultrain经济体系代码的更新,具体经济体系介绍可见该链接;通过本次升级,DApp开发者已经可以通过选择“专属侧链”或“按需定制侧链”的方式申请购买Ultrain信任计算服务;同时系统也已经具备了接入Ultrain矿机进行挖矿的能力;

Ultrain准入制主网已经完成了对外商用服务的准备,同时,我们已经与包括电魂区块链实验室,幂玛科技,Real Event limited,君戈网络,宏日新能源等多家行业内知名公司签订了战略合作协议,在未来的数月内将陆续有一系列商业DApp采用Ultrain准入制主网的信任计算服务,落地到Ultrain链上为用户提供服务。

现有的Ultrain准入制主网中提供算力服务的矿机有80台,主要由Ultrain及数家合作伙伴共同提供,未来随着越来越多的企业需要购买Ultrain信任计算服务,我们需要更多的矿机加入到Ultrain网络中提供算力服务,届时我们将会开放矿机购买,期待更多的矿工加入到Ultrain商业生态中,共同为企业提供稳定高效的信任计算服务。

Ultrain商用主网资源套餐申请服务也已上线,用户可通过此链接https://explorer.ultrain.io/account-recharge免费申请测试网资源套餐体验。

read more
怎样在合约中调用其它合约中定义的action
F
Action.requireRecepient、Action.sendInline和Transaction.send

在Ultrain的合约体系中,我们是没有办法将其它合约的代码嵌入到当前代码中来执行的,但是这并不意味不能执行其它合约中的代码。我们提供了三个方法,允许你从自己的合约中调用其它合约的代码:Action.requireRecepient()Action.sendInline()Transaction.send()。这三个方法可以提供不同的调用其它合约的方法。

Action.requireRecepient

从这个方法的名字中我们也可以看出来,这是一个通知。它的原型是
Action.requireRecepient(to: account_name): void
这个方法被调用时,to合约上部署的同名方法将被调用。假如我们编写了含有以下方法的两个合约:
合约1:

//... @action recepient(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call recepient with parameter: ").s(name).flush(); Action.requireRecipient(NAME("jack")); }

我们将这个合约部署到帐号rose上。

合约2:

//... @action recepient(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", recepient was called with parameter: ").s(name).flush(); }

这个合约部署到了jack上。

这样我们就可以发起一笔交易来测试一下:

clultrain push action rose recepient '["messi"]' -p rose

如果一切正常的话,会产生以下输出

executed transaction: 6d2cfdd6fa9de76f00b7f8a46eeafa7acef955f8890e5976ab4a42e0ac31ae8d 112 bytes 848 us # rose <= rose::recepient {"name":"messi"} >> hi, it is rose, I will call recepient with parameter: messi # jack <= rose::recepient {"name":"messi"} >> hi, it is jack, recepient was called with parameter: messi

从上面的小示例我们可以看到以下几个事实:

jack的++同名方法recepient++也被调用了。 传递的参数和交易发起时的参数messi是一致,不需要明确的传递这个参数。 rose和jack的recepient方法都在同一个transaction里被执行了。 rose和jack的recepient方法都具有rose的权限。 Action.sendInline

从Action.requireRecepient()的测试结果中,我们看到,requireRecepient()方法只能用相同的参数调用同名方法,这个在很多时候是很受限制的。所以,我们需要另一种方式,能够调用任意的方法。
这个新的方法就是Action.sendInline(),它允许我们调用我们想调用的任意方法。同样的,我们用例子来说明。

rose帐号的合约:

//... @action inline(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call sendInline with parameter: ").s(name).flush(); let pl = new PermissionLevel(this.receiver, NAME("active")); let params = new Parameters(); params.name = "messi"; Action.sendInline([pl], NAME("jack"), NEX("onInline"), params); } //...

jack帐号的合约:

//... @action onInline(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", onInline was called with parameter: ").s(name).flush(); } //...

代码编译完之后,分别将它们部署到rose和jack帐号上。然后执行下面的命令:
clultrain push action rose inline '["cr7"]' -p rose
成功执行之后,将会产生以下输出:

executed transaction: 6a65a2ee39d35e469d2ea21e36665547090d2ee9795994511d3e17d48b500131 112 bytes 821 us # rose <= rose::inline {"name":"cr7"} >> hi, it is rose, I will call sendInline with parameter: cr7 # jack <= jack::onInline {"name":"messi"} >> hi, it is jack, onInline was called with parameter: messi

我们可以得到以下事实:

rose的合约中可以调用jack任意的方法。 调用方法时,可以传递任意参数。 jack中的方法被调用时,具有发起时一样的权限(rose)。 交易在同一个transaction中被执行。 Transaction.send

前面我们介绍了Action.requireRecepient()和Action.sendInline()的使用方法和它们的特点,其中一条就是它们都在++同一个transaction中++被执行,这也就意味着,整个执行链条上如果有一个action失败了,那么整个transaction也就失败了。有些情况下,我们并不想所有的actions作为一个事务处理,这时候我们就需要Transaction.send()。
我们来演示一下这个方法是怎么使用的。

rose的合约:

//... @action deferred(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call Tx.send deferred with parameter: ").s(name).flush(); let p = new Parameters(); p.name = name; let act = new ActionImpl(); act.account = NAME("jack"); act.name = NEX("onDeferred"); act.data = SerializableToArray(p); act.authorization.push(new PermissionLevel(this.receiver, NAME("active"))); let tx = new Transaction(0); tx.actions.push(act); tx.header.delay_sec = 5; tx.send(1111, this.receiver, false); } //...

jack的合约:

//... @action onDeferred(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", onDeferred was called with parameter: ").s(name).flush(); } //...

把合约部署到链上之后,我们执行一下rose的deferred方法(需要将rose的active权限代理给utrio.code,否则这个方法执行时会失败,设置代理的命令参考
clultrain set account permission rose active '{"threshold": 1,"keys": [{"key":"pubkey_of_rose","weight": 1}],"accounts": [{"permission":{"actor":"rose","permission":"utrio.code"},"weight":1}]}' owner -p rose):
clultrain push action rose deferred '["henry"]' -p rose )

这时候我们发现产生的返回信息:

executed transaction: 386b8c647cf6812586e7d7c2a711482eb5b9b25851f78d2a608932ffc513ffe5 152 bytes 1522 us # rose <= rose::deferred {"name":"henry"} >> hi, it is rose, I will call Tx.send deferred with parameter: henry

这里并没有jack合约中打印的log信息啊,log到哪里去了呢?如果我们可以看到节点的log的话,会发现有这样的log:

[(jack,onDeferred)->jack]: CONSOLE OUTPUT BEGIN ===================== hi, it is jack, onDeferred was called with parameter: henry [(jack,onDeferred)->jack]: CONSOLE OUTPUT END =====================

这也说明,这个action后面被执行了。
Transaction.send具有以下特性:

Transaction.send()可以调用jack任意的方法。 调用方法时,可以传递任意参数。 jack中的方法被调用时,具有发起时一样的权限(rose)。 交易在不同的transaction中被执行。 总结

通过上面三个方法的执行结果对比,我们可以做一个关于它们的小总结:

方法 调用对方的方法 参数 权限 是否事务性质 Action.requireRecepient 同名方法 相同参数 和发起方权限一致 是的 Action.sendInline 任意方法 任意参数 和发起方权限一致 是的 Transaction.send 任意方法 任意参数 和发起方权限一致 不是 源码

上面我们提供的是代码片断,下面我们附上完整的源码,供大家参考。

rose合约的源码:

import "allocator/arena"; import { Log } from "../../../src/log"; import { Contract } from "../../../src/contract"; import { NAME, RNAME } from "../../../src/account"; import { Action, ActionImpl, SerializableToArray } from "../../../src/action"; import { PermissionLevel } from "../../../src/permission-level"; import { NEX } from "../../../lib/name_ex"; import { Transaction, OnErrorValue } from "../../../src/transaction"; class Parameters implements Serializable { name: string; } class SourceContract extends Contract { @action recepient(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call recepient with parameter: ").s(name).flush(); Action.requireRecipient(NAME("jack")); } @action inline(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call sendInline with parameter: ").s(name).flush(); let pl = new PermissionLevel(this.receiver, NAME("active")); let params = new Parameters(); params.name = "messi"; Action.sendInline([pl], NAME("jack"), NEX("onInline"), params); } @action deferred(name: string): void { Log.s("hi, it is ").s(RNAME(this.receiver)).s(", I will call Tx.send deferred with parameter: ").s(name).flush(); let p = new Parameters(); p.name = name; let act = new ActionImpl(); act.account = NAME("jack"); act.name = NEX("onDeferred"); act.data = SerializableToArray(p); act.authorization.push(new PermissionLevel(this.receiver, NAME("active"))); let tx = new Transaction(0); tx.actions.push(act); tx.header.delay_sec = 5; tx.send(1111, this.receiver, false); } public onError(): void { let error = OnErrorValue.fromCurrentAction(); Log.s("I am ").s(RNAME(this.receiver)).s(", I get a onError calling for id: ").i(error.sender_id).flush(); if (error.sender_id == 1111) { let tx = error.getTransaction(); Log.s("onError action account: ").s(RNAME(tx.actions[0].account)).flush(); // you send deferred tx but something wrong happened. // you can do something to handle this case. } } }

jack的合约源码:

import "allocator/arena"; import { Log } from "../../../src/log"; import { Contract } from "../../../src/contract"; import { RNAME, NAME } from "../../../src/account"; import { Action } from "../../../src/action"; class TargetContract extends Contract { @action recepient(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", recepient was called with parameter: ").s(name).flush(); } @action onInline(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", onInline was called with parameter: ").s(name).flush(); } @action onDeferred(name: string): void { Action.requireAuth(NAME("rose")); Log.s("hi, it is ").s(RNAME(this.receiver)).s(", onDeferred was called with parameter: ").s(name).flush(); } public filterAction(orginalReceiver: account_name): boolean { return true; // 这里设置本合约可以接受requireRecepient()调用。 } }

read more
U3使用说明
B

<img src="https://user-images.githubusercontent.com/1866848/46092827-535d5880-c1e8-11e8-8a65-f5d9d74df96e.png" width="250" align=center />

Javascript封装的负责与链交互的通用库

应用环境

浏览器(ES6)或 NodeJS

如果你想集成u3.js到react native环境中,有一个可行的方法,借助rn-nodeify实现,参考示例U3RNDemo

使用方法

一、如果是在浏览器中使用u3,请参考以下用法:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> <script src="../dist/u3.js"></script> <script> let u3 = U3.createU3({ httpEndpoint: 'http://127.0.0.1:8888', httpEndpoint_history: 'http://127.0.0.1:3000', broadcast: true, debug: false, sign: true, logger: { log: console.log, error: console.error, debug: console.log }, chainId:'0eaaff4003d4e08a541332c62827c0ac5d96766c712316afe7ade6f99b8d70fe', symbol: 'UGAS' }); u3.getChainInfo((err, info) => { if (err) {throw err;} console.log(info); }); </script> </head> <body> </body> </html>

二、 如果是在NodeJS环境中使用u3,请参照以下用法:

安装u3

npm install u3.js 或 yarn add u3.js

初始化 const { createU3 } = require('u3.js/src'); let config = { httpEndpoint: 'http://127.0.0.1:8888', httpEndpoint_history: 'http://127.0.0.1:3000', chainId: '0eaaff4003d4e08a541332c62827c0ac5d96766c712316afe7ade6f99b8d70fe', keyProvider: ['PrivateKeys...'], broadcast: true, sign: true } let u3 = createU3(config); u3.getChainInfo((err, info) => { if (err) {throw err;} console.log(info); }); 本地环境运行

本地运行u3,需要依赖docker.

1.从这里 下载docker并安装;

2.然后添加中国区镜像地址:https://registry.docker-cn.com;

3.点击"Apply & Restart";

<img src="https://user-images.githubusercontent.com/1866848/46121838-3d7f8000-c248-11e8-933a-fbcf30cfc443.png" width="500" hegiht="700" align=center />

4.进入u3.js/docker && ./start.sh

配置 全局配置 <b>httpEndpoint</b> string - 链实时API的http或https地址.如果是在浏览器环境中使用u3,请注意配置相同的域名. <b>httpEndpoint_history</b> string - 链历史API的http或https地址.如果是在浏览器环境中使用u3,请注意配置相同的域名. <b>chainId</b> 链唯一的ID. 链ID可以通过 [httpEndpoint]/v1/chain/get_chain_info获得. <b>keyProvider</b> [array<string>|string|function] - 提供私钥用来签名交易. 提供用于签名事务的私钥。
如果提供了多个私钥,不能确定使用哪个私钥,可以使用调用get_required_keysAPI 获取要使用签名的密钥. 如果是函数,那么每一个交易都将会使用该函数.
如果这里不提供keyProvider,那么它可能会Options配置项提供在每一个action或每一个transaction中 <b>expireInSeconds</b> number - 事务到期前的秒数,时间基于nodultrain的时间. <b>broadcast</b> [boolean=true] - 默认是true。使用true将交易发布到区块链,使用false将获取签名的事务. <b>verbose</b> [boolean=false] - 默认是false。详细日志记录. <b>debug</b> [boolean=false] - 默认是false。低级调试日志记录. <b>sign</b> [boolean=true] - 默认是true。使用私钥签名交易。保留未签名的交易避免了提供私钥的需要. <b>logger</b> - 默认日志配置. logger: { log: config.verbose ? console.log : null, // 如果值为null,则禁用日志 error: config.verbose ? console.error : null, } Options配置项

Options可以在方法参数之后添加. Authorization应用于单独的actions.比如:

options = { authorization: 'alice@active', broadcast: true, sign: true } u3.transfer('alice', 'bob', '1.0000 UGAS', '', options) <b>authorization</b> [array<auth>|auth] - 指明账号和权限,典型地应用于多重签名的配置中. Authorization必须是一个字符串格式,形如account@permission. <b>broadcast</b> [boolean=true] - 默认是true。使用true将交易发布到区块链,使用false将获取签名的事务. <b>sign</b> [boolean=true] - 默认是true。使用私钥签名交易。保留未签名的交易避免了提供私钥的需要. <b>keyProvider</b> [array<string>|string|function] - 就像global配置项中的keyProvider一样,这里的配置可以以覆盖全局配置的形式为每一个action或每一个transaction提供单独的私钥. await u3.anyAction('args', {keyProvider}) await u3.transaction(tr => { tr.anyAction() }, {keyProvider}) 创建账号

创建账号需要花费creator账号的一些代币,为新账号抵押部分RAM和带宽

const u3 = createU3(config); const name = 'abcdefg12345';//普通账号需要满足规则:必须为12345abcdefghijklmnopqrstuvwxyz中的12位 let params = { creator: 'ben', name: name, owner: pubkey, active: pubkey, updateable: 1,//可选,账号是否可以更新(更新合约) }; await u3.createUser(params); 转账(UGAS)

转账方法使用非常频繁,UGAS的转账需要调用系统合约utrio.token.

const u3 = createU3(config); const c = await u3.contract('utrio.token') // 使用位置参数 await c.transfer('ben', 'bob', '1.2000 UGAS', '') // 使用名称参数 await c.transfer({from: 'bob', to: 'ben', quantity: '1.3000 UGAS', memo: ''}) 签名

使用 { sign: false, broadcast: false } 创建一个u3实例并且做一些action, 然后将未签名的交易发送到钱包中.

const u3_offline = createU3({ sign: false, broadcast: false }); const c = u3_offline.contract('utrio.token'); let unsigned_transaction = await c.transfer('ultrainio', 'ben', '1 UGAS', 'uu');

在钱包中你可以提供私钥或助记词来签名,并将签名后的交易发送到链上.

const u3_online = createU3(); let signature = await u3_online.sign(unsigned_transaction, privateKeyOrMnemonic, chainId); if (signature) { let signedTransaction = Object.assign({}, unsigned_transaction.transaction, { signatures: [signature] }); let processedTransaction = await u3_online.pushTx(signedTransaction); } 资源

调用合约只会消耗合约Owner的资源,所以如果你想部署一个合约,请先购买一些资源.

resourcelease(payer,receiver,slot,days) const u3 = createU3(config); const c = await u3.contract('ultrainio') await c.resourcelease('ben', 'bob', 1, 10);// 1 slot for 10 days

通过以下方法查询资源详情.

const resource = await u3.queryResource('abcdefg12345'); console.log(resource) 合约 部署合约

部署合约需要提供包含目标文件为 .abi,.wast,*.wasm 的三个文件的文件夹.

deploy(contracts_files_path, deploy_account) 第一个参数为合约目标文件的绝对路径,第二个合约部署者账号. const u3 = createU3(config); await u3.deploy(path.resolve(__dirname, '../contracts/token/token'), 'bob'); 调用合约 const u3 = createU3(config); const c = await u3.contract('ben'); await c.transfer('bob', 'ben', '1.0000 UGAS',''); //或者像这样调用 await u3.contract('ben').then(sm => sm.transfer('bob', 'ben', '1.0000 UGAS','')) // 一笔交易也可以包含多个合约中的多个action await u3.transaction(['ben', 'bob'], ({sm1, sm2}) => { sm1.myaction(..) sm2.myaction(..) }) 发行代币 const u3 = createU3(config); const account = 'bob'; await u3.transaction(account, token => { token.create(account, '10000000.0000 DDD'); token.issue(account, '10000000.0000 DDD', 'issue'); }); const balance = await u3.getCurrencyBalance(account, account, 'DDD') console.log('currency balance', balance) 事件

Ultrain提供了一个事件注册监听机制用来解决异步场景下业务需求.客户端首先订阅一个事件,提供一个用来接收消息的地址,
当合约中的某个方法触发时,该地址会收到来自链的推送消息.

订阅/取消订阅 registerEvent(deployer, listen_url) unregisterEvent(deployer, listen_url)

deployer : 合约的部署者账号

listen_url : 接收消息的地址

注意: 如果你是在本地docker环境中使用改机制,请确认接收地址是一个可以从docker访问到的本地宿主地址.

const u3 = createU3(config); const subscribe = await u3.registerEvent('ben', 'http://192.168.1.5:3002'); //or const unsubscribe = await u3.unregisterEvent('ben', 'http://192.168.1.5:3002'); 监听 const { createU3, listener } = require('u3.js/src'); listener(function(data) { // do callback logic console.log(data); }); U3Utils.test.wait(2000); //must call listener function before emit event const contract = await u3.contract(account); contract.hi('ben', 30, 'It is a test', { authorization: [`ben@active`] });

read more
Ultrain商用主网监控页面上线 &第二期测试网矿工入选名单公布
R

尊敬的社区成员们:

感谢大家一直以来对于Ultrain的支持!Ultrain正式宣布:商用主网监控页面已上线,各位可通过此页面https://www.ultrain.io/miner-registration查看商业主网的运行数据,了解Ultrain的共识规则和商业模式,并可结合此文https://www.ultrain.io/trends-detail/rk0uOe0gN充分了解Ultrain Token经济体系,明白Ultrain的经济模型建立于服务实体经济的基础之上,是一个更为良性和符合商业发展规律的模型。随着算力购买的商业需求的增长,Ultrain经济体系将会良性发展,保证早期进入者的利益,从而也可以保证整个商业生态的健康发展。

此外,继1月8日发布第二期测试网矿工招募公告后,我们再次收到了大量来自全球各地的申请信息,再次感谢大家对于Ultrain的大力支持,Ultrain于此正式宣布第二期测试网入选名单,欢迎各位加入Ultrain测试网络,提前预览Ultrain技术概貌:

1、aveloyan@****.com

2、975719785@****.com

3、1341568776@****.com

4、lowesyang@****.com

5、739884701@****.com

6、gaoshouye@****.com

7、xiangzhong0912@****.com

8、429624223@****.com

9、771310025@****.com

10、312374979@****.com

11、563585812@****.com

12、35433651@****.com

(以下为Ultrain2018年12月26日-2019年1月13日的测试网矿工排名)

欢迎有意向者继续参与我们的下期测试公网矿工申请,也请大家保持对于Ultrain的关注!

ULTRAIN TEAM

2019年1月24日

read more