× Smart stable coin NDAO

钻石标准简要分析与及改进(补充)

  • User_30dcdfc2
  • Topic Author
  • 新手上路
  • 新手上路
More
2 weeks 20 hours ago - 2 weeks 19 hours ago #62 by User_30dcdfc2
三部分内容:
  1. 对钻石合约核心部分的简要分析
  2. 对solidity合约的slot(storage)简要分析
  3. 基于钻石标准,提出新标准
  • 对钻石合约核心部分的简要分析
  • 核心函数diamondCut:
function diamondCut(bytes[] memory _diamondCut) internal {
        DiamondStorage storage ds = diamondStorage();
        SlotInfo memory slot;
        slot.originalSelectorSlotsLength = ds.selectorSlotsLength;
        uint selectorSlotsLength = uint128(slot.originalSelectorSlotsLength);
        uint selectorSlotLength = uint128(slot.originalSelectorSlotsLength >> 128);
        if(selectorSlotLength > 0) {
            slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
        }
        // loop through diamond cut
        for(uint diamondCutIndex; diamondCutIndex < _diamondCut.length; diamondCutIndex++) {
            bytes memory facetCut = _diamondCut[diamondCutIndex];
            require(facetCut.length > 20, "Missing facet or selector info.");
            bytes32 currentSlot;
            assembly {
                currentSlot := mload(add(facetCut,32))
            }
            bytes32 newFacet = bytes20(currentSlot);
            uint numSelectors = (facetCut.length - 20) / 4;
            uint position = 52;

            // adding or replacing functions
            if(newFacet != 0) {                
                // add and replace selectors
                for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
                    bytes4 selector;
                    assembly {
                        selector := mload(add(facetCut,position))
                    }
                    position += 4;
                    bytes32 oldFacet = ds.facets[selector];
                    // add
                    if(oldFacet == 0) {
                        // update the last slot at then end of the function
                        slot.updateLastSlot = true;
                        ds.facets[selector] = newFacet | bytes32(selectorSlotLength) << 64 | bytes32(selectorSlotsLength);
                        // clear selector position in slot and add selector
                        slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorSlotLength * 32) | bytes32(selector) >> selectorSlotLength * 32;
                        selectorSlotLength++;
                        // if slot is full then write it to storage
                        if(selectorSlotLength == 8) {
                            ds.selectorSlots[selectorSlotsLength] = slot.selectorSlot;
                            slot.selectorSlot = 0;
                            selectorSlotLength = 0;
                            selectorSlotsLength++;
                        }
                    }
                    // replace
                    else {
                        require(bytes20(oldFacet) != bytes20(newFacet), "Function cut to same facet.");
                        // replace old facet address
                        ds.facets[selector] = oldFacet & CLEAR_ADDRESS_MASK | newFacet;
                    }
                }
            }
            // remove functions
            else {
                slot.updateLastSlot = true;
                for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
                    bytes4 selector;
                    assembly {
                        selector := mload(add(facetCut,position))
                    }
                    position += 4;
                    bytes32 oldFacet = ds.facets[selector];
                    require(oldFacet != 0, "Function doesn't exist. Can't remove.");
                    // Current slot is empty so get the slot before it
                    if(slot.selectorSlot == 0) {
                        selectorSlotsLength--;
                        slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
                        selectorSlotLength = 8;
                    }
                    slot.oldSelectorSlotsIndex = uint64(uint(oldFacet));
                    slot.oldSelectorSlotIndex = uint32(uint(oldFacet >> 64));
                    // gets the last selector in the slot
                    bytes4 lastSelector = bytes4(slot.selectorSlot << (selectorSlotLength-1) * 32);
                    if(slot.oldSelectorSlotsIndex != selectorSlotsLength) {
                        slot.oldSelectorSlot = ds.selectorSlots[slot.oldSelectorSlotsIndex];
                        // clears the selector we are deleting and puts the last selector in its place.
                        slot.oldSelectorSlot = slot.oldSelectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
                        // update storage with the modified slot
                        ds.selectorSlots[slot.oldSelectorSlotsIndex] = slot.oldSelectorSlot;
                        selectorSlotLength--;
                    }
                    else {
                        // clears the selector we are deleting and puts the last selector in its place.
                        slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
                        selectorSlotLength--;                        
                    }
                    if(selectorSlotLength == 0) {
                        delete ds.selectorSlots[selectorSlotsLength];
                        slot.selectorSlot = 0;                        
                    }
                    if(lastSelector != selector) {
                        // update last selector slot position info
                        ds.facets[lastSelector] = oldFacet & CLEAR_ADDRESS_MASK | bytes20(ds.facets[lastSelector]);
                    }
                    delete ds.facets[selector];
                }
            }
        }
