DAPP开发者指南
DAPP用户的参与方法
DAPP用户使用子链的前提是有足够多的SCS矿工已经注册进了SCS子链矿池。 DAPP用户需要准备自己业务逻辑的智能合约。
部署子链控制合约subchainbase
子链控制合约subchainbase是DAPP用户使用子链的基本工具,其提供子链运行前和运行中的一些必要接口。通常来说,子链控制合约subchainbase需要修改相应的内容如下:
- 参数proto:通过官方渠道获取到子链矿池合约subchainprotocolbase的地址,复制粘贴到此变量;
- 参数vnodeProtocolBaseAddr:通过官方渠道获取到代理智能合约vnodeprotocolbase的地址,复制粘贴到此变量;
- 参数min:子链运行后需要的SCS的最小数量,建议数量为 3
- 参数max:子链运行后需要的SCS的最大数量,建议数量为30,最大不要超过100
- 参数thousandth:默认为1
- 参数flushRound:子链刷新周期(以母链block生成数量为准),小于100的值,合约会自动置为100
- 合约部署时的gas值必须为6700000
另附1示例代码:
chain3.personal.unlockAccount(mc.accounts[0], '',0);
var proto = "0x8fab1913cc......2deb725" ;
var vnodeProtocolBaseAddr = "0x8fab1913c......2db52deb725" ;
var min = 1 ;
var max = 10 ;
var thousandth = 1 ;
var flushRound = 20 ;
var subchainbaseContract = chain3.mc.contract([{"constant":true,"inputs":[],"name":"maxMember",......,"type":"event"}]);
var subchainbase = subchainbaseContract.new(
proto,
vnodeProtocolBaseAddr,
min,
max,
thousandth,
flushRound,
{
from: chain3.mc.accounts[0],
data: '0x6060604052600c601555670d...708e8ee3c23da8b02d0278eb0029',
gas: '6700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
})
B2、RegisterOpen:
首先介绍一下RegisterOpen的主要工作:
- Dapp用户设置子链开放注册;
- 主网广播通知所有的协议合约中的候选SCS;
- SCS收到广播后,参与子链的注册,按注册先后排序,直到注册数目达到子链的上限。
通过开放子链注册,候选SCS内部完成注册,最终成为子链的一员,才有资格参与子链的相关业务。 需要注意的是:SCS参与子链注册是通过SCS地址(我们也称为scsid,放在“./scskeystore”目录下)发送交易到子链完成;因此需要给SCS地址储备一定量的moac,建议1个moac。
另附1示例代码RegisteOpen:
function subchainRegisterOpen(dappAddr,dappPasswd,subchainAddr)
{
chain3.personal.unlockAccount(dappAddr, dappPasswd,0);
sendtx(dappAddr, subchainAddr, '0','0x5defc56c' );
}
【说明】:
- dappAddr、dappPasswd:Dapp用户用来发送交易前账户解锁;
- subchainAddr:部署子链控制合约subchainbase的地址;
- 数据:'0x5defc56c'是子链控制合约subchainbase中‘registerOpen()’通过hash算法Keccak256得到前4个字节,此处不用修改;
另附1调用实例:
subchainRegisterOpen('0x1b9ce7e4f15...38913a56cd986786',
‘123’,
'0x6f5b81365fb1d3d...536cc78749af2c')
registerOpen后,系统将自动选择符合条件的SCS节点并通知他们进行注册,DAPP用户需要通过如下方法查看已经注册完成的SCS节点数目(nodeCount): 方法一:刚部署完子链合约可以在Console窗口直接查询
> subchainBase.nodeCount()
通过子链合约对象subchainBase调用合约成员nodeCount 方法二:根据合约地址,console命令直接调用
> mc.getStorageAt(subchainAddr,0x0e)
通过合约地址subchainAddr查询合约中第0x0e个public成员变量(即nodeCount)的值,请不要修改此值 当达到子链运行需要的SCS的数量区间后,即可进入RegisterClose步骤
B3、RegisterClose:
首先介绍一下RegisterClose的主要工作:
- Dapp用户设置子链关闭注册;
- 已经注册SCS数目必须不小于子链要求的最小数目,否则子链注册无效;
- 主网广播通知所有的协议合约中候选SCS,包括已经注册的成功的SCS;
- SCS收到广播后,SCS自身完成初始化并开始子链运行。
关闭子链注册后,候选SCS不能再通过subchainRegisterOpen方式注册该子链,已经注册的SCS处于正常运行状态,参与子链的相关业务,如:处理交易、出块、刷新等。
regiserClose Javascript方法:
function subchainRegisterClose(dappAddr,dappPasswd,subchainAddr)
{
chain3.personal.unlockAccount(dappAddr, dappPasswd,0);
sendtx(dappAddr, subchainAddr, '0','0x69f3576f' );
}
【说明】:
- dappAddr、dappPasswd:Dapp用户用来发送交易前账户解锁;
- subchainAddr:部署子链合约得到的合约地址;
- 数据:'0x69f3576f'是子链控制合约subchainbase中‘registerClose()’通过hash算法Keccak256得到前4个字节;
另附1调用实例:
subchainRegisterClose('0x1b9ce7e4f15......e0e38913a56cd986786',
‘123’,
'0x6f5b81365fb1d3......6907fa536cc78749af2c')
再次建议在close之前先查看有足够多的节点已经注册进来,以便子链启动时有足够多的矿工可以工作。
B4、DAPP智能合约的部署
通过directcall方式进行子链合约的部署与调用,directcall也是通过console方法mc. sendTransaction向VNODE-PROXY节点发送交易,只不过需要注意以下几点:
- to填写子链控制合约subchainbase的地址;
- directcall成功调用不需要gas费用,所以gas和gasPrice可以是任意值;
- shardingflag必须填写0x1;
- via必须填写发送交易VNODE-PROXY节点config中的地址; 编译DAPP智能合约,生成ABI和BIN,把BIN数据按照directcall方式写入到data里通过交易发送到VNODE-PROXY,查看部署结果参考B6。
另附1示例代码:
function deploycode()
{
chain3.personal.unlockAccount(mc.accounts[0],'',0);
chain3.mc.sendTransaction(
{
from: mc.accounts[0],
value:chain3.toSha('0','mc'),
to: subchainbase,
gas: "0",
gasPrice: "0",
shardingflag: "0x1",
nonce: 1,
data: '0x606060405234156......9c6697187ac00029',
via: '0x78e1b4584085......e3cff29f11f8d5e08f54dc'
});
//console.log('sending from:' + src + ' to:' + tgtaddr + ' with data:' + strData);
}
B5、DAPP智能合约的调用
DAPP智能合约的调用参考B4中directcall方法,编译DAPP智能合约,获取需要调用的合约方法数据,把数据按照directcall方式写入到data里通过交易发送到VNODE-PROXY,查看调用结果参考B6。
另附1示例代码:
function testSet(num)
{
chain3.personal.unlockAccount(mc.accounts[0],'',0);
chain3.mc.sendTransaction(
{
from: mc.accounts[0],
value:chain3.toSha('0','mc'),
to: subchainbase,
gas: "0",
gasPrice: "0",
shardingflag: "0x1",
nonce: num,
data: '0x4f2be91f',
via: '0x78e1b45840850......ff29f11f8d5e08f54dc'
});
//console.log('sending from:' + src + ' to:' + tgtaddr + ' with data:' + strData);
}
B6、DAPP智能合约的查询(RPC模式)
DAPP智能合约查询的前提是使用SCS MONITOR方式接入到子链中,并开启RPC。当前提供的接口如下(go语言实现):
数据定义:
type Args struct {
Sender common.Address // Dapp创建者
SubChainAddr common.Address
}
type ArgsData struct {
Sender common.Address // Dapp创建者
SubChainAddr common.Address
Func string // eg:"SetData()", "rpcGetData()"
}
RPC接口1:获取SCSID
接口:func GetScsId(args Args, reply common.Address) error 调用方法:
client, err := rpc.DialHTTP("tcp", serverAddress+":"+serverPort)
var scsid common.Address
client.Call("ScsRPCMethod.GetScsId", Args{}, &scsid)
RPC接口2:获取Dapp创建者在子链的nonce
接口:func GetNonce(args Args, reply uint64) error 调用方法:
args := Args{sender, subChainAddr}
var noce uint64//
client.Call("ScsRPCMethod.GetNonce", args, & noce)
RPC接口3:调用Dapp合约某个无参函数并获得返回值
如下举例,调用GetData()并获取返回值存到replyData 接口:func GetData(argsData ArgsData, reply []byte) 调用方法:
var replyData []byte
argsData := ArgsData{sender, subChainAddr, "GetData()"}
client.Call("ScsRPCMethod.GetData", argsData, &replyData)
RPC接口4:获取Dapp合约状态
返回值0:未创建;1:已成功创建 接口:func (scs ScsRPCMethod) GetDappState(args Args, reply *uint64) error 调用方法:
args := Args{sender, subChainAddr}
var reply uint64
client.Call("ScsRPCMethod.GetDappState", args, &reply)
RPC接口5:GetContractInfo:
接口提供子链DAPP智能合约全局变量查询,接口协议使用http。 请求参数:
type ContractInfoReq struct {
Reqtype int//查询类型0: 查看合约全部变量 , 1: 查看合约某一个数组变量 , 2: 查看合约某一个mapping变量 , 3: 查看合约某一个结构体变量, 4: 查看合约某一简单类型变量(单倍长度存储的变量), 5: 查看合约某一变长变量(如string、bytes)
Key string//64位定长十六进制字符串,查询的变量在合约里面的index ,查询全部变量时可以不填
Position string//64位定长十六进制字符串,当Reqtype==1时,Position为数组里第几个变量(从0开始);当Reqtype==2时,Position为mapping下标
Structformat []byte//当出现结构体变量时,此变量描述结构,结构体只允许出现1-single(简单类型变量单倍长度存储的变量), 2-list(简单类型数组变量), 3-string变长变量(如string、bytes),若结构变量为ContractInfoReq,Structformat = []byte{‘1’,’3’,’3’,’3’}
}
type GetContractInfoReq struct {
SubChainAddr common.Address//合约地址
Request []ContractInfoReq//查询合约全部变量数据,数据太大,可以选择查询需要的几个变量
}
返回参数:
type ContractInfo struct {
Balance *big.Int
Nonce uint64
Root common.Hash
CodeHash []byte
Code []byte
Storage map[string]string
}