Adressen in Array oder Mapping speichern?

Mein Vertrag bedient derzeit eine Reihe von Adressen an ein Front-End:

address[] public addresses;
function getAddresses() public view returns (address[]) {
    return addresses;
}

Ich mache mir Sorgen um die Skalierbarkeit. Gibt es eine Begrenzung für die Länge eines Arrays von Adressen, die zurückgegeben werden können? Wäre es besser, alle Adressen in einem Mapping mit einem Zähler zu speichern?

mapping public (uint => address) addressMap;
uint public addressCounter;

Dies scheint skalierbarer zu sein, würde jedoch erfordern, dass das Front-End viel mehr Anrufe an den Vertrag tätigt, der dann jede Adresse verwendet, um einen weiteren Anruf zu tätigen.

Antworten (1)

Es gibt Kompromisse zwischen den verschiedenen Arten von indiziertem Speicher, und viele von uns stellen fest, dass wir eine Kombination der Optionen verwenden müssen, um alle Ziele eines bestimmten Vertrags zu erreichen.

Es gibt ein Skalierbarkeitsproblem beim Übergeben der gesamten Liste in einem Zug. Dies ist nicht erforderlich, wenn der Client möglicherweise bereits über den größten Teil der Liste verfügt. In jedem Fall werden Sie feststellen, dass Skalierung erreicht wird, wenn alle Operationen als Festkostenfunktionen angeordnet sind.

Auch wenn Ihr Beispiel einfach ist, zeige ich Ihnen, wie Sie die häufigsten langfristigen Speicheranforderungen erfüllen können (langfristig, da Sie den Vertrag normalerweise später nicht ändern können).

Iterierbare Liste, wie in Ihrem Beispiel, aber geben Sie sie einzeln zurück:

function getAddressCount() public view returns(uint count) {
  return addesses.length;
}

function getAddressAtRow(uint row) public view returns(address theAddress) {
  return addresses[row];
}

Sie finden es (sehr wahrscheinlich) praktisch, (ohne Iteration) zu wissen, ob sich eine Adresse irgendwo befindet addresses[], um Probleme wie das zweimalige Anhängen derselben Adresse zu vermeiden. Sie möchten, dass der Vertrag eine fehlerhafte Clientanfrage abfängt, also muss dies in großem Maßstab möglich sein (keine Iteration). Zuordnungen können helfen. Zusätzlich zum Array:

mapping(address => bool) bool isActuallyAnAddressOnMyList;

Das passt so etwas wie:

function isAddress(address check) public view returns(bool isIndeed) {
   return isActuallyAnAddressOnMyList[check];
}

Sie können mit einem Muster etwas ausgefallener werden, das zusätzliche Eigenschaften zu den Adressen enthält (so viele Eigenschaften wie Sie benötigen):

struct AddressStruct {
  bool isAddress;
  // more fields
}

mapping(address => AddressStruct) public addressStructs;
address[] public addressList;

Sie können den Vertragsstatus vollständig auffindbar machen:

function getAddressCount() public constant returns(uint count) {
  return addressList.length;
}
function getAddressStruct(address fetch) public view returns(bool isAddress, ... ) {
  require(isAddress(fetch);
  return(addressStructs[fetch].isAddress, ...);
}

Sie können (sollten) Ereignisse ausgeben, wenn Änderungen auftreten:

event LogNewAddress(address sender, address newAddress);

function appendAddress(address newAddress) returns(bool success) {
  require(!isAddress(newAddress));
  addressList.push(newAddress);
  addressStructs[newAddress].isAddress = true;
  LogNewAddress(msg.sender, newAddress);
  return true;
}

Indem Sie das Ereignis ausgeben, geben Sie Clients, die bereits synchronisiert sind, die Möglichkeit, neue Adressen einfach zu notieren, sobald sie eintreffen. Es bedeutet auch, dass es theoretisch möglich ist, die gesamte Zustandsgeschichte des Vertrages zu rekonstruieren, wenn dies richtig gemacht wird.

Die Idee, sowohl Mapping als auch Array zu verwenden, erhöht die Einfügungsgaskosten, adressiert jedoch die Kompromisse bei der ausschließlichen Verwendung des einen oder anderen. Verschiedene Strukturen und Kompromisse werden hier beschrieben: Gibt es gut gelöste und einfache Speichermuster für Solidity?

Ich hoffe es hilft.