公链 defi 质押挖矿分红 dapp 系统开发(合约定制)
DApp 是什么
DApp( Decentralized Application )即分布式应用或去中心化应用。DApp 就是智能合约+App。
一个真正的 DApp 应用,需要同时满足以下几个条件,系统开发对接唯 hkkf5566:
应用必须完全开源、自治,且没有一个实体控制该应用超过 51%的- Token。该应用必须能够根据用户的反馈及技术要求进行升级,且应用升级必须由大部分用户达成共识之后方可进行;
应用的数据必须加密后存储在公开的区块链上;
应用必须拥有 Token 机制,矿工或应用维护节点需得到代币奖励;
应用代币的产生必须依据标准的加密算法,有价值的节点可以根据该算法获取应用的代币奖励。
DApp 原理
一个 DApp 被调用之前需要先部署到以太坊上,不管是私链,公链还是联盟链。故本章节分为两部分,DApp 部署原理和调用原理。
部署
一个 DApp 由多个智能合约组成,部署一个 DApp 也就是同时部署多个智能合约,这里讲述一下部署一个智能合约的流程。
以太坊 Web3.js 提供调用合约的方法一共有四种:
call: 这是最简单的调用方式,适用于调用只读的方法,也就是调用过程不会修改区块链上的数据,因为它只读取本地数据即可,因此不会消耗 gas,而且可以立刻获得返回值,适用于前端调用,具体例子如下。
//合约声明
contract test {
function multiply(uint a) returns(uint d) {
return a * 7;
}
}
//合约调用
var Multiply7 = eth.contract(contract.info.abiDefinition);
var myMultiply7 = Multiply7.at(address);
myMultiply7.multiply.call(3)
sendTransaction: 可调用读或写方法,调用过程会创建一个交易,调用之后会返回一个交易 hash 值,它会广播到网络,等待矿工打包, 它会消耗 gas,而且该调用不能立刻获得返回值,只能从 event log 中获取,也是适用于前端调用,代码如下。
wheelOfFortune.makeBet.sendTransaction(num - 1, betCount, tips, { from: web3.eth.accounts[0], value: betUnit \* betCount + tips }).then(function (result) {
if (result.logs.length > 0) {
var eventobj = result.logs[0].args;
$.ajax({
url: '/Wheel/makeBet/' + eventobj.pieceIdx + '/' + betCount + '/' + tips + '/' + web3.eth.accounts[0],
success: function (data) {
var nextRound = JSON.parse(data);
$("#currentTotal").text(nextRound.betPool.toLocaleString());
playersNumberOfPiece = nextRound.playersNumberOfPiece;
alert('成功');
}
});
}
});
直接调用: 这是一种特殊调用,可以说是前两种调用的结合,因为当合约方法有 constant 修饰时,直接调用会等同于 call,否则等同于 sendTransaction。
var winIdx = (Math.random() \* wheel.config.pieceCount) >> 0;
// 后台调用 infura 部署的合约必须用 sendRawTransaction
var coder = require('web3/lib/solidity/coder');
var CryptoJS = require('crypto-js');
var Tx = require('ethereumjs-tx');
var privateKey = new Buffer("71112e795325d5cbf14d665091ce4626f26c8342b8038f1adcdfff26be04a220", 'hex');
var functionName = 'finishRound';
var types = ['uint'];
var args = [winIdx];
var fullName = functionName + '(' + types.join() + ')';
var signature = CryptoJS.SHA3(fullName, { outputLength: 256 }).toString(CryptoJS.enc.Hex).slice(0, 8);
var dataHex = signature + coder.encodeParams(types, args);
var data = '0x' + dataHex;
var account = "0x4BEB9EA54fc912B619D5C682BA1cB3524bc80955";
var nonce = web3.toHex(web3.eth.getTransactionCount(account));
var gasPrice = web3.toHex(web3.eth.gasPrice);
var gasLimitHex = web3.toHex(3000000);
var rawTx = { 'nonce': nonce, 'gasPrice': gasPrice, 'gasLimit': gasLimitHex, 'from': account, 'to': contractAddress, 'data': data }
var tx = new Tx(rawTx)
tx.sign(privateKey)
var serializedTx = '0x' + tx.serialize().toString('hex')
web3.eth.sendRawTransaction(serializedTx, function (err, txHash) {
if (!err) {
console.log(JSON.stringify({ "transactionHash": txHash }));
} else {
console.log("finish round error " + err);
}
});
sendRawTransaction: 前两种方法都不需要调用者提供交易发起者的私钥进行方法签名,因为 MetaMask 或本地以太坊节点提供了,但是当我们没有 MetaMask 时调用公链合约,我们只能调用 sendRawTransaction 使用指定账户的私钥签名方法后才能调用合约,值得注意的是,该方法我们无法获得返回值,即使在 event log 中也拿不到,只能在得到 transaction 的 hash 后再读取区块链信息才可以,一般在后台调用方法时用到。
调用时需要注意的几点:
当调用者给予 Gas 不足时,EVM 会报出 out of gas 的错误,这时候会回滚本地交易所做过的所有修改,好在以太坊还提供了 estimategas 方法,可以让我们在调用之前预估交易所需的 Gas,因为 Solc 编译器会算出每一句代码的价格,不过有时会不准确,特别是出现死循环或者违反了修饰方法的限制条件时。
调用频率不能过快,一方面所有调用方法都是异步的,返回时间可能会较长,另一方面有可能第一次调用的 transaction 还没被挖到区块中,下一次调用就来了,而且两次调用的 hash 可能是一样的,所以就会报 replacement transaction underpriced 错误。
当我们调用了不存在的方法时,EVM 会自动调用合约中的 Fallback 方法作为兜底,而且会将 Gas 全耗完,如果没有定义 Fallback 方法,才会报错回滚。
评论