首先,diamondStorage(),这个函数返回结构体DiamondStorage的storage.通过手动分配slot来实现:
function diamondStorage() internal pure returns(DiamondStorage storage ds) {
        bytes32 position = DIAMOND_STORAGE_POSITION;
        assembly { ds.slot := position }
    }
DIAMOND_STORAGE_POSITION是手动分配的slot:
bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
注意 constant变量不占据任何slot,只在代码中有效。
我们先分析一下两个结构体所代表的含义:
struct DiamondStorage {

        // owner of the contract
        address contractOwner;

        // maps function selectors to the facets that execute the functions.
        // and maps the selectors to the slot in the selectorSlots array.
        // and maps the selectors to the position in the slot.
        // func selector => address facet, uint32 slotIndex, uint64 slotsIndex
        mapping(bytes4 => bytes32) facets;

        // array of slots of function selectors.
        // each slot holds 8 function selectors.
        mapping(uint => bytes32) selectorSlots;

        // uint32 selectorSlotLength, uint32 selectorSlotsLength
        // selectorSlotsLength is the number of 32-byte slots in selectorSlots.
        // selectorSlotLength is the number of selectors in the last slot of
        // selectorSlots.
        uint selectorSlotsLength;

        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
    }
该结构体主要看三个元素:facets,selectorSlots,selectorSlotsLength:
facets:每个元素大小32字节,索引bytes4是函数选择器。元素组成:函数选择器地址(20字节). 函数选择器所在插槽内偏移 (4字节). 函数选择器所在插槽 (8字节)
selectorSlots:32字节插槽。索引uint是插槽索引。bytes32是表示该插槽的内容.内容组成:8个函数选择器(8*4) 故而偏移最大值为8.
selectorSlotsLength:表示目前函数选择器的数量。由两个参数合成,一个是插槽数目:在diamondCut函数用selectorSlotsLength表示,低128位表示其值。另一个是最新函数选择器在最新插槽内的偏移:在diamondCut函数中用selectorSlotLength表示,高128位表示其值。
struct SlotInfo {
        uint originalSelectorSlotsLength;
        bytes32 selectorSlot;
        uint oldSelectorSlotsIndex;
        uint oldSelectorSlotIndex;
        bytes32 oldSelectorSlot;
        bool updateLastSlot;
    }
该结构体中包含了两种内容,也正好符合diamondCut函数的两个功能:增加/删除/替换函数选择器。
originalSelectorSlotsLength:与上一个结构体之中的 selectorSlotsLength元素一致。
selectorSlot:最新插槽的内容
oldSelectorSlotsIndex:被删除的函数选择器所在的插槽
oldSelectorSlotIndex:被删除的函数选择器的槽内偏移
oldSelectorSlot:被删除的函数选择器所在槽的内容
updateLastSlot:在实现删除/增加功能时候记为true。
DiamondStorage storage ds = diamondStorage();
        SlotInfo memory slot;
        slot.originalSelectorSlotsLength = ds.selectorSlotsLength;
        uint selectorSlotsLength = uint128(slot.originalSelectorSlotsLength);
        uint selectorSlotLength = uint128(slot.originalSelectorSlotsLength >> 128);
        if(selectorSlotLength > 0) {
            slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
        }
