Es ist möglich, dass dieser Vertrag ein Problem hat, weil ich etwas Seltsames in meinem Vertragsprotokoll sehe. Jemand führt eine andere Vertragsfunktion aus, um mit dieser Mini-Glücksspiel-App zu spielen, und in 2 Spielen räumt er das ganze Geld ab xD.
Danke im Voraus :)
pragma solidity ^0.4.11;
contract MetaCoin {
event FlipCoinEvent(
uint value,
address owner
);
event PlaySlotEvent(
uint value,
address owner
);
function() public payable {}
function flipCoin() public payable {
assert(msg.value < 100000000000000000);
uint value = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
if (value > 55){
msg.sender.transfer(msg.value * 2);
}
FlipCoinEvent(value, msg.sender);
}
function playSlot() public payable {
require(msg.value < 100000000000000000);
uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
if(r >0 && r<3){
PlaySlotEvent(3,msg.sender);
msg.sender.transfer(msg.value * 12);
}else if(r >3 && r<6){
PlaySlotEvent(2,msg.sender);
msg.sender.transfer(msg.value * 6);
}else if(r >6 && r<9){
PlaySlotEvent(1,msg.sender);
msg.sender.transfer(msg.value * 3);
}else{
PlaySlotEvent(0,msg.sender);
}
}
function getBalance() public constant returns(uint bal) {
bal = this.balance;
return bal;
}
}
Laut Dokumentation bleiben die block.timestamp
, die block.blockhash
und die block.number
gleich, bis der nächste Block zur Blockchain hinzugefügt wird. Da die Blockzeit etwa 15 bis 17 Sekunden beträgt, ist es leicht möglich, "den richtigen Block zu bekommen", um Ihren Smart Contract anzugreifen.
Man könnte einfach einen Smart Contract mit einer Funktion schreiben getChances()
, die den aktuellen Wert von prüft (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1
und, wenn er im richtigen Bereich liegt (z. B. < 3), die playSlot()
Funktion Ihres Vertrags aufruft.
Die Funktion getChances()
muss dann nur jedes Mal von einem Orakel aufgerufen werden, wenn ein neuer Block zur Blockchain hinzugefügt wird.
Keine Magie.
Ich hoffe es hilft
BEARBEITEN :
Ich habe Ihren Smart Contract komprimiert und einen weiteren geschrieben, um besser zu zeigen, was ich oben gemeint habe:
pragma solidity ^0.4.11;
contract MetaCoin {
event PlaySlotEvent(
uint value,
address owner
);
function playSlot() public returns (uint){
uint r = (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
PlaySlotEvent(r,msg.sender);
}
}
contract Test {
function getCurrentR() constant returns (uint) {
return (block.timestamp + uint(block.blockhash(block.number-1)))%100 + 1;
}
}
Wenn Sie dies in Remix versuchen, rufen Sie einfach an getCurrentR()
und rufen Sie sofort an playSlot()
. Sie werden sehen, dass sich der Wert nur in Sekunden unterscheidet, die Sie warten, bevor Sie aufrufen playSlot()
. Daher kann ein Spieler überprüfen, r
bevor er mit dem Spielen beginnt...
Ihre Lotterie ist nicht wirklich zufällig.
Jede Entscheidung, die ein Benutzer trifft, die sich auf das Ergebnis auswirkt, verschafft diesem Benutzer einen unfairen Vorteil. Beispiele beinhalten:
- Verwenden eines Blockhashs, Zeitstempels oder eines anderen vom Miner definierten Werts. Denken Sie daran, dass der Miner die Wahl hat, ob er einen Block veröffentlicht oder nicht, so dass er möglicherweise eine Chance auf den Preis pro Block hat, den er abbaut.
Siehe diese Antwort für eine ausführlichere Erklärung: Wie kann ich sicher eine Zufallszahl in meinem Smart Contract generieren? .
behaupten (msg.value <100000000000000000); require(msg.value < 100000000000000000);
Das heißt, ich kann auch mit 0 wei spielen und trotzdem gewinnen, richtig?
Vielleicht meintest du >?
Zur Generierung der Zufallszahl verwenden Sie den aktuellen Blockzeitstempel und den vorherigen Blockhash. Der vorherige Block-Hash ist bekannt, sodass keine Zufälligkeit hinzugefügt wird. Der aktuelle Blockzeitstempel ist dem Miner bekannt, sodass er flipCoin anrufen kann, wann immer der Zeitstempel für ihn günstig ist.
Beachten Sie, dass Sie den aktuellen Block-Hash in Solidity nicht abrufen können. Wie in den Dokumenten angegeben:
block.blockhash(uint blockNumber) gibt zurück (bytes32): Hash des angegebenen Blocks – funktioniert nur für die 256 neuesten Blöcke , ausgenommen den aktuellen
Stattdessen sollten Sie das Spiel in 2 Phasen aufteilen:
flipCoin
notieren Sie das block.number
und msg.value
für den Absender.withdrawReward
Sie die Belohnung übertragen, wenn der Block-Hash für die aufgezeichnete Blocknummer ihn zum Gewinner macht. Hier müssen Sie auch bestätigen, dass die aufgezeichnete Blocknummer kleiner als die aktuelle Blocknummer ist.Es ist in diesem Fall in Ordnung, Blockhash zu verwenden, da die 0,1-Ether-Belohnung viel niedriger ist als die Block-Belohnung. Um die Zufallszahl zu generieren, sollten Sie auch die Adresse des Absenders in den Seed aufnehmen, da sonst ein Angreifer mehrere flipCoin-Anfragen senden kann, die insgesamt mehr als die Blockbelohnung ergeben, was einen Anreiz zur Manipulation des Blockhashs und die Möglichkeit zum Schummeln bietet.
hugofreire
joffi
block.number
bekommt man mit die Nummer des neusten Blocks. Soblock.number-1
wird die Nummer des Blocks vor dem aktuellen hinzugefügt. Daherblock.blockhash(block.number-1)
erhalten Sie mit etwas, das auf Informationen der Vergangenheit basiert . Übersehe ich etwas?hugofreire
joffi
block.number-1
Sinn. Entschuldigen Sie das Durcheinander. Aber obwohl Sie eine Zahl auf der Grundlage bereits verfügbarer Daten berechnen, richtig?hugofreire
joffi
hugofreire