I just started learning solidity and have some questiones about the smart contract I created for pratice/fun.
Please let me know if any of my concepts are inaccurate, appreciate for all the advices and suggestiones.
Description:
the concept of this smart contract is really simple, whoever send the bigger amount of ether into the contract wins, it will pair you with whoever is before you (if no one is before you, you are player_one) and it will reset after 2 player are played (so it can be played again)
Code:
contract zero_one {
address public player_one;
address public player_two;
uint public player_one_amount;
uint public player_two_amount;
function zero_one() public{
reset();
}
function play() public payable{
//Scenario #1 Already have two player in game, dont accpet new player. do I even need this check at all? since smart contract execute serially i should never face this condition?
if(player_one != address(0) && player_two != address(0)) throw;
//Scenario #2 First player enter the game
else if(player_one == address(0) && player_two == address(0)){
player_one=msg.sender;
player_one_amount = msg.value;
}
//Scenario #3 Second player join in, execute the game
else{
player_two = msg.sender;
player_two_amount = msg.value;
//check the amount send from player_one and player two, whoever has the bigger amount win and get their money
if(player_two_amount>player_one_amount){
player_one.transfer(player_one_amount+player_two_amount);
reset();
}
else if(player_two_amount<player_one_amount){
player_two.transfer(player_one_amount+player_two_amount);
reset();
}
else{
//return fund back to both player
player_one.transfer(player_one_amount);
player_two.transfer(player_two_amount);
reset();
}
}
}
function reset() internal{
player_one = address(0);
player_two = address(0);
player_one_amount = 0;
player_two_amount = 0;
}
}
Questions
When sending ether back to users, do I need to calcualate how much
gas is going to take? or would smart contract automatically deduct
the gas from the amount i am going to send
Is it correct to set reset() as interal since i only want it to be
called within the smart contract, it shouldn't be called by anyone else
Would Scenario#1 ever happen? since from what I understand
smart contract do not have to worry about race condition and it
should never be in that state?
Is adding playable to play correct? ( since user are going to send ether with this call)
Is using throw a bad practice? (warning on remix)
transfer vs send, why is it better to use transfer?
I coded this smart contract as a practice. I can already see that if
someone want to game the system, he could just wait for someone to be player_one and check the amount player_one send(after the block is mined) and just send a bigger sum than that. Is there anyway this exploit could be stopped? Is there other security/flaws I did not see?
Thanks!
When sending ether back to users, do I need to calcualate how much gas
is going to take? or would smart contract automatically deduct the gas
from the amount i am going to send
The gas limit specified when executing a transaction needs to cover all activity end-to-end. This includes calls out to other contracts, transfers, etc. Any gas not used after the transaction is mined is returned to you.
Is it correct to set reset() as interal since i only want it to be
called within the smart contract, it shouldn't be called by anyone
else
internal is fine for what you're intending to do. But, it's more like protected access. A sub-contract can call internal methods. private provides the strictest visibility.
Would Scenario#1 ever happen? since from what I understand smart
contract do not have to worry about race condition and it should never
be in that state?
No, it should not happen. You are correct that transactions are processed serially, so you don't really have to worry about race conditions. That doesn't mean you shouldn't have this sort of protection in your code though...
Is adding playable to play correct? ( since user are going to send
ether with this call)
Yes. Any method that is expecting to receive wei and uses msg.value needs to be marked as payable.
Is using throw a bad practice? (warning on remix)
throw is deprecated. You want to use one of revert, require, assert as of 0.4.13. The one you use depends on the type of check you're doing and if gas should be refunded. See this for more details.
transfer vs send, why is it better to use transfer?
send and transfer are similar. The difference is that send limited the amount of gas sent to the call, so if the receiving contract tried to execute any logic, it would most likely run out of gas and fail. In addition, failures during send would not propagate the error and simply return false. Source
I coded this smart contract as a practice. I can already see that if
someone want to game the system, he could just wait for someone to be
player_one and check the amount player_one send(after the block is
mined) and just send a bigger sum than that. Is there anyway this
exploit could be stopped? Is there other security/flaws I did not see?
Security is a much more in depth topic. Any data sent in a transaction is visible, so you can't hide the exploit you mentioned unless you use a private blockchain. You can encrypt data sent in a transaction yourself, but I don't believe you can encrypt sending ether.
In terms of other security issues, there are several tools out there that perform security checks on contracts. I'd suggest looking into one of those. Also, read through the security considerations page on the Solidity documentation.
Related
Glad to see blockchain development support on Stack Overflow as resources as very scarce nowadays.
I'm a new kid trying to learn this ecosystem.
How can I add an image logo to a token that I've already created and deployed on remix.ethereum.org?
Should I have done this before deploying it?
Newbie question: Once deployed the same code can never modified?
I'm currently interacting with the token on BSC - seems that all BSC tokens are created as a fork of Solidity and the ETH ERC20 paradigm. (?)
This is my code
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
contract Foobar is ERC20 {
constructor(uint256 initialSupply) public ERC20("Foobar", "FOO") {
_mint(msg.sender, initialSupply);
}
}
Short answer: No, once you deploy a contract to the mainnet, you can't modify nor delete it, it'll be in the blockchain forever (unless you're using a testnet or a private blockchain network to test the contract).
Long answer:
Once a contract is deployed, it can never be modified nor deleted, the only thing you can modify is it's storage (variables that the contract uses) by calling it's defined functions, but you can't modify the logic behind it. If you messed up, you'll need to fix your contract, and re-deploy.
Take in mind that heavy tests and thoughtful inspections are advised before you deploy a contract to the mainnet and set an app into production (and even more if you'r contract will handle sensitive values), mainly because even if you screw up and re-deploy, if you had said contract work in production, its extremly difficult to fix.
For example: You make you'r own tiny bank, the contract recives eth, and it stores it a variable a mapping of addresses with a variable that keeps a count of how much eth from the total eth that the contract is holding is for each user.
contract Bank {
// mapping of address with current money value.
mapping (address user => uint money) balance;
constructor(){}
function withdraw(uint memory money) public {
// Require money parameter is not empty.
require(money != 0, "Can't withdraw 0 eth");
// Big issue here, if we send the money, and then update the balance,
// the user will be able to withdraw money even if hi's balance turns
// to 0 if he spams the withdraw function before he runs out of money.
// The good way would be the other way around, first update balance, and then send.
// Check if the user has enough money.
if (balance[msg.sender].money >= money) {
// Send money
address(msg.sender).transfer(money);
// Update balance.
balance[msg.sender].money -= money;
}
}
...
}
Now this has a critical issue, if someone withdraws money twice very fast, it skips a check iteration that checks if the user has enough money in it's balance to withdraw.
Now to fix this, you'll need to quickly withdraw all the money that the contract has and it's mapping(address user => uint money) ... mapping data, fix and re-deploy the contract, manually send all the money to the new contract, and also set the previous data that it had, and if that wasn't enough you'll need to change you'r front-end app to connect with the new contract.
I know its a long example, but when you start coding contracts from 0, you have to understand that testing and revising the contract is a must.
Once a contract is deployed, it cannot be modified nor deleted, it's there forever, alteast in the mainned, that's why you use private blockchains such as Ganache, or use a public testnet (Ropsten, Kovan, Rinkeby, Goerli).
Also about the logo, most tokens don't have their "logo" in their contract ( unless you're making a NFT [ ERC721 ] ), yet, if you still want to add a logo that anyone can access from your contract, i'd suggest to make a state variable that holds its value (string mainly).
The value can be a IPFS hash (IPFS is a decentralized file system that allows you to upload files of any kind, it will return a hash once it's uploaded, and you can access you'r file via https://HASHTOKEN.ipfs.infura-ipfs.io/, for example:
https://bafybeibctnxu7zpatp3caj2gevofs2oirdvdyo6yulxk2hfyaewxib3pj4.ipfs.infura-ipfs.io/
soo in you'r case, you can upload the logo there, grab the hash, and store it in you'r contract, then in your frontend app you'll only need to add the hash to the url.
A second way of adding you'r logo would be to turn you'r image into base64 and paste the whole thing into you'r contract, that way you have literally the image in the contract, yet, i don't recommend this last solution since base64 strings can get very large depending on how heavy is the file, and the heavier you'r contract is, the more gas it will use when you deploy it, and more expensive for the users when they try to use it.
Yet, if what you mean is to add a logo on sites such as BscScan, you can find a guide here.
I'm working on a uni project based on blockchain, and I have to audit our system, check known attacks, ect.
This the the document that I check, principaly, since i start to work on smart contracts issues first :
Known-attack ethereum smart contract
I have trouble understanding the example used in the "Dos With (unexpected) revert attack" part. I share the code :
// INSECURE
contract Auction {
address currentLeader;
uint highestBid;
function bid() payable {
require(msg.value > highestBid);
require(currentLeader.send(highestBid)); // Refund the old leader, if it fails then revert
currentLeader = msg.sender;
highestBid = msg.value;
}}
They say that an attacker could force the call of bid to revert everytime so no-one is able to bid, which would make the attacker win the auction by default.
But.. How would he do that, that's the part I don't get. Do we agree that at least this piece of contract is the "valid one", and isn't a payload ? If the payload is a contract, can anyone provide an exemple/explanation ?
I'll add that, if here I quote a solidity contract, we work with Vyper, but from what I read before, this is still a kind of issue that i'll find there too.
Thanks in advance !
If send() target address is a smart contract it will execute the fallback function.
If the currentLeader points to a smart contract that has a fallback function that has been intentionally made to revert on failed send, the bid() won't work for any participants until currentLeader has been changed.
More information here.
This is not a "DoS" attack but simply gotcha in Solidity programming.
In order to manage balances of a smart contract with Solidity, do we still need to implement the balance-keeper by ourselves?
In the following blogpost, the author is doing so:
https://medium.com/daox/three-methods-to-transfer-funds-in-ethereum-by-means-of-solidity-5719944ed6e9
contract Sender {
function send(address _receiver) payable {
_receiver.call.value(msg.value).gas(20317)();
}
}
contract Receiver {
uint public balance = 0;
function () payable {
balance += msg.value;
}
}
According to the docs, it seems to already built in: https://solidity.readthedocs.io/en/develop/units-and-global-variables.html#address-related (although it was implemented in the address-property which can be cast from this, don't know if I understand it correctly)
Can someone Experienced please clarify a bit?
PS: sorry for bad formatting of my question. Safari doesn't show the formatting-toolbar of stackoveflow anymore properly.
The two do different things. You can always check the balance of a contract via <address>.balance from a contract or eth_getBalance via JSON-RPC.
But the balance state variable in the contract you shared might be different from that number. For example, the contract address may have ether in it before deployment, or another contract might call selfdestruct(<address>) and add to this contract's ether that way. In both cases, the address's balance will reflect that, but the balance state variable in the code will not, because it only increases when the fallback function is invoked.
So it sort of depends... if you just want to know how much ether a contract holds, you should use the built-in <address>.balance, but if you want to track some other number, you'll need to do it yourself.
I'm working on something, just a proof of concept, where people are rewarded for sharing metadata about their IP location. In this case (please ignore the code errors for now), someone would submit their ip, I would send it to an oracle that would resolve it and send back metadata about the ip, then would send the address and metadata to another contract.
This is the naive attempt at pseudo code (or non-compiling, at least) I've come up with:
pragma solidity ^0.4.0;
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract IpMeta is usingOraclize {
uint public metadata;
function Metadata(bytes32 _ipAddress) public {
update(_ipAddress);
}
function __callback(bytes32 myid, string result) public {
if (msg.sender != oraclize_cbAddress()) revert();
bytes32 ipMetadata = result;
/* send address and result to another contract */
}
function update(bytes32 ipAddress) public payable {
oraclize_query("URL", "xml(https://ipresolver.com?ipresolve=" . ipAddress . ")");
}
}
The goal here is to allow people to prove something about their IP address (country, provider, etc) without the actual IP address being stored with the wallet address in the transaction or anywhere else. Is there a way to hide input data? I'm at a loss on research, and it seems like it would be a really useful function if possible. The im/practicalities of anything other than how to keep input data hidden or inaccessible aren't important.
Thanks,
Mark
The EVM is essentially a network composed of nodes of computers. Anyone can run their own node and your smart contract code will get run on those computers if they have a valid node that can receive the transactions. And since the data will also get transmitted to those computers, the owner that sees the code running in his/her computer will eventually have the power to look into the process getting run(by overseeing network, inspecting memory, etc.).
So from my understanding, the difficulty of doing this depends on the implementation of a Ethereum node. Actually Ethereum's protocols handles some level of security by encrypting the p2p connection. However, what makes all these obsolete in your context is that, the input data is serviced and revealed by services like etherscan(random example). I think it's still possible to achieve want you want, but it will only be a workaround and it will be difficult.
I think of Ethereum and its EVM as a transparent computer, basically the data it receives and sends are not secure at all. If you want to achieve security, that's the thing to achieve in somewhere else, for example process the ip related jobs in some other secure place(trusted 3rd party), then store the data without ip onto the blockchain. That's the best I can think of.
I am using testrpc and web3.
I used the idiom below to ensure that only a previously defined user should be able to do something:
function doSomethingProtected() {
if ( msg.sender != authorizedUser )
throw;
flagSomething = true;
}
When calling the function on an instantiated contract with web3 as follows:
myContract.doSomethingProtected( { from: "0x..." } );
it worked. At first I was pleased but then I realized the web3 API had not required me to provide any passphrase for a private key or such like.
Can anyone with the simple knowledge of someones public key/address call this function?
The use of this idiom in the examples led me to believe a benefit of the Ethereum contracts was that it ensured msg.sender was cryptographically assured.
The reason is that you are using testRPC, which doesn't lock it's accounts, so you don't need a password.
If you were to do this with geth, you would need to unlock the account before sending from it.
Without the private key, that function will throw an error, so you are correct in using that authorization method.
It's difficult to be certain without seeing more of your code, but it seems likely that you were calling the contract on your local node, rather than sending a transaction. Transactions can only be signed by someone with the account's private key, meaning you can rely on msg.sender to be accurate, but messages executed on your local node won't enforce that. Any changes they make are rolled back and not applied to state, though, so it doesn't matter what your local call does.
In general, there are two ways to call a function from web3.js: Using a transaction or just using a "call". Only in transactions you can actually modify the blockchain content (reading is always possible). Transactions always require a valid signature and thus access to a private key.
The reason you were not asked for a password might be that you already unlocked the account. Furthermore, users other than the authorized user can call the function, only the changes will be thrown away.
My guess is that your account was already unlocked when calling the function. I don't remember the exact period that your account is unlocked after unlocking it in web3. I might be wrong though. Would have added this as a comment, but I am not allowed right now.