Repo contains truffle tests for contract from Simple Bank Example
Let's add method to check bank's balances to be able write assertments in tests:
function getBalance(address aAddress) public constant returns(uint256) {
return balances[aAddress];
}
Initial contract code is available in contracts/Bank_0.sol
Execute truffle test
to see failing tests.
Before fixing logical errors in the contract we have to fix saveAccount() method. Method saveAccount() is not payable so msg.value doesn't exist in the context.
- Warning throwed by Remix IDE
browser/Bank.sol:10:53: Warning: "msg.value" used in non-payable function. Do you want to add the "payable" modifier to this function?
balances[msg.sender] = balances[msg.sender] + msg.value;
^-------^
- Validated by test
test/Bank_0_saveAccount_not_payable.js
Solution: add payable
modifier to the method
Accordingly to section 3.4. Run 2 for setup3 – Find 1st Bug sbuilder-eth indicates that it is possible to call constructor and transfer some amount of ether. Unfortunately it is impossible as constructor is not payable.
- Remix IDE gives the same warning:
browser/Bank.sol:8:30: Warning: "msg.value" used in non-payable function. Do you want to add the "payable" modifier to this function?
balances[msg.sender] = msg.value;
^-------^
- Validated by
migrations/1516447849_deploy__bank_1.js
. Uncomment line 4 and runtruffle test
and as a result migration will fail with error:Cannot send value to non-payable constructor
Solution: add payable
modifier to the constructor
Important: If problem 1. is not solved this problem doesn't exist!
In the same section 3.4. Run 2 for setup3 – Find 1st Bug stated:
Error analysis: constructor does not update balances variable, and invariant Bank_promise is violated in Bank() constructor.
There is internal variable for every contract called balance
. It is updated every time somebody sends funds to a contract. So actual balance of the contract will be updated but this transaction will not be represented in the balances map
Validated by migration migrations/1516449292_deploy__bank_2.js
which sends funds to contract during deployment and test test/Bank_2_constructor_records_transaction.js
Solution: add balances[msg.sender] = msg.value;
to the constructor.
Section 4.2 Run 3 for setup3 – Find 2nd Bug states that it is possible to call saveAccount()
with bank address. It is impossible as to use bank (contract) address we have to unlock bank (contract) account but we have no key and password to do so. Only contract can call saveAccount()
with it's own address.
Validated by test/Bank_3_saveAccount_recursive_funding_0.js
Solution: add method recursiveTransfer(uint amount)
to the contract which will transfer funds. Important notice: Remix IDE gives two warnings about the method:
Gas requirement of function Bank_4.recursiveTransfer() high: infinite. If the gas requirement of a function is higher than the block gas limit, it cannot be executed. Please avoid loops in your functions or actions that modify large areas of storage (this includes clearing or copying arrays in storage)
browser/Bank_4.sol:18:5:Use of "this" for local functions: Never use this to call functions in the same contract, it only consumes more gas than normal local calls.
Important: If problem 3. is not solved this problem doesn't exist.
Validated by test/Bank_4_saveAccount_recursive_funding_1.js
Final version of contract availabe in contracts/Bank_Final.sol
and tests are in test/Bank_Final_saveAccount_no_recursive_funding.js
.
Simplified but still valid version presented in contracts/Bank_Final_Simple.sol
. The only difference between simplified version and initial contract is payable
modifier for saveAccount()
method.