通用行为规范包括一个定义代币铸造操作的主函数、一个用于发行多个代币的可选合并铸造操作、用于获取挑战码、挖矿难度、挖矿目标和当前奖励的 getter,以及最终的 Mint 事件,该事件将在成功解决方案验证和代币发行后发出。至少,合约必须遵守此接口(保存可选的合并操作)。建议合约与下面描述的更具行为定义的抽象合约进行交互,以便利用更明确的构造,从而允许通过重写的阶段性函数进行更轻松的外部实现。(参见下面的“抽象合约”)
mint 操作作为一个公共函数存在,该函数调用 4 个单独的阶段,表示为函数 hash、_reward、_newEpoch 和 _adjustDifficulty。为了创建最灵活的实现,同时遵守必要的合约协议,建议代币实现者覆盖内部方法,允许基本合约通过 mint 处理它们的执行。
这个面向外部的函数由矿工调用,以验证挑战摘要、计算奖励、
填充统计信息、更改 epoch 变量并根据需要调整解决方案难度。完成后,
在返回布尔成功标志之前,会发出一个 Mint 事件。
contractAbstractERC918isEIP918Interface{// the amount of time between difficulty adjustments
uintpublicadjustmentInterval;// generate a new challenge number after a new reward is minted
bytes32publicchallengeNumber;// the current mining target
uintpublicminingTarget;// cumulative counter of the total minted tokens
uintpublictokensMinted;// number of blocks per difficulty readjustment
uintpublicblocksPerReadjustment;//number of 'blocks' mined
uintpublicepochCount;/*
* Externally facing mint function that is called by miners to validate challenge digests, calculate reward,
* populate statistics, mutate epoch variables and adjust the solution difficulty as required. Once complete,
* a Mint event is emitted before returning a success indicator.
**/functionmint(uint256nonce)publicreturns(boolsuccess){require(msg.sender!=address(0));// perform the hash function validation
hash(nonce);// calculate the current reward
uintrewardAmount=_reward();// increment the minted tokens amount
tokensMinted+=rewardAmount;epochCount=_epoch();//every so often, readjust difficulty. Don't readjust when deploying
if(epochCount%blocksPerReadjustment==0){_adjustDifficulty();}// send Mint event indicating a successful implementation
emitMint(msg.sender,rewardAmount,epochCount,challengeNumber);returntrue;}}
Mint 事件
成功验证和奖励后,mint 方法会分派一个 Mint 事件,指示奖励地址、奖励金额、epoch 计数和最新的挑战码。
为了方便第三方铸造提交范例,例如矿工将解决方案提交给矿池运营商和/或系统的情况,可以使用委托铸造扩展来允许矿池帐户代表用户提交解决方案,从而使矿工可以避免直接支付以太坊交易费用。这通过链下挖掘帐户打包和签署标准化的 mint 解决方案包并将其发送给矿池或第三方以进行提交来执行。
ERC918 可挖矿 Mint 数据包元数据应使用以下架构准备:
{"title":"Mineable Mint Packet Metadata","type":"object","properties":{"nonce":{"type":"string","description":"Identifies the target solution nonce",//标识目标解决方案 nonce
},"origin":{"type":"string","description":"Identifies the original user that mined the solution nonce",//标识挖掘解决方案 nonce 的原始用户
},"signature":{"type":"string","description":"The signed hash of tightly packed variables sha3('delegatedMintHashing(uint256,address)')+nonce+origin_account",//紧密打包变量的签名哈希 sha3('delegatedMintHashing(uint256,address)')+nonce+origin_account
}}}
在 JavaScript 客户端上准备可挖矿的 mint 数据包将如下所示:
functionprepareDelegatedMintTxn(nonce,account){varfunctionSig=web3.utils.sha3("delegatedMintHashing(uint256,address)").substring(0,10)vardata=web3.utils.soliditySha3(functionSig,nonce,account.address)varsig=web3.eth.accounts.sign(web3.utils.toHex(data),account.privateKey)// prepare the mint packet
varpacket={}packet.nonce=noncepacket.origin=account.addresspacket.signature=sig.signature// deliver resulting JSON packet to pool or third party
varmineableMintPacket=JSON.stringify(packet,null,4)/* todo: send mineableMintPacket to submitter */...}
一旦准备好数据包并对其进行格式化,就可以将其路由到第三方,该第三方会将交易提交给合约的 delegatedMint() 函数,从而支付交易 gas 并接收生成的代币。然后,矿池/第三方必须手动将铸造的代币(减去费用)偿还给原始矿工。
以下代码示例说明了第三方数据包中继:
//received by minter //由矿工接收
varmineableMintPacket=...varpacket=JSON.parse(mineableMintPacket)erc918MineableToken.delegatedMint(packet.nonce,packet.origin,packet.signature)
委托 Mint 扩展以 ERC918 为基础实现为子合约:
import'openzeppelin-solidity/contracts/contracts/cryptography/ECDSA.sol';contractERC918DelegatedMintisAbstractERC918,ECDSA{/**
* @notice Hash (keccak256) of the payload used by delegatedMint
* @param _nonce the golden nonce
* @param _origin the original minter
* @param _signature the original minter's elliptical curve signature
*/functiondelegatedMint(uint256_nonce,address_origin,bytes_signature)publicreturns(boolsuccess){bytes32hashedTx=delegatedMintHashing(_nonce,_origin);addressminter=recover(hashedTx,_signature);require(minter==_origin,"Origin minter address does not match recovered signature address [ AbstractERC918.delegatedMint() ]");require(minter!=address(0),"Invalid minter address recovered from signature [ ERC918DelegatedMint.delegatedMint() ]");success=mintInternal(_nonce,minter);}/**
* @notice Hash (keccak256) of the payload used by delegatedMint
* @param _nonce the golden nonce
* @param _origin the original minter
*/functiondelegatedMintHashing(uint256_nonce,address_origin)publicpurereturns(bytes32){/* "0x7b36737a": delegatedMintHashing(uint256,address) */returntoEthSignedMessageHash(keccak256(abi.encodePacked(bytes4(0x7b36737a),_nonce,_origin)));}}
可挖矿代币元数据(可选)
为了为特定的可挖矿代币提供更丰富且可能可变的元数据,提供对所述数据的链下引用更为可行。这需要实现一个接口方法“metadataURI()”,该方法返回一个 JSON 字符串,该字符串使用字符串字段 symbol、name、description、website、image 和 type 进行编码。
可挖矿代币元数据的 Solidity 接口:
/**
* @title ERC-918 可挖矿代币标准,可选的元数据扩展
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-918.md
*
*/interfaceERC918MetadataisAbstractERC918{/**
* @notice A distinct Uniform Resource Identifier (URI) for a mineable asset.
*/functionmetadataURI()externalviewreturns(string);}
可挖矿代币元数据 JSON 模式定义:
{"title":"Mineable Token Metadata","type":"object","properties":{"symbol":{"type":"string","description":"Identifies the Mineable Token's symbol",//标识可挖矿代币的符号
},"name":{"type":"string","description":"Identifies the Mineable Token's name",//标识可挖矿代币的名称
},"description":{"type":"string","description":"Identifies the Mineable Token's long description",//标识可挖矿代币的长描述
},"website":{"type":"string","description":"Identifies the Mineable Token's homepage URI",//标识可挖矿代币的主页 URI
},"image":{"type":"string","description":"Identifies the Mineable Token's image URI",//标识可挖矿代币的图像 URI
},"type":{"type":"string","description":"Identifies the Mineable Token's hash algorithm ( ie.keccak256 ) used to encode the solution",//标识用于编码解决方案的可挖矿代币的哈希算法(即 keccak256)
}}}
该标准的早期版本在 mint() 函数中包含了一个冗余的“challenge_digest”参数,该参数哈希编码了打包变量 challengeNumber、msg.sender 和 nonce。已决定可以从标准中删除它,以帮助最大限度地减少处理,从而减少 mint 操作期间的 gas 使用量。但是,为了与现有的挖矿程序和矿池软件实现互操作性,可以将以下合约添加到继承树中:
/**
* @title ERC-918 可挖矿代币标准,可选的向后兼容性函数
* @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-918.md
*
*/contractERC918BackwardsCompatibleisAbstractERC918{/*
* @notice 面向外部的 mint 函数,保留用于与之前的 mint() 定义向后兼容
* @param _nonce 解决方案 nonce
* @param _challenge_digest keccak256 编码的挑战码 + 消息发送者 + 解决方案 nonce
**/functionmint(uint256_nonce,bytes32_challenge_digest)publicreturns(boolsuccess){//挑战摘要必须与预期匹配
bytes32digest=keccak256(abi.encodePacked(challengeNumber,msg.sender,_nonce));require(digest==_challenge_digest,"Challenge digest does not match expected digest on token contract [ AbstractERC918.mint() ]");success=mint(_nonce);}}