当前位置: 首页 > 文档资料 > MOAC 中文 WIKI >

DAPP开发者指南

优质
小牛编辑
145浏览
2023-12-01

DAPP用户的参与方法

DAPP用户使用子链的前提是有足够多的SCS矿工已经注册进了SCS子链矿池。 DAPP用户需要准备自己业务逻辑的智能合约。

部署子链控制合约subchainbase

子链控制合约subchainbase是DAPP用户使用子链的基本工具,其提供子链运行前和运行中的一些必要接口。通常来说,子链控制合约subchainbase需要修改相应的内容如下:

  1. 参数proto:通过官方渠道获取到子链矿池合约subchainprotocolbase的地址,复制粘贴到此变量;
  2. 参数vnodeProtocolBaseAddr:通过官方渠道获取到代理智能合约vnodeprotocolbase的地址,复制粘贴到此变量;
  3. 参数min:子链运行后需要的SCS的最小数量,建议数量为 3
  4. 参数max:子链运行后需要的SCS的最大数量,建议数量为30,最大不要超过100
  5. 参数thousandth:默认为1
  6. 参数flushRound:子链刷新周期(以母链block生成数量为准),小于100的值,合约会自动置为100
  7. 合约部署时的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节点发送交易,只不过需要注意以下几点:

  1. to填写子链控制合约subchainbase的地址;
  2. directcall成功调用不需要gas费用,所以gas和gasPrice可以是任意值;
  3. shardingflag必须填写0x1;
  4. 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
}