Я изучал стратегии написания обновляемых контрактов. Шаблон, который неоднократно возникал, заключается в том, чтобы отделить бизнес-логику ваших контрактов от ее хранилища, чтобы обновления могли происходить без потери данных ( написание обновляемых контрактов в твердости , дизайн контракта с обновляемой твердостью ).
В этой статье говорится, что вечное хранилище — это «простой и расширяемый способ хранения любых данных, от простых значений до массивов и данных сложных типов объектов». Однако, учитывая приведенный ниже контракт хранилища и макет контракта пользователя, мне трудно понять, как лучше всего переместить хранилище пользователя в контракт EternalStorage, учитывая, что он содержит такие вещи, как массивы, сопоставления, структуры и т. д. Любые мысли/ предложения?
pragma solidity ^0.4.24;
contract UserRegistry {
struct User {
address addr;
uint points;
address[] friendsList;
mapping(address => bool) friends;
}
mapping(address => User) users;
mapping(address => mapping(address => uint)) public gamesPlayedTogether; // user address => (friendAddress => games played together)
function createUser() public {
User memory user = User(msg.sender, 0, new address[](0));
users[msg.sender] = user;
}
// Various other business logic
}
contract Storage {
mapping(bytes32 => uint256) private uIntStorage;
mapping(bytes32 => string) private stringStorage;
mapping(bytes32 => address) private addressStorage;
mapping(bytes32 => bytes) private bytesStorage;
mapping(bytes32 => bool) private boolStorage;
mapping(bytes32 => int256) private intStorage;
function getAddress(bytes32 _key) public view returns (address) {
return addressStorage[_key];
}
function getUint(bytes32 _key) public view returns (uint) {
return uIntStorage[_key];
}
function getString(bytes32 _key) public view returns (string) {
return stringStorage[_key];
}
function getBytes(bytes32 _key) public view returns (bytes) {
return bytesStorage[_key];
}
function getBool(bytes32 _key) public view returns (bool) {
return boolStorage[_key];
}
function getInt(bytes32 _key) public view returns (int) {
return intStorage[_key];
}
function setAddress(bytes32 _key, address _value) public {
addressStorage[_key] = _value;
}
function setUint(bytes32 _key, uint _value) public {
uIntStorage[_key] = _value;
}
function setString(bytes32 _key, string _value) public {
stringStorage[_key] = _value;
}
function setBytes(bytes32 _key, bytes _value) public {
bytesStorage[_key] = _value;
}
function setBool(bytes32 _key, bool _value) public {
boolStorage[_key] = _value;
}
function setInt(bytes32 _key, int _value) public {
intStorage[_key] = _value;
}
}
Я бы изменил некоторые детали в фиктивном клиенте. Здесь он близок к шаблону Mapped Structs with Index: Существуют ли хорошо решенные и простые шаблоны хранения для Solidity? .
Возможно, было бы неплохо понять этот шаблон ссылочной целостности: https://medium.com/@robhitchens/enforcing-referential-integrity-in-ethereum-smart-contracts-a9ab1427ff42 , прежде чем вы отправитесь в путь, затем...
Сделать без гражданства...
Свести все к парам ключ/значение с bytes32
ключами. Это грубый минимальный набросок, оптимизированный для удобочитаемости:
contract StatelessUserRegisty {
Storage dataStore;
constructor() public {
dataStore = new Storage();
}
function userKey(address userAddr) public pure returns(bytes32 userID) {
return keccak256(abi.encodePacked(userAddr));
}
function isUser(address userAddr) public view returns(bool isIndeed) {
return dataStore.getBool(userKey(userAddr));
}
function createUser(address userAddr) public returns(bool success) {
require(!isUser(userAddr));
dataStore.setBool(userKey(userAddr),true);
return true;
}
function updateUserPoints(address userAddr, uint userPoints) public returns(bool success) {
require(!isUser(userAddr));
dataStore.setUint(userKey(userAddr),userPoints);
return true;
}
}
Мне сошло с рук немного читов, потому что есть только 1 bool
и один uint
. Что, если бы было несколько uint
переменных?
Продолжайте хешировать...
bytes32 key1 = keccak256(userAddr, "first");
bytes32 key2 = keccak256(userAddr, "second");
Вы можете сделать еще один шаг и добавить row
или index
к сочетанию массивов и отображений.
Немного поэкспериментировав с такого рода паттернами, я пришел к выводу, что эти специфичные для приложения контракты хранения представляют собой что-то отдельное от логики приложения — просто необходимые гарантии создания, извлечения, обновления, удаления и ссылочной целостности. Затем рассмотрите возможность предоставления контрактам приложений «владеть» одним или несколькими контроллерами данных.
ApplicationContract => Data Controller => EternalStorage
Регулярная замена контроллера данных и/или контракта приложения — это отдельная тема (подсказка: не нарушайте его!). Дизайн предполагает, что в какой-то момент может потребоваться расширить схему.
Надеюсь, поможет.
Иордания
Роб Хитченс