初始化slot.
for(uint diamondCutIndex; diamondCutIndex < _diamondCut.length; diamondCutIndex++)
开始for循环检索传过来的每一个函数选择器,其中传过来的参数(一个bytes[])的元素组成是这样子的:
cut[1] = abi.encodePacked(
            diamondLoupeFacet,
            IDiamondLoupe.facetFunctionSelectors.selector,
            IDiamondLoupe.facets.selector,
            IDiamondLoupe.facetAddress.selector,
            IDiamondLoupe.facetAddresses.selector
        );
就参数结构来说,是20+4*n的结构。理解了该结构,就不难理解其该段代码用意:
bytes memory facetCut = _diamondCut[diamondCutIndex];
            require(facetCut.length > 20, "Missing facet or selector info.");
            bytes32 currentSlot;
            assembly {
                currentSlot := mload(add(facetCut,32))
            }
            bytes32 newFacet = bytes20(currentSlot);
            uint numSelectors = (facetCut.length - 20) / 4;
            uint position = 52;
那一段汇编(assembly {
currentSlot := mload(add(facetCut,32))
})的目的是不加载长度信息。所以currectSlot加载的是前32字节的内容。用于提取函数选择器的地址,如果地址为0表示删除,否则增加/替换函数选择器。
那么接下来简要分析一下其增加/替换/删除:
增加/替换:
// add and replace selectors
                for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
                    bytes4 selector;
                    assembly {
                        selector := mload(add(facetCut,position))
                    }
                    position += 4;
                    bytes32 oldFacet = ds.facets[selector];
                    // add
                    if(oldFacet == 0) {
                        // update the last slot at then end of the function
                        slot.updateLastSlot = true;
                        ds.facets[selector] = newFacet | bytes32(selectorSlotLength) << 64 | bytes32(selectorSlotsLength);
                        // clear selector position in slot and add selector
                        slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorSlotLength * 32) | bytes32(selector) >> selectorSlotLength * 32;
                        selectorSlotLength++;
                        // if slot is full then write it to storage
                        if(selectorSlotLength == 8) {
                            ds.selectorSlots[selectorSlotsLength] = slot.selectorSlot;
                            slot.selectorSlot = 0;
                            selectorSlotLength = 0;
                            selectorSlotsLength++;
                        }
                    }
                    // replace
                    else {
                        require(bytes20(oldFacet) != bytes20(newFacet), "Function cut to same facet.");
                        // replace old facet address
                        ds.facets[selector] = oldFacet & CLEAR_ADDRESS_MASK | newFacet;
                    }
                }
首先是加载信息,利用循环不断检索每一个函数选择器。然后从bytes32 oldFacet = ds.facets[selector];的olaFacet来判断是否增加/替换。如果是0表示增加,反之则是替换。增加函数选择器则是对三个元素动手:facets selectorSlotLength(满8进1)selectorSlot。对于facets与及selectorSlot更新的主要手段就是拼接,其中~(CLEAR_SELECTOR_MASK >> selectorSlotLength * 32)的目的是清除函数选择器拼接位的内容,全部置0。替换直接修改facets即可。
删除:
slot.updateLastSlot = true;
                for(uint selectorIndex; selectorIndex < numSelectors; selectorIndex++) {
                    bytes4 selector;
                    assembly {
                        selector := mload(add(facetCut,position))
                    }
                    position += 4;
                    bytes32 oldFacet = ds.facets[selector];
                    require(oldFacet != 0, "Function doesn't exist. Can't remove.");
                    // Current slot is empty so get the slot before it
                    if(slot.selectorSlot == 0) {
                        selectorSlotsLength--;
                        slot.selectorSlot = ds.selectorSlots[selectorSlotsLength];
                        selectorSlotLength = 8;
                    }
                    slot.oldSelectorSlotsIndex = uint64(uint(oldFacet));
                    slot.oldSelectorSlotIndex = uint32(uint(oldFacet >> 64));
                    // gets the last selector in the slot
                    bytes4 lastSelector = bytes4(slot.selectorSlot << (selectorSlotLength-1) * 32);
                    if(slot.oldSelectorSlotsIndex != selectorSlotsLength) {
                        slot.oldSelectorSlot = ds.selectorSlots[slot.oldSelectorSlotsIndex];
                        // clears the selector we are deleting and puts the last selector in its place.
                        slot.oldSelectorSlot = slot.oldSelectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
                        // update storage with the modified slot
                        ds.selectorSlots[slot.oldSelectorSlotsIndex] = slot.oldSelectorSlot;
                        selectorSlotLength--;
                    }
                    else {
                        // clears the selector we are deleting and puts the last selector in its place.
                        slot.selectorSlot = slot.selectorSlot & ~(CLEAR_SELECTOR_MASK >> slot.oldSelectorSlotIndex * 32) | bytes32(lastSelector) >> slot.oldSelectorSlotIndex * 32;
                        selectorSlotLength--;                        
                    }
                    if(selectorSlotLength == 0) {
                        delete ds.selectorSlots[selectorSlotsLength];
                        slot.selectorSlot = 0;                        
                    }
                    if(lastSelector != selector) {
                        // update last selector slot position info
                        ds.facets[lastSelector] = oldFacet & CLEAR_ADDRESS_MASK | bytes20(ds.facets[lastSelector]);
                    }
                    delete ds.facets[selector];
                }
