Dapple allows you with its test framework to unit tests each part of your dApp in isolation before deploying it. But in complex systems dApps rarely live in isolation. Instead they integrate with a rich subset of other dApps existing on ethereum's live network. The dapple-chain feature set allows you to test the integration of dApps into the real ethereum state by pretending to extend ethereum's livenet state. Alongside reasoning about hypothetical chain states is now possible.
Dapple-Chain provides lazy-loading with a pseudo light client implementation. You can fork of and work on the full ethereum blockchain without having to download the blockchain to your machine which lowers the entrance barrier for newcomers significantly and makes working with dapple light-weight.
Dapples lazy loading be will be extended with the ethereum's official light client proticol once it's ready.
For a first integration test we will test DSEthToken@0x992c64ac907ef9e531e7ff8d06cec599778a0e72 which is provided by the Dappsys framework.
First we initialize a new git and dapple project and install dappsys:
git init
dapple init
git submodule add [email protected]:nexusdev/dappsys.git dapple_packages/dappsys
Our integration test should call the on-chain contract and display correctly its total supply. For this we create a new dapple project with a small test contract:
// contracts/test.sol
import "dapple/test.sol";
import "dappsys/token/eth_wrapper.sol";
contract IntegrationTest is Test {
function testEchoCall() {
DSEthToken dsEthToken = DSEthToken(0xecf8f87f810ecf450940c9f60066b4a7a501d6a7);
// this will give us the total supply of tokens
uint value = dsEthToken.totalSupply();
//@log total Supply is: `uint value`
// this will give us the balance of an address
uint value2 = dsEthToken.balanceOf(0x9f73bc871764c879fd9e0f524278373fa7875068);
//@log a balance: `uint value2`
// this will give us the balance of the address 0x00.00
uint value3 = dsEthToken.balanceOf(0x0);
//@log 0 balance: `uint value3`
}
}
Also we need to work on the livenet chain:
$ dapple chain branch
* master #0
livenet #1788860
$ dapple chain checkout livenet
switched to livenet
Now we can inspect our current branch with:
$ dapple chain status
genesis: d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3
stateRoot: 0x52324375f49843b61fc2ca693d33ca4037b5aeebe07c32867e95de9bacba773d
branch: livenet
fork root: 1788860
height: 1788860
dev mode: false
default account:
0x0000000000000000000000000000000000000000
faked accounts:
0x0000000000000000000000000000000000000000
As you can see here we forked of at block 1788860. Now we run our test:
$ dapple test --report
Testing...
IntegrationTest
test echo call
LOG: total Supply is: 463200000000000000000
LOG: balance: 77300000000000000000
LOG: 0 balance: 0
Passed!
Summary
Passed all 1 tests!
Awesome: there is a total of 463.2 eth and the balance of an address is 77.3eth
and there were no tokens burned (e.g. 0x0 has no tokens).
If we look again at the stats(dapple chain status
) we see that they didn't change.
This is because dapple test
don't affect the persistent state of the branch.
Now we simulate a scenario where the address 0x9f73bc871764c879fd9e0f524278373fa7875068
decides to burn all his ether. First we have to make sure or dapple project
is aware of the DSEthToken contract, by including it in our local dappfile:
environments:
default:
objects:
eth_token:
class: DSEthToken
address: '0xecf8f87f810ecf450940c9f60066b4a7a501d6a7'
Then we write a small dapple-script:
// ./send.ds
import eth_token
eth_token.transfer(0,77300000000000000000)
Fake the ownership of our target address:
$ dapple chain fake 0x9f73bc871764c879fd9e0f524278373fa7875068
And execute it:
$ dapple run send.ds -s
Deploying to environment "default"...
Using internal EVM...
Connected to RPC client, block height is 1788860
CALLED: DSEthToken("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7").transfer(0,77300000000000000000)
GAS COST: 49793 for "call DSEthToken.transfer(0,77300000000000000000)"
.
Successfully deployed!
If we look now at our stats (dapple chain status
), we see that our block height
increased by one block. Also when we execute our test again to test the new
balances, we see that the money is gone!
$ dapple test --report
Testing...
IntegrationTest
test echo call
LOG: total Supply is: 463200000000000000000
LOG: balance: 0
LOG: 0 balance: 77300000000000000000
Passed!
Dapple has a build in blockchain. It can either start a new by creating a new genesis block or "fork of" the current livenet state.
dapple test
will run without leaving a state change on top of the current state.
You can persistently change the changes caused by the test with the --persistent
flag.
dapple run
executed on the internal environment will always cause a persisent
state change!
dapple chain status print the current status of the chain
dapple chain branch list all aviable branches
dapple chain fork <name> fork chain from the current HEAD
<name> new branch name
dapple chain checkout <name> switch to another branch
<name> branch name
dapple chain log logs all blocks and transactions since the fork root
dapple chain server start an rpc server on the current branch
dapple chain fake <address> pretend you have the private keys over the given address
<address> address, you want to fake ownership of
Prints the relevant information about the current chain state:
$ dapple chain status
genesis: 966a9507fc0a9579a926de84558c99ad47077d72e021406c601d751127cc03c3
stateRoot: 0xab07efc7c692bca7473b944f96343df9c670c3cfa42fb050d0e86e7679c5ef56
branch: master
fork root: 0x601f6c46323d6372fc3ac90f277a55aa5e482068cc396143069a91d6dba6483b
height: 7
List all branches:
* master #7
livenet #1650000
Fork of the current state to a new branch.
$ dapple chain fork omg
Forked master to omg
$ dapple chain branch
master #7
livenet #1650000
* omg #7
Switch to another branch
$ dapple chain checkout livenet
switched to livenet
$ dapple chain branch
master #7
* livenet #1650000
omg #7
Logs all relevant information since the fork-root. Such as:
- blocks
- transactions
- contract deployments
- function calls
THIS IS CURRENTLIN IN WIP EXPERIMENTAL MODE, NOT ALL RPC FEATURES ARE FULLY
IMPLEMENTED
Run a RPC server. You can interact with it's current state through localhost:8545
This allows faking the ownership of an address. With a faked ownership you can
send transactions from any address and change the chain-state arbitrary.
Note here that in case of an illegal operation like faked transaction, dapples
chain state will transition in to a dev-mode which will stay as an inherent
property for the next changes. In dev-mode it won't be possible to merge to
the real ethereum chain as invalid signed transaction will be rejected by the
network.
The default address is always 0x0000000000000000000000000000000000000000
.
$ dapple chain status # previous state
...
default account:
0x0000000000000000000000000000000000000000
faked accounts:
0x0000000000000000000000000000000000000000
...
$ dapple chain fake 0xffffffffffffffffffffffffffffffffffffffff
$ dapple chain status # new state
...
default account:
0xffffffffffffffffffffffffffffffffffffffff
faked accounts:
0x0000000000000000000000000000000000000000
0xffffffffffffffffffffffffffffffffffffffff
...
-
remote push/ pull to dapphub service Similarly to a git repository one might want to share his state with others. This feature set should provide the exchange of hypothetical chain states, preferably in a decentral manner.
-
account management In order to sign real transactions, some notion of account management has to be implemented. This should cover (1) adding accounts, (2) selecting accounts/ default, (3) removing accounts.
-
merging To merge two chain states to one. This also includes releasing correct signet transactions to the livenet.
-
improved livenet interface. It should be possible to fork the livenet chain on any arbitrary block, as well as synchronize the forked chain.
-
improved logger Put more Mojo into the logger.