在 Solidity 中初始化一个固定长度的大数组

Initialize a big fixed length array in Solidity

我的第一个项目是在以太坊上开发一款游戏,我面临着存储和气体限制。我想在区块链上存储一个存储智能合约,以便在部署后查询。我真的需要用我手动插入的常量值初始化一个固定长度的数组。我的情况如下:

    contract A {

...some states variables/modifiers and events......

uint[] public vector = new uint[](162);

vector = [.......1, 2, 3,......];

function A () {

....some code....

ContractB contract = new ContractB(vector);

}

....functions....

}

此代码未部署。显然我在混音时超出了气体限制。我尝试了以下方法:

我真的只需要一个向量,因为它表示图的边集,其中 ContractB 是构建图的数据结构。向量元素的顺序如下:

vector = [edge1From, edge1To, edge2From, edge2To,.......]

我得到了 81 条边(向量中有 162 个条目)。

我想我可以创建一个 setData 函数,在部署后调用此函数将值一个一个地推送到向量中,但这不是我的情况,因为我需要在调用之前填充向量

ContractB contract = new ContractB(vector);

现在看来我有两个疑惑:

1) 在 A 构造函数中的函数调用中尝试将向量作为参数传递是我错了吗?

2) 我可以看到我可以为边缘创建双重映射。像

mapping (bool => mapping(uint => uint)) 

但是我将需要多键值映射(从同一点开始的更多边)并且我将无法像对向量一样一次初始化所有映射?

如果数组的值范围足够小,您可以通过为 uints 使用更合适的大小来节省 gas 消耗。以太坊将值存储到 32 字节的插槽中,您为每个使用的插槽支付 20,000 gas。如果您能够使用较小尺寸的 uint(请记住,uintuint256 相同),您将能够节省 gas 使用量。

例如,考虑以下合约:

pragma solidity ^0.4.19;

contract Test {
    uint256[100] big;
    uint128[100] small;

    function addBig(uint8 index, uint256 num) public {
        big[index] = num;
    }

    function addSmall(uint8 index, uint128 num1, uint128 num2) public {
        small[index] = num1;
        small[index + 1] = num2;
    }
}

每次使用以前未使用的索引调用 addBig() 的执行成本将略高于 20,000 gas,并导致将一个值添加到数组中。每次调用 addSmall() 将花费大约 26,000,但是您向数组添加了 2 个元素。两者都只使用 1 个存储槽。如果你可以小于 uint128.

,你可以获得更好的结果

另一种选择(取决于您是否需要操作数组数据)是将您的 vector 存储在链外。您可以使用 oracle 检索数据或将数据存储在 IPFS 中。

如果这些选项都不适合您的用例,那么您将不得不更改您的数据结构and/or使用多个事务来初始化您的数组。

为什么合约需要在构建时初始化?

这应该有效

pragma solidity ^0.4.2;

contract Graph {
    address owner;

    struct GraphEdge {
        uint128 from;
        uint128 to;
    }

    GraphEdge[] public graph;
    bool public initialized = false;

    constructor() public {
        owner = msg.sender;
    }

    function addEdge(uint128 edgeFrom, uint128 edgeTo) public {
        require(!initialized);
        graph.push(GraphEdge({
            from: edgeFrom,
            to: edgeTo
        }));
    }

    function finalize() public {
        require(msg.sender == owner);
        initialized = true;
    }
}

contract ContractB {
    Graph graph;

    constructor(address graphAddress) public {
        Graph _graph = Graph(graphAddress);
        require(_graph.initialized());
        graph = _graph;
    }
}