删除是比较复杂的,通过不断的检索函数选择器进行删除操作,其核心就是最新的函数选择器替换被删除的函数选择器,函数选择器的数量减1.。删除操作分为两个部分,即是考虑删除的函数选择器是否在最新的插槽当中,如果不是,那么需要额外的操作:
ds.selectorSlots[slot.oldSelectorSlotsIndex] = slot.oldSelectorSlot;
把替换之后的被删除函数选择器所在的插槽内容重新赋值。
如果是,直接对slot.selectorSlot进行修改即可(因为在收尾部分,会赋值给DiamondStorage的最新插槽)
另外,facets selectorSlotsLength selectorSlotLength 这些逻辑也是比较明显的。
没有清除最新函数选择器的数据,只是长度减1(除非长度减1出现借位)
  • 对solidity合约的slot(storage)简要分析
  • 第一种是常规变量(基本类型):这一类的储存机制是按照类似于紧打包的方式。比如有两个变量,连续声明,uint128 与及 uint8那么128+8<256,即它们可以用一个32字节的slot来储存,故而它们在同一个插槽,uint8的offset是16。为什么是16呢?是这样的,一个单位offset相当于1个字节,128占据16个字节。而且其编码方式是右对齐,即uint128右对齐,uint8从第128位开始储存。在solidity里面的单位最小是字节。这一类包括除动态数组,mapping,bytes,string之外的类型,包括结构体(结构体就是紧打包编码方式的,而且占据整数个slot,即不会与上述一样与其他变量共享某一个slot)。
    第二种是bytes与及string。该两种具备变长数组的性质:没有固定的存储上限(除了14kb),但是在赋值方面是不具备数组的特性,因为两者没有下标,有下标的数组可以通过memory来赋值。听说sstore与及sload函数只可以对一个插槽进行操作,而对于这两个是不起作用的。同时两者的长度数据以两种不同的方式存储于插槽当中,第一种是当小于31字节时候,在插槽p(分配到的插槽)进行如下写入:31字节扩展的内容(向下零扩展),第0字节则(即最右边的那个)写入length*2。第二种是大于32字节的时候,插槽p写入length*2+1,keccak256(p)处的插槽开始写入内容(内容占多个插槽)。如果直接对两者进行sstore来赋值,是需要实现以上细节的,但是利用string[1]就可以避免以上细节。
    第三种是不定长数组。这种包括type[],与及mapping,这种其分配到的插槽是存储长度的,mapping则是一个空值。假设分配到的插槽是p,那么其数组数据的存储于keccak256(p)的插槽,注意其是连续存储,对于非string[]而言,直接存储其值(结构体也是一样的,不过结构体是紧打包的且占据整数个插槽),对于string而已,当然是需要按照第二种介绍那样进行存储。在对数组元素赋值的时候,这些都是封装好的。对于mapping来说,其存储插槽则是keccack(p.key),其中.是表示连接符(个人认为应该是类似于string连接一样的),多重mapping可以迭代。在这里,目前不允许创建空的mapping storage变量(先创建后赋值也不行),不过这里可以通过internal函数来解决,直接利用函数返回renturn mapping storage即可。
    solidity分配slot规则:分配规则与你的变量声明顺序息息相关。比如以下:
    struct ygyg{
            uint256 ks;
            uint256 ii;
            string kk;
        }
        uint256 public uinttest;
        string public stringtest = "菜cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
        bytes32 public bytes32test;
        address[10] public ssdd;
        address[] public ssfff;
        string[] public testStringA;
        mapping(uint256=>string) public testMapString;
        string[1] public testStringAD;
        bytes4 public bytes4test;
        ygyg public testStruct;
        uint128 public First128;
        uint8 public Second8;
        uint8 public Three8;
        uint8 public Four8;
        uint128 public Second128;
    其插槽分配结果为:
    nameslotoffset
    uinttest00
    stringtest10
    bytes32test/td]20
    ssdd30
    ssfff130
    testStringA140
    testMapString150
    testStringAD160
    bytes4test170
    testStruct180
    First128210
    Second82116
    Three82117
    Four82118
    Second128220
    定长数组的长度没有储存在storage当中,而是仅仅存在于代码当中,和constant差不多。
    以下是笔者小小测试用的代码:
    pragma solidity ^0.6.0;
    
    
    contract testSlotALL{
        struct ygyg{
            uint256 ks;
            uint256 ii;
            string kk;
        }
        uint256 public uinttest;
        string public stringtest = "菜cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
        bytes32 public bytes32test;
        address[10] public ssdd;
        address[] public ssfff;
        string[] public testStringA;
        mapping(uint256=>string) public testMapString;
        string[1] public testStringAD;
        bytes4 public bytes4test;
        ygyg public testStruct;
        uint128 public First128;
        uint8 public Second8;
        uint8 public Three8;
        uint8 public Four8;
        uint128 public Second128;
        
        
        function getslot() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := uinttest_slot
                     off := uinttest_offset
            }
        }
        
        function getslot1() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := stringtest_slot
                     off := stringtest_offset
            }
        }
        
        function getslot2() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := bytes32test_slot
                     off := bytes32test_offset
            }
        }
        
        function getslot4() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := First128_slot
                     off := First128_offset
            }
        }
        
        function getslot3() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := bytes4test_slot
                     off := bytes4test_offset
            }
        }
        
        function getslot5() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := Second128_slot
                     off := Second128_offset
            }
        }
        
        function getslot6() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := Second8_slot
                     off := Second8_offset
            }
        }
        
        function getslot7() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := Three8_slot
                     off := Three8_offset
            }
        }
        
        function getslot8() external pure returns(uint256 ssd,uint256 off){
            assembly{ssd := Four8_slot
                     off := Four8_offset
            }
        }
        
        function sstoreslot() external {
            uint256 s = 234;
            assembly {sstore(uinttest_slot,s)}
        }
        
        function sloadslot() external view returns(uint256) {
            uint256 s = 234;
            assembly {s := sload(uinttest_slot)}
            return s;
        }
        
        function testS() external {
            uint256 s = 9;
            uint256 v = 888;
            assembly{ sstore(s,v)}
        }
        
        function testL() external view returns(uint256){
            uint256 V ;
            assembly{V := sload(9)}
            return V;
        }
        
        function testMapping() external{
            address[11] storage testdin;
            assembly{testdin_slot := ssdd_slot}
            testdin[10] = 0x783CAbA0EE1bC2b331bCac806bbDC7A62859Ed4c;
            uint256 uyu = testdin.length;
            assembly{uyu := sload(add(ssdd_slot,10))}
            assembly{sstore(uinttest_slot,uyu)}
            bytes32 ffff = bytes32(uinttest);
            assembly{sstore(bytes32test_slot,ffff)}
            uyu = ssfff.length;
            assembly{sstore(uinttest_slot,uyu)}
            string memory oo = "oooo";
            bytes memory ssdddd = bytes(oo);
        }
        
        function teststring() external view returns(string memory ) {
            string storage testStr1 ;
            //bytes memory testbytes1 = bytes(testStr1);
            assembly{testStr1_slot := stringtest_slot}
            //string memory demostr = "ooooooooooo";
            uint256 demostr;
            assembly{demostr := stringtest_slot}
            bytes memory sour = abi.encodePacked(demostr);
            bytes32 position = keccak256(sour);
            bytes memory taker;
           // assembly{sstore(stringtest_slot,demostr)}
            //assembly{taker := sload(position)}
            //string memory strTest = string(abi.encodePacked(taker));
            string memory strTest = testStr1;
            //demostr = stringtest;
            return strTest;
        }
        
        function testString1() external {
            string memory demostr = "ooooooooooo";
            string storage testStr1 ;
            //bytes memory testbytes1 = bytes(testStr1);
            assembly{testStr1_slot := stringtest_slot}
            //testStr1.push(demostr);
        }
        
        function testStrA() external {
            string[] storage testSA;
            assembly{testSA_slot := testStringA_slot}
            //string memory ssddd = ";;;;";
            //uint256 ssd = testSA[0].length;
            testSA.push("iiii");
        }
        
        function testMapString0() external{
            mapping(uint256=>string) storage mapstr = MappUS();
            assembly{mapstr_slot := testMapString_slot}
            mapstr[0] = "pp";
        }
        
        function testMapString1() external view returns(uint256){
            mapping(uint256=>string) storage mapstr = MappUS();
            assembly{mapstr_slot := testMapString_slot}
            return 1;
            //return mapstr[0].length;
        }
        
        function MappUS() internal view returns(mapping(uint256=>string) storage ds){
            assembly{ds_slot := testMapString_slot}
        }
        
        function testStringAD0() external {
            string[1] storage testSA;
            assembly{testSA_slot := testStringAD_slot}
            //string memory ssddd = ";;;;";
            //uint256 ssd = testSA[0].length;
            testSA[0] = "iiii";
        }
        
        function test128() external{
            uint128 test128_ = 100;
            assembly{ sstore(18,test128_)}
        }
        
        function get128() external view returns(bytes32 re){
            assembly{re := sload(18)}
        }
    }
  • 基于钻石标准,提出新标准
  • 该标准主要实现以下功能:
    1 自由添加变量(但不允许添加长度大于1的数组与及bytes,string,后面两个类型可以用string[1]代替。)同样可以删除,但是不应该设计有删除数据的功能。
    2 自由创建任意的结构体(在结构体内部可以直接使用bytes string)
    3 自由添加功能(保留钻石合约内容)
    4 实现完全升级换代
    但是有以下风险:
    1 开发者对slot有一定了解,不至于storage冲突/覆盖/干扰
    2 对于更新功能的审查,尤其是涉及到赋值操作,防止溢出以及出现不可预料的修改结果。
    3 keccack256函数保证不同的内容,加密结果是不同的,即不会发生碰撞。这个就目前以太坊的情况来说,还没有出现该种情况,同时概率几乎为0。
    合约组成:
    1.diamond标准的合约(我已经改写成0.6.0版本)。
    2.database.sol:分配slot与及记录slot 在非结构体类型用顺序分配,结构体类型用函数keccak256函数分配。
    3.functions: 用于添加新功能/数据类型,有三个文件 power.sol(功能,继承后两个)struct.sol(定义结构体),mapping.sol定义返回mapping storage的函数)
    4.main.sol:中心控制台,数据储存的合约,随意添加删减功能的合约。
    合约内容:
    database.sol:
    pragma solidity ^0.6.0;
    import "./lib/Ownable.sol"
    
    //该合约是用于分配slot,但是我们开发者是没有权利去删除别人的数据的,所以在设计上不涉及删除函数
    //该合约不作继承,为一个独立的合约
    contract database is Ownable{
        //Slot的规则如下
        // name.type  是否应该添加一个申请人在其后表明申明地址,或者直接添加一个mapping记录
        //name是变量名称,type是变量类型。比如DAism.mappingUintAddress表示 mapping(uint256 => address) DAism
        //string[1]代替string bytes类型,不支持其他的定长数组。所以所有的变量初始分配的Slot数量是1
        //uint256是分配的Slot
        //布尔变量是否支持?都可以的,我们的分配是直接分配一个插槽,而没有像solidity那样有紧打包的现象。虽然会造成空间浪费
        //所以建议把32字节用满
        mapping(string => uint256) public Slot;
        //Slot大小
        uint256 public slotNum;
    
        //认证的功能合约
        address[] public updateContract;
        //方便认证
        mapping(address => bool) public isUpdate;
        //方便删除
        mapping(address => uint256) public toIndex;
    
        //之所以从1开始,是因为main合约需要记录database的地址
        constructor() public{
            slotNum = 1;
        }
    
        modifier IsUpdate(){
            require(isUpdate[msg.sender],"not a update address");
            _;
        }
    
        function addUpdate(address _add) external onlyOwner{
            require(!isUpdate[_add]);
            updateContract.push(_add);
            toIndex[_add] = updateContract.length;
        }
    
        function delUpdate(address _del) external onlyOwner IsUpdate{
            updateContract[toIndex[_del]-1] = updateContract[updateContract.length-1];
            updateContract.pop();
        }
    
        function setSlot(string memory _index) external IsUpdate{
            require(Slot[_index] == 0, "has been used");
            Slot[_index] = slotnum;
            slotnum++;
        }
    
        function setStruct(string memory _index) external IsUpdate{
            require(Slot[_index] == 0, "has been used");
            Slot[_index] = keccak256(_index);
        }
        
        function getSlot(string memory _index) external view returns(uint256){
            return Slot[_index];
        }
    }
    main.sol:
    pragma solidity ^0.6.0;
    import "https://github.com/1999321/diamond0.6/blob/master/Diamond.sol";
    
    
    //该函数是继承diamond的,还有一个功能,就是记录database地址,做功能模块和数据库模块的桥梁
    //考虑一下,在什么时候为功能函数申请数据库空间
    contract main is Diamond{
        address public database;
    
        constructor(address _database) Diamond(msg.sender) public{
            database = _database;
        }
    }
    power.sol:
    pragma solidity ^0.6.0;
    import "./struct.sol"
    
    interface database{
        mapping(string => uint256) public Slot;
    }
    interface main{
        address public databaseAddress;
    }
    contract Power is Structs,MappingNM{
        
    }
    mapping.sol:
    pragma solidity ^0.6.0;
    
    
    contract MappingNM{
        function mappingUintAddress(bytes32 position) internal view returns
            {
                mapping(uint256 => address) storage ds
            }
        {
            assembly{ ds_slot := position };
        }
    }
    struct.sol:
    pragma solidity ^0.6.0;
    
    
    contract Structs {
        struct example1{
            uint256 example11;
        }
    
        struct example2{
            uint256 example21;
        }
    }
    以上5份代码没进行编译。
    问题:
    1.新功能附带其定义的数据类型上线,在什么时候如何有效快速方便地去database合约申请地址?
    2.sstore与及sload消耗的gas如何?
    3.如何有效检查代码申请的数据/slot是其需要的数据,而不会无意中/有意使用/篡改别人的数据?
    4.如何防止有人利用struct的申请动态数组/mapping占据的内存(即将struct的名字改成某特殊名字可以把内存指向某动态数组/mapping指向的内存位置)(动态数组和mapping利用keccak256指向内存的时候,其keccak256的参数都会包含数字)
    [/ul]
    Last edit: 2 weeks 19 hours ago by User_30dcdfc2.
    The following user(s) said Thank You: User_4ac被盗

    Please Log in to join the conversation.

    • User_4ac被盗
    • User_4ac被盗's Avatar
    • 版主
    • 版主
    More
    2 weeks 19 hours ago #63 by User_4ac被盗
    Replied by User_4ac被盗 on topic 钻石标准简要分析与及改进(补充)
    新标准是补充进钻石标准,还是另发起一个EIP,每个合约开发或审核者请给一个明确的意见!

    Please Log in to join the conversation.

    • User_0a45efc6
    • 新手上路
    • 新手上路
    More
    2 weeks 5 hours ago #64 by User_0a45efc6
    Replied by User_0a45efc6 on topic 钻石标准简要分析与及改进(补充)
    几个问题:

    1 “selectorSlotsLength:表示目前函数选择器的数量。” ---这里是否应该是槽的数量?原文为“selectorSlotsLength is the number of 32-byte slots in selectorSlots”

    2 “自由添加变量”和“自由创建任意的结构体”是指充分利用槽(slot)是吗?

    3 “自由添加功能”---这里所谓的自由添加功能是不是指由于实现了“自由添加变量”和“自由创建任意结构体”使得添加的功能函数可以带更加自由定义的参数,从而可以更灵活的定义函数和功能?

    Please Log in to join the conversation.

    • User_30dcdfc2
    • Topic Author
    • 新手上路
    • 新手上路
    More
    2 weeks 3 hours ago #65 by User_30dcdfc2
    Replied by User_30dcdfc2 on topic 钻石标准简要分析与及改进(补充)
    1.原文解析:selectorSlotsLength is the number of 32-byte slots in selectorSlots是在使用这个参数的时候的注释,并非对该参数的注释。该注释提及的selectorSlotsLength与及selectorSlotsLength均在diamondCut函数当中有使用,两个的数据来源就是对DiamondStorage 中元素selectorSlotsLength的拆分,低128位是selectorSlotsLength,即槽的数量。高128位表示selectorSlotLength,即最新插槽当中已经使用的部分(即使用偏移量)。该元素在数值上不是代表函数选择器的数量,而是在经过拆分成两部分之后,拆分后数据可以表示函数选择器的数量。
    2.自由添加变量/自由创建结构体 是指在主体合约部署之后,你可以在未来部署的合约当中,如果该合约需要添加/创建新的变量/结构体,是完全可以通过分配slot来实现的。另外,该方法的最小单位的一个插槽,即32字节,而solidity分配的最小单位是1个字节(因为我怕抛弃了offset)。不过这对于结构体,数组,mapping,uint256,string,bytes32是没有任何影响的(即不会消耗更多的内存)。只会在使用uintN,bytesN,bool变量并出现紧打包储存的时候(即假如现在有uint128,uint256,uint128,此时不会出现紧打包储存,因为第一个uint128不会和uint256在同一个插槽当中【空间不足】,第二个uint128不会和uint256在同一个插槽【空间不足】,总共耗3个插槽。但是如果按照uint128,uint128,uint256的方式打包,两个uint128可以在同一个插槽,总共使用2个插槽,这时候相对于紧打包的方式储存),该分配方法会较solidity耗slot。总体利用率稍逊色于solidity的分配slot机制。
    3 是的,添加的功能可以附带自己定义的变量/结构体,使得其操作性不仅仅局限于初始通过声明变量方式的变量们。

    Please Log in to join the conversation.

    • User_0a45efc6
    • 新手上路
    • 新手上路
    More
    1 week 6 days ago #66 by User_0a45efc6
    Replied by User_0a45efc6 on topic 钻石标准简要分析与及改进(补充)
    我建议把它做成一个新的EIP。这个对钻石标准是个挺有用的扩展,实际上就算没有钻石标准,它在某种程度上也可以看成是一个独立的EIP

    Please Log in to join the conversation.