连载:Qtum量子链设计文档(五):分布式自治协议DGP

量子链Qtum2018-06-15 19:10:33  阅读 -评论 0

连载:Qtum量子链设计文档(五):分布式自治协议DGP点击上方蓝字关注QTUM公众号,了解最新行业资讯

连载:Qtum量子链设计文档(五):分布式自治协议DGP

Qtum量子链原始设计文档汇总(五)-- Qtum分布式自治协议DGP

分布式自治协议(DGP)是Qtum量子链提出的全新链上治理协议。它使得Qtum可以在链上完成网络参数的修改,且无需分叉。DGP的安全性及其具体实现都通过智能合约完成。DGP是Qtum生态稳定发展、且不断进化的前提,同时也是未来实现Qtum区块链上智能合约和DApp治理的重要一环。

以下截取部分早期Qtum开发团队针对DGP的相关原始设计文档(附中文翻译)(ps:文档中QTUM<#>或QTUMCORE<#>为内部设计文档编号):

QTUMCORE-55:Decentralized Governance Protocol and Dynamic Gas Schedules Description:Decentralized Governance Protocol is a new concept to be implemented in Qtum. It allows us to change various network parameters without needing to release a new binary wallet. It's configuration is secured by a smart contract which implements multi-sig checks etc. The first big concept to be controlled by DGP is to allow for gas schedules to be changed without needing to download a new binary wallet nor create a network fork. DGP is intended to control many things eventually, but to start with it will only be used for "dynamic gas schedules". There are 2 primary contracts involved in each DGP feature: 1. The DGP framework contract for each feature 2. The actual DGP feature contract which controls the consensus data Contract code: https://github.com/qtumproject/qtum-dgp/blob/master/dgp-template.sol.js The DGP framework contract accomplishes the following: 1. Proposals and Voting - Each parameter change including internal key management is proposed, and then voted on. If the vote meets chosen conditions, then the proposal is accepted and the operation is performed. A vote is counted using `msg.sender`, so that either a pubkeyhash address or a contract address can be used as a participant 2. Key management - It should be possible to add and remove participants, as well as changing parameters such as how many keys are required for a proposal to be accepted, for a key to be added, etc 3. Sending data of the right format to the DGP feature contract 4. Allow for itself to be disabled so that no further DGP changes can be made without a hard-fork to fix it (in case of a major exploit or problem) 5. Only one proposal is allowed at one time, a proposal can only be made by a participant. Each proposal should include an expiration of no more than 5,000 blocks. Every proposal expires after the expiration, or after voting is complete enough to reject and approve it. 6. (maybe) there should be a list of administrators which can remove proposals, and optionally be the only allowed parties to add a proposal? Meanwhile, the DGP feature contract is much simpler and only does 2 things: 1. Only accepts messages/data from the appropriate DGP framework contract (using msg.sender) 2. Stores the consensus data in a standardized way using `SSTORE` so that the blockchain can retreive and parse the data in the RLP without needing to execute the EVM Every DGP Feature data record starts with a blockheight this consensus data should become active at. Note that old consensus data records should NOT be replaced or modified. This is because we may later need to reference this historical data. Although changing the historical data will not affect syncing the blockchain, but it should be kept in case it is needed. For instance, with this Dynamic Gas Schedule data it is planned to add an additional feature so that a contract call can choose to use the gas schedule which was active when the contract was created. In order for this to be easily possible without a separate database, the historical data must remain in the RLP. This also allows the DGP to retroactively change this feature if a problem is bad enough that it is deemed necessary. The Dynamic Gas Schedule data record will contain the following data: 1. tierStepGas0 2. tierStepGas1 3. tierStepGas2 4. tierStepGas3 5. tierStepGas4 6. tierStepGas5 7. tierStepGas6 8. tierStepGas7 9. expGas 10. expByteGas 11. sha3Gas 12. sha3WordGas 13. sloadGas 14. sstoreSetGas 15. sstoreResetGas 16. sstoreRefundGas 17. jumpdestGas 18. logGas 19. logDataGas 20. logTopicGas 21. createGas 22. callGas 23. callStipend 24. callValueTransferGas 25. callNewAccountGas 26. suicideRefundGas 27. memoryGas 28. quadCoeffDiv 29. createDataGas 30. txGas 31. txCreateGas 32. txDataZeroGas 33. txDataNonZeroGas 34. copyGas 35. extcodesizeGas 36. extcodecopyGas 37. balanceGas 38. suicideGas 39. maxCodeSize (not gas really, but related so it can stay here) For every DGP feature, there should be an equivalent that is hardcoded into Qtum, so that the blockchain is functional without the DGP being deployed and functional. So, for Dynamic Gas Schedules there should be a default hardcoded gas schedule which is used before the DGP contract causes a new gas schedule to be used. Activation Considerations Although not enforced by consensus, no DGP parameter change should ever be published with less than 500 blocks before it activates. Doing so can cause unnecessary network forks and orphans. It is the responsibility of the DGP participants to ensure no proposal is approved that would be active in faster than 500 blocks. Dynamic Gas Schedule Limits We should determine reasonable minimums for each gas schedule item for the EVM. Some opcodes should be allowed to remain free, but also some opcodes like signature and hashing should never be free. For right now, the minimum limits for each gas schedule item should match what is hardcoded into the default gas schedule for Qtum. DGP Features 1. getGasSchedule 2. [reserved for getReward] 3. [reserved for getMinGasPrice] Future Extensibility One big need that may arise is using a significantly more complicated authorization model, such as requiring votes from the community or two separate multisig configurations or some other radically different model that what is implemented in the DGP framework contract. It is possible to completely change the authorization model by adding a new "participant key", and then removing all other keys and reducing the maximum signatures needed to 1. This "participant key" in this case would be a smart contract which implements all of the authorization logic. Note that the authorization contract in this case would also need to handle the proposal/vote system and then simply propose and approve a matching change to the actual DGP framework contract in a single execution. DGP Parameter Change Procedure 1. A proposal is made after discussion with the community. Unless there is an emergency issue, the activation date should be no sooner than 20,000 blocks away. If it is an emergency issue, it should be no sooner than 1000 blocks away (500 blocks to vote, 500 blocks for activation) 2. The proposal is sent to the DGP framework contract 3. The proposal is voted on 4. Assuming the vote passes, then the DGP framework sends the DGP feature contract the proposal data 5. The new DGP parameter change becomes active at the proposal activation block What must actually be implemented We will support multiple DGP contracts. So, we should implement a framework for supporting them. Each DGP contract will be deployed to the blockchain and the address will be hardcoded into the blockchain. Each DGP contract will have a specific address that contains all of the DGP data for consensus. This data should be read directly from the RLP database, and not require executing the EVM. Each DGP "record" will contain 2 items: 1. The blockheight it should become active at 2. The new parameter values to use after the specified blockheight These records will be stored sequentially in memory as an array. There should also be a piece of data that includes the total length of the array. This data should be somewhere standardized (you may have to look up how Solidity stores arrays to figure this out). With a DGP framework implemented that can discover the parameters stored in the RLP, it should be easily possible to use the DGP provided parameters to determine the gas schedule for the current block. Notes 1. It should be possible to have multiple DGP parameter records 2. Caching of the parameters is fine, but the cache should be invalidated when a message is sent to the DGP contract (since the parameters could be changed) 3. If a DGP record has a retroactive blockheight (ie, record says it becomes active at block 500, and blockheight when the update is received is 1000), then it should take effect immediately and not affect previous blocks 4. Make sure to be careful of invalid data. We should gracefully ignore invalid DGP records 5. For the gas schedule DGP, if a DGP gas schedule record says to use less than default value of the template parameters (https://github.com/qtumproject/qtum-dgp/blob/master/gasSchedule-dgp-template.sol.js), then that value should be ignored and set to the value of the template parameter 6. Old DGP data should not be overwritten and should be append-only. So, when a new DGP record becomes active, no data is modified or deleted, there is only data added (except for the integer value that gives the array length) task:分布式自治协议(DGP)和动态的gas schedule 描述:分布式自治协议(DGP)是Qtum中实现的一个新概念。使用DGP,我们不需要发布新的钱包就能修改各种网络参数,其安全性由实现了多重签名的智能合约来保证。 第一个由DGP控制的是,在不下载新的钱包也不创建网络分叉的情况下修改gas schedule参数。DGP最终会控制很多参数,但是最开始还是将它用于“动态的gas schedule”参数。 在每个DGP特性中主要涉及两个合约: 1.  每个特性的DGP框架合约(framework contract) 2.  控制共识数据的实际的DGP特性合约 合约代码:https://github.com/qtumproject/qtum-dgp/blob/master/dgp-template.sol.js DGP框架合约实现以下功能: 1.  提案和投票 -- 每个参数改变包括内部治理席位管理需要先被提议,然后对它进行投票。如果投票符合所选择的条件,则该提议被接受,并执行该操作。投票使用“msg.sender”计算,这样公钥哈希地址或合约地址都可以作为参与者参与投票。 2.  治理席位管理 -- 可以添加和删除参与者,也可以修改治理席参数,比如一个提案被接受需要多少个治理席位同意,添加一个治理席位需要多少个治理席位同意等等。 3.  发送正确格式的数据给DGP特性合约 4.  允许自己被禁用,这样在不使用硬分叉的情况下就不能进行进一步的DGP修改(防止重大的漏洞或问题) 5.  一次只允许一个提案,提案只能由参与者提出。每个提案的有效期不超过5000个区块。每个提案在到期后,或者在投票结束后,完全可以拒绝或者批准它。 6.(可能)应该有一个管理员的列表,管理员可以删除提案,可选地,管理员也可以是唯一允许添加提案的人? DGP特性合约则更简单,它只需要完成以下两件事情: 1.  只从合适的DGP框架合约接收信息/数据(使用msg.sender) 2.  使用“SSTORE”以一种标准化的形式存储共识数据,以便区块链在不运行EVM的情况下就能在RPL中检索和解析该数据。 每个DGP特性数据从该共识数据生效的区块高度开始被记录下来。注意,旧的共识数据记录不应该被替换或修改。虽然修改历史数据不会影响区块链的同步,但仍应保留以方便未来引用这些历史数据。 例如,有了动态的gas schedule数据,我们计划支持调用合约相关的新特性,使得合约调用可以选择采用合约创建时的gas schedule。为了使其在没有单独的数据库的情况下也能很容易实现,历史数据必须保留在RLP中。这也使得发生严重问题时,DGP可以对该特性进行回滚。 动态的Gas Schedule数据记录包含以下数据: 1. tierStepGas0 2. tierStepGas1 3. tierStepGas2 4. tierStepGas3 5. tierStepGas4 6. tierStepGas5 7. tierStepGas6 8. tierStepGas7 9. expGas 10. expByteGas 11. sha3Gas 12. sha3WordGas 13. sloadGas  14. sstoreSetGas  15. sstoreResetGas  16. sstoreRefundGas 17. jumpdestGas 18. logGas 19. logDataGas 20. logTopicGas 21. createGas 22. callGas 23. callStipend 24. callValueTransferGas 25. callNewAccountGas 26. suicideRefundGas 27. memoryGas 28. quadCoeffDiv 29. createDataGas 30. txGas 31. txCreateGas 32. txDataZeroGas 33. txDataNonZeroGas 34. copyGas 35. extcodesizeGas 36. extcodecopyGas 37. balanceGas 38. suicideGas 39. maxCodeSize (不是gas,但是是相关的参数,因此保留在这里) 对于每个DGP特性,都应该有一个在Qtum中硬编码的(即,写死的)等价值,这样即使在DGP没有部署或未工作的情况下,区块链也能正常运行。因此,对于动态的gas schedules,应该有一个默认的写死的gas schedule,该gas schedule在DGP合约产生一个新的gas schedule之前使用。 关于激活的考虑(Activation Consideration) 虽然不是由共识强制执行,但是DGP参数的修改应该在其被激活前的至少500个区块之前发布。不这样做会导致不必要的网络分叉以及孤块问题。DGP参与者有责任确保激活的速度快于500个区块的提案不被通过。 动态的Gas Schedule限制 我们应该为EVM的每个gas schedule项确定合理的最小值。一些操作码应该被允许保持免费,但是也有一些操作码例如签名和哈希永远都不会免费。目前,每个gas schedule项的最低限额应该与Qtum中写死的默认gas schedule相匹配。 DGP特性 1. getGasSchedule 2. [为getReward预留] 3. [为getMinGasPrice预留] 未来的可扩展性 未来可能出现需要采用更复杂授权模型的场景,比如需要来自社区的投票或两种独立的multi-sig(多重签名)配置,或者其他与在目前DGP框架合约中实现的完全不同的模型。 可以通过以下方法来完全改变授权模型:添加一个新的“参与者key”,然后删除所有其他的key并且将需要的最大签名数减少到1。本例中的“参与者key”将是实现所有授权逻辑的智能合约。注意,本例中的授权合约也需要处理提案/投票系统,然后在一次运行中简单地提议并通过对实际的DGP框架合约的匹配修改。 DGP参数修改流程 1.  在和社区讨论后提出一个提案。除非出现紧急事件,否则激活日期不应该早于这之后的20000个区块。如果是紧急事件,那么激活日期不应早于这之后的1000个区块(500区块用于投票,500个区块用于激活) 2.  将提案发送给DGP框架合约 3.  对提案进行投票 4.  假设投票通过,那么DGP框架将提案数据发送给DGP特性合约 5.  在提案激活的区块上新修改的DGP参数被激活 实际必须的功能 我们将支持多个DGP合约。因此,我们应该实现一个用于支持它们的框架。 每个DGP合约将部署在区块链上,并且它们的地址将硬编码入区块链中。 每个DGP合约将有一个特定的地址,该地址包含了所有用于共识的DGP数据。该数据可以直接从RLP数据库中读取,且不需要运行EVM。每个DGP“记录”将包含2项: 1.  其被激活的区块高度 2.  在特定的区块高度之后使用的新的参数值 这些记录将以数组的形式顺序存储在内存中。还应该有一段数据,包含了数组的总长度。该数据应该在某个标准化了的地方(你可能需要查找Solidity是如何存储数组的来找到这个地方)。 由于DGP的框架实现可以发现存储在RLP中的参数,可以很容易地使用DGP提供的参数来确定当前区块的gas schedule。 注意事项 1.  可以有多个DGP参数记录 2.  参数可以缓存,但是当一个信息发送给DGP合约时该缓存将变得无效(因为参数是可以改变的) 3.  如果DGP记录有一个追溯的区块高度(例如,记录显示它在第500个区块被激活,并且接收到更新时的区块高度为1000),那么它应该立即生效,且不会影响前面的区块。 4.  一定要注意无效的数据。我们应该忽略无效的DGP记录。 5.  对于gas schedule DGP,如果DGP gas schedule记录显示使用的值小于模板参数的默认值 (https://github.com/qtumproject/qtum-dgp/blob/master/gasSchedule-dgp-template.sol.js),那么那个值应该被忽略,并设置为模板参数的值。 6.  旧的DGP数据不应该被覆盖,应该只能附加(append-only)。因此,当新的DGP记录被激活时,没有数据被修改或删除,只有数据被添加进来(除了显示数组长度的整形值)。

以上任务详细阐述了DGP的工作原理和组成部分,并在此基础上提出实现Gas Schedule链上治理的具体方法。Qtum项目组还对未来的扩展性和可能出现的需求进行了相关设计,确保其可以适应不断发展的Qtum生态。事实证明该设计具有很强的预见性,即使从今天看来,该设计也能解决大部分目前存在的区块链链上治理问题。对链上治理有兴趣的读者可以参考《链上治理与智能合约的新方向探讨》。

QTUMCORE-60: Gas Schedule DGP implementation details Description: Below work needs to be done in the submodule, please try to keep it concise and clean, with as little as possible changes to the main code, for example by using functions/classes inside the evm code and put the definitions in a new file. This way we can keep the EVM code clean for future updates. 1- create a new qtum genesis file: libethashseal/genesis/qtumMainNetwork.cpp the file should be based on: libethashseal/genesis/mainNetwork.cpp with below changes: First we change constants name: static dev::h256 const c_genesisStateRootQtumMainNetwork(""); // stateRootHash should be placed here after its computation static std::string const c_genesisInfoQtumMainNetwork = std::string() + then params section: {code:java}        "homsteadForkBlock": "0x0",        "daoHardforkBlock": "0xfffffffffffffff",        "EIP150ForkBlock": "0x0",        "EIP158ForkBlock": "0x0", {code} {code:java}        "registrar": "" {code} also in genesis section: {code:java} "extraData": "0x5174756d4d61696e4e6574c9987fd35877cdbbbb84ffeb5315ab1f86c21398c0", {code} and in accounts section: keep the top 4 accounts (precompiled contracts) and remove all the rest. then we add 5 accounts: all instances of dgp-template.sol.js (https://github.com/qtumproject/qtum-dgp) {code:java} "0000000000000000000000000000000000000080": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000081": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000082": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000083": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000084": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" } {code} those will be used for gas schedule, minimum gas price, and block size ... this story only focuses on the first one. the code should contain the deployed code of the DGP contract (can be obtained using getacocuntinfo, remember to add 0x) the format of storage and possibly code might need to be slightly different, refer to libethereum/Account.cpp lines 75 to 106 for details about the format the objective here is to have both contracts as part of the genesis state and can be called from the rpc ... also don't forget to update c_genesisStateRootQtumMainNetwork with the new stateRootHash, and also the core genesis stateRootHash as well as tests hash 2. related changes (indications) in file: /libethashseal/GenesisInfo.h add qtumMainNetwork = 9,        ///QTUM Homestead + EIP150 + EIP158 Rules active from block 0 to enum class Network in file: /libethashseal/GenesisInfo.cpp add #include "genesis/qtumMainNetwork.cpp" add {code:java} case Network::qtumMainNetwork: return c_genesisInfoQtumMainNetwork; {code} to std::string const& dev::eth::genesisInfo(Network _n) cases and {code:java} case Network::qtumMainNetwork: return c_genesisStateRootQtumMainNetwork; {code} to h256 const& dev::eth::genesisStateRoot(Network _n) cases 3. replace dev::eth::Network::HomesteadTest with dev::eth::Network::qtumMainNetwork in validation.cpp so we use the new genesis for Qtum, as well as anywhere else needed. 4. Before moving forward, make sure the submodule compiles, and the core compiles and passes tests. And that both contracts are callabe with their address 5. Now for the fun part, we need to implement the core functionality of the DGP, which is read Params from the contracts: the way to get params is to read storage from address: 0000000000000000000000000000000000000080 with globalState->storage(addrAccount) or a better solution first we get storage from 0000000000000000000000000000000000000080 and reconstruct paramsHistory array (see DGP contract, reconstruction can be done using existing functions to parse storage or create one) then we create a function that will have a blockNumber as parameter (the function wil do the following): 1. the function will parse the array and return the current address of gas Schedule contract (similar to getParamsForBlock in the solidity contract) a good place to implement this function call is before line 63 of the file: libethcore/SealEngine.cpp in function: EVMSchedule const& SealEngineBase::evmSchedule(EnvInfo const& _envInfo) const let's call this function qtumDGP->getGasSchedule(u256 blockHeight) (let's create a separate class in new files .h, .cpp to manage all DGP future functions) and call it qtumDGP.h qtumDGP.cpp ... if the address returned is not null (!=0000000000000000000000000000000000000000) - the function will then get the storage at the returned address using globalState->storage(scheduleAddress) or a better solution, then parse it to get the gas values for each op, then return the parsed schedule if the address returned is null (=0000000000000000000000000000000000000000) - Use the default hardcoded schedule in the core code, the function will return false and execution will continue as default the first test will return a null address, we then need to deploy dgp-template.sol.js contract, add an admin to 0000000000000000000000000000000000000080 and add a new schedule address (deployed contract address) to 0000000000000000000000000000000000000080 which shall be returned if the function is called again. please check both DGP contracts by deploying them and adding different schedules at different blocks, and analysing storage changes to get an idea how the storage should be parsed. sample main DGP storage: {code:java} "storage": {    "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": {      "0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000005"    },    "45991fce5fc3033a0031207ac65e4c413069a0cd6e6bc7664254f8c4341cfe66": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56a": "0000000000000000000000009c63b222d9db1de070e45802d7fd636cb5da922a"    },    "510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": "00000000000000000000000000000000000000000000000000000000000000ef"    },    "51bdce570797d7347ee2c632abdbe21de6c1cddf1026b9348df268225cf35eed": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b": "00000000000000000000000000000000000000000000000000000000000000f3"    },    "5306a7ea1091503364459f70885dc372117f70834621ea9300aa244571124d0a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e568": "0000000000000000000000008c63b222d9db1de070e45802d7fd636cb5da922a"    },    "63d75db57ae45c3799740c3cd8dcee96a498324843d79ae390adc81d74b52f13": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e565": "00000000000000000000000000000000000000000000000000000000000000f0"    },    "68ebfc8da80bd809b12832608f406ef96007b3a567d97edcfc62f0f6f6a6d8fa": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566": "0000000000000000000000007c63b222d9db1de070e45802d7fd636cb5da922a"    },    "6c13d8c1c5df666ea9ca2a428504a3776c8ca01021c3a1524ca7d765f600979a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564": "0000000000000000000000006c63b222d9db1de070e45802d7fd636cb5da922a"    },    "7b5fcc8f73196524ea5f04c38888c2f09c6cbef411cb31e259d35b56e3d0047b": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56c": "0000000000000000000000001063b222d9db1de070e45802d7fd636cb5da922a"    },    "8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": {      "0000000000000000000000000000000000000000000000000000000000000004": "0000000000000000000000000000000000000000000000000000000000000001"    },    "9c418048a637d1641c6d732dd38174732bbf7b47a1cf6d5f65895384518b07d9": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567": "00000000000000000000000000000000000000000000000000000000000000f1"    },    "aaab8540682e3a537d17674663ea013e92c83fdd69958f314b4521edb3b76f1a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e569": "00000000000000000000000000000000000000000000000000000000000000f2"    },    "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": {      "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001"    },    "b5d9d894133a730aa651ef62d26b0ffa846233c74177a591a4a896adfda97d22": {      "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "0000000000000000000000008c53de5f85032a9cba46ccbdc2b7c2598ec58f42"    },    "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {      "0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"    }  }, {code} you can see that the paramsHistory array elements are detectable: {code:java}    "63d75db57ae45c3799740c3cd8dcee96a498324843d79ae390adc81d74b52f13": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e565": "00000000000000000000000000000000000000000000000000000000000000f0"    },    "68ebfc8da80bd809b12832608f406ef96007b3a567d97edcfc62f0f6f6a6d8fa": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566": "0000000000000000000000007c63b222d9db1de070e45802d7fd636cb5da922a" {code} those shows a paramsHistory array element which is a struct: {code:java}    struct paramsInstance{        uint blockHeight;        address paramsAddress;    } {code} we can see all elements of the array share the same key number, which is incremented by 1 sample schedule state is simpler: {code:java}  "storage": {    "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": {      "0000000000000000000000000000000000000000000000000000000000000000": "0000000a0000000a0000000a0000000a0000000a0000000a0000000a0000000a"    },    "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": {      "0000000000000000000000000000000000000000000000000000000000000002": "00002328000008fc0000002800007d0000000177000000080000017700000001"    },    "8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": {      "0000000000000000000000000000000000000000000000000000000000000004": "00000000ffffffff000000000000001400000014000000140000000300000044"    },    "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": {      "0000000000000000000000000000000000000000000000000000000000000001": "00003a980000138800004e2000000032000000060000001e0000000a0000000a"    },    "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {      "0000000000000000000000000000000000000000000000000000000000000003": "000000040000cf0800005208000000c8000002000000000300005dc0000061a8"    }  }, {code} what we need here is to sort the key, 0 to 4, then parse the gas values which are represented as 4 bytes starting from the right, so above the first gas value is 0000000a, and the 9th is also 0000000a fell free to discuss ideas you might have to implement this better. Task:Gas Schedule DGP实现细节 描述:下面的工作需要在子模块中完成,请尽量保持简洁和整洁,尽可能少地修改主代码,例如使用EVM代码中的函数/类,并将定义放到一个新的文件中。这样我们可以保持EVM代码的整洁,方便以后的更新。 1- 生成新的qtum创世文件:libethashseal/genesis/qtumMainNetwork.cpp 文件应该基于:libethashseal/genesis/mainNetwork.cpp 并进行下述修改: 首先修改常量名称: static dev::h256 const c_genesisStateRootQtumMainNetwork(""); // 在计算 static std::string const c_genesisInfoQtumMainNetwork = std::string() + 之后,stateRootHash应该放在这里 然后,参数部分: {code:java}        "homsteadForkBlock": "0x0",        "daoHardforkBlock": "0xfffffffffffffff",        "EIP150ForkBlock": "0x0",        "EIP158ForkBlock": "0x0", {code} {code:java}        "registrar": "" {code} 创世部分也需要修改: {code:java} "extraData": "0x5174756d4d61696e4e6574c9987fd35877cdbbbb84ffeb5315ab1f86c21398c0", {code} 以及账户部分: 保留顶部的4个账户(预编译的合约)并且删除所有其余账户。 然后我们添加5个账户:dgp-template.sol.js 的所有实例 (https://github.com/qtumproject/qtum-dgp) {code:java} "0000000000000000000000000000000000000080": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000081": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000082": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000083": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" }, "0000000000000000000000000000000000000084": { "code": "0x60606040523615610110576000357c010000000....", "storage": "{"c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {"0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"}}" } {code} 那些将用于gas Schedule,最小的gas价格,以及区块大小……该task仅关注gas schedule。 代码应该包含DGP合约的已部署代码(可以使用getaccountinfo获取,记住要添加0x)。 存储以及代码的格式可能略有不同,关于格式的细节,请参考libethereum/Account.cpp第75至106行。 这里的目的是使两个合约都成为创世状态的一部分,并且可以从RPC调用… 同样不要忘记用新的stateRootHash更新c_genesisStateRootQtumMainNetwork,还有核心的创世stateRootHash以及测试哈希。 2. 相关修改(indications,指示) 在文件:/libethashseal/GenesisInfo.h中 添加qtumMainNetwork = 9,      ///QTUM Homestead + EIP150 + EIP158规则从区块0到enum类网络都有效 在文件:/libethashseal/GenesisInfo.cpp中 添加 #include "genesis/qtumMainNetwork.cpp" 添加 {code:java} case Network::qtumMainNetwork: return c_genesisInfoQtumMainNetwork; {code} to std::string const& dev::eth::genesisInfo(Network _n) cases 和 {code:java} case Network::qtumMainNetwork: return c_genesisStateRootQtumMainNetwork; {code} to h256 const& dev::eth::genesisStateRoot(Network _n) cases 3.在validation.cpp中用dev::eth::Network::qtumMainNetwork替换 dev::eth::Network::HomesteadTest,这样我们可以给Qtum使用新的创世块,以及在其他需要的地方。 4. 在继续之前,确保子模块编译,核心编译并通过测试。并且这两个合约都可以使用它们的地址调用。 5. 现在到了有趣的部分,我们需要实现DGP的核心功能,就是从合约中读取参数: 获取参数的方法是用globalState->storage(addrAccount)或一种更好的方法从地址: 0000000000000000000000000000000000000080中读取存储的信息。 首先从0000000000000000000000000000000000000080获取存储信息,并重建paramsHistory数组(参见DGP合约,可以通过现有的函数解析存储信息或者创建一个来进行重建)。 然后我们创建一个函数,参数为blockNumber(该函数将完成以下功能): 1.  函数将解析数组并返回gas schedule合约的当前地址(和solidity合约中的getParamsForBlock类似) 实现该函数调用的一个较好位置是在下列文件的第63行之前:libethcore/SealEngine.cpp,在函数EVMSchedule const& SealEngineBase::evmSchedule(EnvInfo const& _envInfo) const中。 调用函数qtumDGP->getGasSchedule(u256 blockHeight)(在新的文件.h,.cpp中创建一个单独的类,用于管理所有的DGP未来的函数,并且命名为qtumDGP.h qtumDGP.cpp……) 如果返回的地址不为空 (!=0000000000000000000000000000000000000000) •   该函数将使用globalState->storage(scheduleAddress)或更好的方法来获取返回的地址上存储的信息,然后解析它以获取每个op的gas值,再返回解析的schedule 如果返回的地址为空 (==0000000000000000000000000000000000000000) •   使用默认的硬编码入核心代码中的schedule,该函数将返回false并且默认继续运行。 第一个测试将返回一个空地址,然后我们需要部署dgp-template.sol.js合约,给 0000000000000000000000000000000000000080添加一个admin(管理),并且给0000000000000000000000000000000000000080添加一个新的schedule地址(已部署的合约地址),函数再次被调用时将返回该地址。 通过检查这两个DGP合约来了解存储信息是如何被解析的,检查的方法是:部署这两个DGP合约,在不同区块中添加不同的schedule,并分析存储信息的变化。 main DGP内存示例: {code:java} "storage": {    "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": {      "0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000005"    },    "45991fce5fc3033a0031207ac65e4c413069a0cd6e6bc7664254f8c4341cfe66": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56a": "0000000000000000000000009c63b222d9db1de070e45802d7fd636cb5da922a"    },    "510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": "00000000000000000000000000000000000000000000000000000000000000ef"    },    "51bdce570797d7347ee2c632abdbe21de6c1cddf1026b9348df268225cf35eed": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56b": "00000000000000000000000000000000000000000000000000000000000000f3"    },    "5306a7ea1091503364459f70885dc372117f70834621ea9300aa244571124d0a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e568": "0000000000000000000000008c63b222d9db1de070e45802d7fd636cb5da922a"    },    "63d75db57ae45c3799740c3cd8dcee96a498324843d79ae390adc81d74b52f13": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e565": "00000000000000000000000000000000000000000000000000000000000000f0"    },    "68ebfc8da80bd809b12832608f406ef96007b3a567d97edcfc62f0f6f6a6d8fa": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566": "0000000000000000000000007c63b222d9db1de070e45802d7fd636cb5da922a"    },    "6c13d8c1c5df666ea9ca2a428504a3776c8ca01021c3a1524ca7d765f600979a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564": "0000000000000000000000006c63b222d9db1de070e45802d7fd636cb5da922a"    },    "7b5fcc8f73196524ea5f04c38888c2f09c6cbef411cb31e259d35b56e3d0047b": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56c": "0000000000000000000000001063b222d9db1de070e45802d7fd636cb5da922a"    },    "8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": {      "0000000000000000000000000000000000000000000000000000000000000004": "0000000000000000000000000000000000000000000000000000000000000001"    },    "9c418048a637d1641c6d732dd38174732bbf7b47a1cf6d5f65895384518b07d9": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567": "00000000000000000000000000000000000000000000000000000000000000f1"    },    "aaab8540682e3a537d17674663ea013e92c83fdd69958f314b4521edb3b76f1a": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e569": "00000000000000000000000000000000000000000000000000000000000000f2"    },    "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": {      "0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001"    },    "b5d9d894133a730aa651ef62d26b0ffa846233c74177a591a4a896adfda97d22": {      "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": "0000000000000000000000008c53de5f85032a9cba46ccbdc2b7c2598ec58f42"    },    "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {      "0000000000000000000000000000000000000000000000000000000000000003": "000000000000000000000000000000000000000000000000000000000000001e"    }  }, {code} 可以看到,paramsHistory数组元素是可以检测到的。 {code:java}    "63d75db57ae45c3799740c3cd8dcee96a498324843d79ae390adc81d74b52f13": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e565": "00000000000000000000000000000000000000000000000000000000000000f0"    },    "68ebfc8da80bd809b12832608f406ef96007b3a567d97edcfc62f0f6f6a6d8fa": {      "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566": "0000000000000000000000007c63b222d9db1de070e45802d7fd636cb5da922a" {code} 这些显示了paramsHistory数组元素是一个结构体(struct): {code:java}    struct paramsInstance{        uint blockHeight;        address paramsAddress;    } {code} 我们可以看到,数组中所有的元素具有相同的key number,其增加了1. schedule状态示例更简单: {code:java}  "storage": {    "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563": {      "0000000000000000000000000000000000000000000000000000000000000000": "0000000a0000000a0000000a0000000a0000000a0000000a0000000a0000000a"    },    "405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": {      "0000000000000000000000000000000000000000000000000000000000000002": "00002328000008fc0000002800007d0000000177000000080000017700000001"    },    "8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b": {      "0000000000000000000000000000000000000000000000000000000000000004": "00000000ffffffff000000000000001400000014000000140000000300000044"    },    "b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6": {      "0000000000000000000000000000000000000000000000000000000000000001": "00003a980000138800004e2000000032000000060000001e0000000a0000000a"    },    "c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b": {      "0000000000000000000000000000000000000000000000000000000000000003": "000000040000cf0800005208000000c8000002000000000300005dc0000061a8"    }  }, {code} 这里我们需要做的是将key排序,0到4,然后解析从右边开始的4个字节表示的gas值,因此,上面的第一个gas值是0000000a,,第九个也是0000000a。 如果你有更好的方法实现该功能,可以自由的讨论。

QTUMCORE-64: Create tests for QtumDGP Description: Create tests for QtumDGP Task:对QtumDGP进行测试

上述两个任务进一步详述了Gas Schedule DGP的实现细节,包括代码规范,DGP合约代码,EVM相关修改等,成功从智能合约中读取了gas schedule相关参数。并创建了足够多的测试用例对该功能进行测试,确保上线之后能正常工作。

QTUMCORE-65: blockSize DGP implementation Description: After implementing the base DGP class, we need to add more DGP controlled features, the next one shoule be block size. It should work as follow: replace the blocksize constant by a DGP function in the DGP class that will return the blocksize set in DGP contract for current block height if any, if not return the default value. Block validation should reject blocks higher than max size for the specified height Miner should get the max size when building a block The params template can be found here: https://github.com/qtumproject/qtum-dgp/blob/master/blockSize-dgp-template.sol.js we will use DGP contract at address 0x0000000000000000000000000000000000000081 for blocksize DGP task:区块大小DGP实现 描述:在实现基本的DGP类之后,我们需要添加更多的DGP控制的特性。下一个应该是区块大小。 它的工作方式如下: 在DGP类中用一个DGP函数替换区块大小常数,该DGP函数如果没有返回默认值,则将返回DGP合约为当前区块高度设置的区块大小。 区块验证应该拒绝那些比指定高度的最大值还大的区块。 矿工在构建区块的时候应该获取该最大值。 参数模板可以在以下链接中找到: https://github.com/qtumproject/qtum-dgp/blob/master/blockSize-dgp-template.sol.js 我们将在地址0x0000000000000000000000000000000000000081上使用DGP合约,用于区块大小DGP。

QTUMCORE-66: min gas price DGP implementation Description: For the next DGP, we need to implement min gas price, which will use DGP logic to get the min gas price for height, or else return the minimum (1 satoshi) Transactions not respecting the block DGP min gas price should be rejected in block validation. The miner should also check the transactions it includes are using the min gas price for the block. The params template can be found here: https://github.com/qtumproject/qtum-dgp/blob/master/minGasPrice-dgp-template.sol.js we will use DGP contract at address 0x0000000000000000000000000000000000000082 for min gas price DGP Ttask:最小的gas price DGP实现 描述:下一个DGP,我们要实现最小的gas price,它将使用DGP逻辑来获得区块高度的最小gas price,否则返回最小值(1 satoshi) 在区块验证中,不符合区块DGP最小gas price的交易应该被拒绝。 矿工也应该检查区块所包含的交易是否使用该区块的最小gas price。 参数模板可以在以下链接中找到: https://github.com/qtumproject/qtum-dgp/blob/master/minGasPrice-dgp-template.sol.js 我们将在地址0x0000000000000000000000000000000000000082上使用DGP合约,用于最小的gas price DGP。

在成功实现Gas schedule DGP之后,Qtum项目组又进一步实现了区块大小和gas price的DGP,上述两个任务之后,这两个参数也可以通过DGP实现修改,且无需分叉。

QTUMCORE-83:Add EVM mode for reading DGP values and set it as default Description:This should be done on   QTUMCORE-60 DONE  PR We need to add an extra mode for reading DGP params values, that uses the actual EVM calls instead of parsing storage. The mode should be specified when launching qtumd. So we will have 2 modes: qtumd -dgpevm or -dgpstorage, the default will be -dgpevm When specifying -dgpstorage it should work like it does now When specifying -dgpevm it should use the EVM(callcontract like behaviour) to execute dgp contract's getter functions like: getSchedule(), getMinGasPrice(), getBlockSize() to get the actual values. this should be implemented as part of each dgp function in the dgp class: dev::eth::EVMSchedule QtumDGP::getGasSchedule(unsigned int blockHeight) uint32_t QtumDGP::getBlockSize(unsigned int blockHeight) uint32_t QtumDGP::getMinGasPrice(unsigned int blockHeight) with a switch depending on the mode. Task:添加读取DGP值的EVM模式,并将它设为默认 描述:这应该在【QTUMCORE-60】已经完成。 我们需要添加一个额外的模式用于读取DGP参数值,它使用实际的EVM调用而不是解析存储信息。 该模式需要在qtumd启动时指定。 因此我们将有2种模式:qtumd -dgpevm 或 -dgpstorage,默认的模式将是-dgpevm。 当指定-dgpstorage时,它应该像现在一样工作(即解析存储信息)。 当指定-dgpevm时,它将使用EVM(类似于callcontract的行为)运行DGP合约的getter函数,比如:getSchedule(), getMinGasPrice(), getBlockSize()来获取实际的值。 这应该在DGP类中,作为每个DGP函数的一部分实现: dev::eth::EVMSchedule QtumDGP::getGasSchedule(unsigned int blockHeight) uint32_t QtumDGP::getBlockSize(unsigned int blockHeight) uint32_t QtumDGP::getMinGasPrice(unsigned int blockHeight) 根据模式进行切换。

在上述任务之前,所有DGP管理的参数都是通过解析存储信息获得。然而,实际上这些参数还可以通过本地调用DGP智能合约获取。上述任务实现了通过系统调用获取DGP管理的参数的功能,并将其设为默认模式。用户可以根据需要在启动qtumd时选择何种参数获取模式。

QTUMCORE-84: DGP: Add DGP for blockgaslimit Description: We need to use the DGP contract 0000000000000000000000000000000000000084 to control the blockgaslimit variable, same as we did for minGasPrice Task:DGP:添加blockgaslimit的DGP 描述:我们需要使用DGP合约0000000000000000000000000000000000000084来控制blockgaslimit变量,就像对minGasPrice一样

上述任务采用类似方法,添加了区块gas limit的DGP,使得该参数也可以通过DGP进行调整。

QTUMCORE-85: Add min/max/default values for DGP controlled variables Description: We need hardcoded values for min/max/default values of DGP parameters: blocksize() -min: 500kb -max: 32mb -default: 2mb minGasPrice: -min: 1 -max: 10 000 -default: 1 blockGasLimit: -min: 1 000 000 -max: 1 000 000 000 -default: 40 000 000 gasSchedule: -min: 1\100 the default value for the default schedule (for example sloadGas can only be 200/100=2) with a minimum of 1 (no value can be 0) -max: 1000 time the default value for the default schedule (for example sloadGas can only be 200*1000=200 000) -default: default gas schedule (qtum schedule) task:为DGP控制变量添加最小/最大/默认值 描述:对于DGP参数的最小/最大/默认值,我们需要一些硬编码(即,写死的)的值: blocksize() --最小值:500kb --最大值:32mb --默认值:2mb minGasPrice: --最小值:1 --最大值:10000 --默认值:1 blockGasLimit: --最小值:1000000 --最大值:1000000000 --默认值:40000000 gasSchedule: --最小值:默认的schedule的默认值的1/100(例如,sloadGas只能是200/100=2),最小值为1(不能为0) --最大值:默认schedule的默认值的1000倍(例如,sloadGas只能是200*1000=200000) --默认值:默认的gas schedule(qtum schedule)

从安全性方面考虑,Qtum项目组认为不能无限制地对区块参数进行修改,因此上述任务设定了DGP控制变量的取值范围,分别为blocksize,minGasPrice,blockGasLimit以及gasSchedule设定了对应的最小/最大/默认值。

小结

分布式自治协议(DGP)是Qtum最重要的技术创新之一,目前它被用于区块链基本参数的治理。其未来的应用前景会更加广阔,因为DGP是通过智能合约实现的,并且智能合约本身可以参与治理,这就使得DGP合约本身也可能被治理。在DGP的帮助下,Qtum有望成为首个实现自我认知、自我管理、自我升级的区块链。感兴趣的读者可以在Qtum官方github上找到更多相关内容。

连载:Qtum量子链设计文档(五):分布式自治协议DGP

关注Qtum量子链(qtumchain)公众号,回复关键字查阅Qtum量子链相关资料,以下是部分文档关键字

回复:‘白皮书’,查看《Qtum量子链白皮书,设计原理,实现方案,及应用》

回复:‘未来’,查看《Qtum量子链未来2年技术路线规划-简略版》

回复:‘指南’,查看《首篇Qtum量子链区块链开发指南系列面世》

回复:‘专访’,查看《Nasdaq专访Qtum:区块链会成为世界最大的信任服务商》

回复:‘文档’,查看英文版本《Qtum量子链实现文档》

回复:‘中文文档’,查看中文版本《Qtum量子链实现文档》

连载:Qtum量子链设计文档(五):分布式自治协议DGP

连载:Qtum量子链设计文档(五):分布式自治协议DGP

声明:链世界登载此文仅出于分享区块链知识,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不构成投资建议。投资者据此操作,风险自担。此文如侵犯到您的合法权益,请联系我们kefu@lianshijie.com

参与讨论 (0 人参与讨论)

相关推荐

高承实:区块链的工业革命带来了什么?

高承实:区块链的工业革命带来了什么?

高承实:我们团体主要侧重在区块链底层技术和应用场景落地两个层面。在底层技术方面,我们主要聚焦在以下几个方面,一是密钥的分布式生成和验签技术,二是基于密码算法的底层跨链实现,三是区块链对国产操作系统和国产CPU的适配,四是区块链在体系结构方面对等级保护和可信计算的支持,五是区块链的体系结构设计。在应用场景落地方面,我们重点结合一些典型的应用场景,研究和设计相应的区块链体系结构。这里面包括我们目前正在

中智院曹伟与中智科创谁在创造了谁?中智科创是做什么的?灵动IPFS分布式存储企业

中智院曹伟与中智科创谁在创造了谁?中智科创是做什么的?灵动IPFS分布式存储企业

2019年的深圳中智科创科技赋能APEC 展现深圳人工智能新名片与中智院曹伟有什么关系?灵动IPFS分布式存储企业应于任何企业 由工业和信息化部支持,深圳市的人民政府、工业和信息化部中小企业发展促进中心、中国中小企业国际合作协会、中国民营经济研究会共同主办的2019年在APEC中小企业工商论坛在深圳隆重开幕。中智院-曹伟-灵动分布式存储本次论坛以"创新创业创意发展,合作共赢共享未来"为主题,吸引了

​Web3.0中国峰会暨分布式存储生态大会到底密谋着什么?灵动IPFS分布式存储企业

​Web3.0中国峰会暨分布式存储生态大会到底密谋着什么?灵动IPFS分布式存储企业

在2020年11月12日,Web3.0中国峰会暨分布式存储在生态大会是深圳的会展中心的隆重展开开幕仪式,作为"2020中国国际高新技术成果交易会"重要组成部分,除活动的自带流量以外,同时共享着高交会来自智能科技、互联网、物联网、人工智能、大数据、新基建等行业在超40万名的专业观众,加强分布式存储在WEB 3.0中的技术应用、生态链、场景落地及广泛交流合作提供强有力的合作平台。灵动IPFS分布式云存

Bebt交易所:深入分析自动化做市商发展掣肘与设计改进

Bebt交易所:深入分析自动化做市商发展掣肘与设计改进

在基于区块链的分布式系统 (如 Ethereum) 上重构一个新的金融世界时,必须认识到区块链世界与链下世界相比,有着完全不同的动态属性。最值得注意的是,链上并非连续计时,而是通过区块来量化时间的流逝。但因为它受到区块大小的限制,这又导致了延迟问题和计算能力的限制。由于这些结构上的差异,分布式金融的设计者应该具有与中心化世界的设计者完全不同的思路。例如,由于区块链的成本和技术基础设施,做市商在基于

Keystore旗下有Keypool与BMW真的合作吗?灵动IPFS/Filecoin分布式存储服务技术

Keystore旗下有Keypool与BMW真的合作吗?灵动IPFS/Filecoin分布式存储服务技术

BMW与Keystore作为了战略合作,推出了ipfs领域去中心化金融聚合器BMWP 发布时间:10-2811:47金色财经官方帐号BMW官网昨天公布了一条最新的讯息动态,DeFi+IPFS,下一个风口的BMWP了解之下?结合了昨天的BMW全国助力行在了常州公布的消息,基于在BMW打造的去中心化的金融聚合器BMWP将于近期发布,从各个的方面信息进行表明BMWP,将会是在今年的两大热点板块DeFi、

中科院与正舵者的秘密会议?灵动IPFS/Filecoin分布式存储

中科院与正舵者的秘密会议?灵动IPFS/Filecoin分布式存储

中科院与正舵者——高性能区块链技术联合实验室亮相高交会灵动IPFS/Filecoin分布式存储技术性服务企业,经历数年之久的分布式存储专业性技术。正舵者-灵动IPFS分布式存储 发布时间:11-1117:1111月11日,在第二十二届的中国的国际高新技术成果交易是会以"科技改变生活,创新驱动发展"为这样的主题,在深圳的会展中心进行隆重的开幕,正舵者和中国科学院深圳的先进技术研究院进行了共同建立的"

雅典娜矿池(RRmine.com)黑科技揭秘(二)C2云化,GPU的最佳使用姿势

所以,小编决定这篇文章换个思路,尽量简单明了地把雅典娜矿池的Commit2云化思路,给大家介绍清楚。

谁是更好的去中心化云存储?Filecoin vs Storj设计要点和实测PK

而真正能算得上在去中心化云存储赛道主战场上正面PK的重量级选手,其实只有Filecoin和Storj。

麦妖榜
更新日期 2019-09-03
排名用户贡献值
1牛市来了30910
2BitettFan24187
3等待的宿命23810
4区块大康20369
5六叶树20310
6linjm122719429
7天下无双16192
8lizhen00215280
9让时间淡忘14586
10yelanyi050511349
返回顶部 ↑