diff --git a/cmd/geth/main.go b/cmd/geth/main.go index d7d4a79eee..a464633af6 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -318,6 +318,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.WhisperEnabledFlag, utils.SwarmConfigPathFlag, utils.SwarmSwapDisabled, + utils.SwarmSyncDisabled, utils.SwarmPortFlag, utils.SwarmAccountAddrFlag, utils.ChequebookAddrFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index c961b5fe1b..6eae3cd037 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -357,6 +357,10 @@ var ( Name: "bzznoswap", Usage: "Swarm SWAP disabled (false)", } + SwarmSyncDisabled = cli.BoolFlag{ + Name: "bzznosync", + Usage: "Swarm Syncing disabled (false)", + } // ATM the url is left to the user and deployment to JSpathFlag = cli.StringFlag{ Name: "jspath", @@ -788,9 +792,10 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. if len(bzzport) > 0 { bzzconfig.Port = bzzport } - swap := ctx.GlobalBool(SwarmSwapDisabled.Name) + swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name) + syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name) if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return swarm.NewSwarm(ctx, bzzconfig, swap) + return swarm.NewSwarm(ctx, bzzconfig, swapEnabled, syncEnabled) }); err != nil { Fatalf("Failed to register the Swarm service: %v", err) } diff --git a/common/chequebook/api.go b/common/chequebook/api.go index 5507682530..ee53bb0749 100644 --- a/common/chequebook/api.go +++ b/common/chequebook/api.go @@ -1,6 +1,7 @@ package chequebook import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -8,22 +9,44 @@ import ( const Version = "1.0" +var errNoChequebook = errors.New("no chequebook") + type Api struct { - ch *Chequebook + chequebookf func() *Chequebook } -func NewApi(ch *Chequebook) *Api { +func NewApi(ch func() *Chequebook) *Api { return &Api{ch} } +func (self *Api) Balance() (string, error) { + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Balance().String(), nil +} + func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) { - return self.ch.Issue(beneficiary, amount) + ch := self.chequebookf() + if ch == nil { + return nil, errNoChequebook + } + return ch.Issue(beneficiary, amount) } func (self *Api) Cash(cheque *Cheque) (txhash string, err error) { - return self.ch.Cash(cheque) + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Cash(cheque) } func (self *Api) Deposit(amount *big.Int) (txhash string, err error) { - return self.ch.Deposit(amount) + ch := self.chequebookf() + if ch == nil { + return "", errNoChequebook + } + return ch.Deposit(amount) } diff --git a/common/chequebook/cheque.go b/common/chequebook/cheque.go index 41c89418b0..81d4b8eaa1 100644 --- a/common/chequebook/cheque.go +++ b/common/chequebook/cheque.go @@ -54,6 +54,7 @@ type Backend interface { Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) GetTxReceipt(txhash common.Hash) *types.Receipt CodeAt(address string) string + BalanceAt(address common.Address) string } // rlp serialised cheque model for use with the chequebook @@ -165,13 +166,25 @@ func NewChequebook(path string, contract common.Address, prvKey *ecdsa.PrivateKe owner: owner, } if (contract != common.Address{}) { - glog.V(logger.Detail).Infof("[CHEQUEBOOK] new chequebook initialised from %v (owner: %v)", contract.Hex(), owner.Hex()) + err = self.setBalanceFromBlockChain() + if err != nil { + return nil, err + } + glog.V(logger.Detail).Infof("[CHEQUEBOOK] new chequebook initialised from %v (owner: %v, balance: %s)", contract.Hex(), owner.Hex(), self.balance.String()) } return } +func (self *Chequebook) setBalanceFromBlockChain() error { + balanceString := self.backend.BalanceAt(self.contract) + if _, ok := self.balance.SetString(balanceString, 10); !ok { + return fmt.Errorf("Incorrect balance: %s", balanceString) + } + return nil +} + // LoadChequebook(path, prvKey, backend) loads a chequebook from disk (file path) -func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend) (self *Chequebook, err error) { +func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, checkBalance bool) (self *Chequebook, err error) { var data []byte data, err = ioutil.ReadFile(path) if err != nil { @@ -184,7 +197,14 @@ func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend) (sel if err != nil { return nil, err } - glog.V(logger.Detail).Infof("[CHEQUEBOOK] loaded chequebook (%s, owner: %v) initialised from %v", self.contract.Hex(), self.owner.Hex(), path) + if checkBalance { + err = self.setBalanceFromBlockChain() + if err != nil { + return nil, err + } + } + + glog.V(logger.Detail).Infof("[CHEQUEBOOK] loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contract.Hex(), self.owner.Hex(), self.balance, path) return } diff --git a/common/chequebook/cheque_test.go b/common/chequebook/cheque_test.go index 06d95ff380..ed4b3d42ff 100644 --- a/common/chequebook/cheque_test.go +++ b/common/chequebook/cheque_test.go @@ -45,6 +45,10 @@ func (b *testBackend) CodeAt(address string) string { return "" } +func (b *testBackend) BalanceAt(address common.Address) string { + return "0" +} + func genAddr() common.Address { prvKey, _ := crypto.GenerateKey() return crypto.PubkeyToAddress(prvKey.PublicKey) @@ -54,21 +58,21 @@ func TestIssueAndReceive(t *testing.T) { prvKey, _ := crypto.GenerateKey() sender := genAddr() path := "/tmp/checkbook.json" - chbook, err := NewChequebook(path, sender, prvKey, nil) + chbook, err := NewChequebook(path, sender, prvKey, newTestBackend()) if err != nil { - t.Errorf("expected no error, got %v", err) + t.Fatalf("expected no error, got %v", err) } recipient := genAddr() chbook.sent[recipient] = new(big.Int).SetUint64(42) amount := common.Big1 ch, err := chbook.Issue(recipient, amount) if err == nil { - t.Errorf("expected insufficient funds error, got none") + t.Fatalf("expected insufficient funds error, got none") } chbook.balance = new(big.Int).Set(common.Big1) if chbook.Balance().Cmp(common.Big1) != 0 { - t.Errorf("expected: %v, got %v", "0", chbook.Balance()) + t.Fatalf("expected: %v, got %v", "0", chbook.Balance()) } ch, err = chbook.Issue(recipient, amount) @@ -102,9 +106,9 @@ func TestCheckbookFile(t *testing.T) { prvKey, _ := crypto.GenerateKey() sender := genAddr() path := "/tmp/checkbook.json" - chbook, err := NewChequebook(path, sender, prvKey, nil) + chbook, err := NewChequebook(path, sender, prvKey, newTestBackend()) if err != nil { - t.Errorf("expected no error, got %v", err) + t.Fatalf("expected no error, got %v", err) } recipient := genAddr() chbook.sent[recipient] = new(big.Int).SetUint64(42) @@ -112,7 +116,7 @@ func TestCheckbookFile(t *testing.T) { chbook.Save() - chbook, err = LoadChequebook(path, prvKey, nil) + chbook, err = LoadChequebook(path, prvKey, newTestBackend(), false) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -139,12 +143,12 @@ func TestVerifyErrors(t *testing.T) { sender0 := genAddr() sender1 := genAddr() path0 := "/tmp/checkbook0.json" - chbook0, err := NewChequebook(path0, sender0, prvKey, nil) + chbook0, err := NewChequebook(path0, sender0, prvKey, newTestBackend()) if err != nil { t.Errorf("expected no error, got %v", err) } path1 := "/tmp/checkbook1.json" - chbook1, err := NewChequebook(path1, sender1, prvKey, nil) + chbook1, err := NewChequebook(path1, sender1, prvKey, newTestBackend()) if err != nil { t.Errorf("expected no error, got %v", err) } @@ -179,7 +183,7 @@ func TestVerifyErrors(t *testing.T) { } received, err = chbox.Receive(ch1) - t.Log(err) + t.Logf("correct error: %v", err) if err == nil { t.Fatalf("expected receiver error, got none") } @@ -189,19 +193,19 @@ func TestVerifyErrors(t *testing.T) { t.Fatalf("expected no error, got %v", err) } received, err = chbox.Receive(ch2) - t.Log(err) + t.Logf("correct error: %v", err) if err == nil { t.Fatalf("expected sender error, got none") } _, err = chbook1.Issue(recipient0, new(big.Int).SetInt64(-1)) - t.Log(err) + t.Logf("correct error: %v", err) if err == nil { t.Fatalf("expected incorrect amount error, got none") } received, err = chbox.Receive(ch0) - t.Log(err) + t.Logf("correct error: %v", err) if err == nil { t.Fatalf("expected incorrect amount error, got none") } @@ -365,7 +369,7 @@ func TestCash(t *testing.T) { prvKey, _ := crypto.GenerateKey() sender := genAddr() path := "/tmp/checkbook.json" - chbook, err := NewChequebook(path, sender, prvKey, nil) + chbook, err := NewChequebook(path, sender, prvKey, newTestBackend()) if err != nil { t.Errorf("expected no error, got %v", err) } diff --git a/rpc/javascript.go b/rpc/javascript.go index c3f892d6f2..61a3ebda7a 100644 --- a/rpc/javascript.go +++ b/rpc/javascript.go @@ -527,6 +527,18 @@ web3._extend({ params: 1, inputFormatter: [null] }), + new web3._extend.Method({ + name: 'syncEnabled', + call: 'bzz_syncEnabled', + params: 1, + inputFormatter: [null] + }), + new web3._extend.Method({ + name: 'swapEnabled', + call: 'bzz_swapEnabled', + params: 1, + inputFormatter: [null] + }), new web3._extend.Method({ name: 'resolve', call: 'bzz_resolve', @@ -580,12 +592,11 @@ web3._extend({ params: 1, inputFormatter: [null] }), - new web3._extend.Method({ - name: 'info', - call: 'chequebook_info', - params: 1, - inputFormatter: [null] - }), + new web3._extend.Property({ + name: 'balance', + getter: 'chequebook_balance', + outputFormatter: web3._extend.utils.toDecimal + }), new web3._extend.Method({ name: 'cash', call: 'chequebook_cash', diff --git a/swarm/api/ethereum.go b/swarm/api/ethereum.go index 1027d1e768..16032c8c4c 100644 --- a/swarm/api/ethereum.go +++ b/swarm/api/ethereum.go @@ -56,7 +56,7 @@ func NewEthApi(ethereum *eth.Ethereum) *ethApi { // Note: this is not threadsafe, only called in JS single process and tests func (self *ethApi) UpdateState() (wait chan *big.Int) { wait = make(chan *big.Int) - self.state, _ = state.New(self.eth.BlockChain().GetBlockByNumber(0).Root(), self.eth.ChainDb()) + self.state, _ = state.New(self.eth.BlockChain().CurrentBlock().Root(), self.eth.ChainDb()) go func() { eventSub := self.eth.EventMux().Subscribe(core.ChainHeadEvent{}) @@ -130,6 +130,10 @@ func (self *ethApi) StorageAt(addr, storageAddr string) string { return self.state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() } +func (self *ethApi) BalanceAt(address common.Address) string { + return self.state.GetBalance(address).String() +} + func (self *ethApi) CodeAt(address string) string { return common.ToHex(self.state.GetCode(common.HexToAddress(address))) } diff --git a/swarm/api/testapi.go b/swarm/api/testapi.go index 06cbd6d83a..339b623089 100644 --- a/swarm/api/testapi.go +++ b/swarm/api/testapi.go @@ -16,5 +16,13 @@ func NewControl(api *Api, hive *network.Hive) *Control { } func (self *Control) BlockNetworkRead(on bool) { - self.hive.BlockNetworkRead(true) + self.hive.BlockNetworkRead(on) +} + +func (self *Control) SyncEnabled(on bool) { + self.hive.SyncEnabled(on) +} + +func (self *Control) SwapEnabled(on bool) { + self.hive.SwapEnabled(on) } diff --git a/swarm/cmd/swarm/gethup.sh b/swarm/cmd/swarm/gethup.sh index 01418f4b75..ab163db323 100644 --- a/swarm/cmd/swarm/gethup.sh +++ b/swarm/cmd/swarm/gethup.sh @@ -13,6 +13,8 @@ shift # created to the latest, so that monitoring be easier with the same filename # TODO: use this if GETH not set # GETH=geth +echo "ls -l $GETH" +ls -l $GETH # geth CLI params e.g., (dd=04, run=09) datetag=`date "+%c%y%m%d-%H%M%S"|cut -d ' ' -f 5` @@ -21,37 +23,46 @@ log=$root/log/$id.$datetag.log # /tmp/eth/04.09.log linklog=$root/log/$id.current.log # /tmp/eth/04.09.log stablelog=$root/log/$id.log # /tmp/eth/04.09.log password=$id # 04 -port=345$id # 34504 -bzzport=622$id # 62204 -rpcport=62$id # 6204 +port=303$id # 34504 +bzzport=322$id # 32204 +rpcport=32$id # 3204 mkdir -p $root/data +mkdir -p $root/enodes +mkdir -p $root/pids mkdir -p $root/log ln -sf "$log" "$linklog" # if we do not have an account, create one # will not prompt for password, we use the double digit instance id as passwd # NEVER EVER USE THESE ACCOUNTS FOR INTERACTING WITH A LIVE CHAIN -if [ ! -d "$root/keystore/$id" ]; then - # echo "create an account with password $id" - # echo "create an account with password $id [DO NOT EVER USE THIS ON LIVE]" - mkdir -p $root/keystore/$id +keystoredir="$datadir/keystore/" +echo "KeyStore dir: $keystoredir" +if [ ! -d "$keystoredir" ]; then + echo "create an account with password $id [DO NOT EVER USE THIS ON LIVE]" + # mkdir -p $datadir/keystore $GETH --datadir $datadir --password <(echo -n $id) account new >/dev/null 2>&1 -# create account with password 00, 01, ... + # create account with password 00, 01, ... # note that the account key will be stored also separately outside # datadir # this way you can safely clear the data directory and still keep your key # under `/keystore/dd - - cp -R "$datadir/keystore" $root/keystore/$id + # LS=`ls $datadir/keystore` + # echo $LS + while [ ! -d "$keystoredir" ]; do + echo "." + ((i++)) + if ((i>10)); then break; fi + sleep 1 + done + echo "copying keys $datadir/keystore $root/keystore/$id" + mkdir -p $root/keystore/$id + cp -R "$datadir/keystore/" $root/keystore/$id fi -# echo "copying keys $root/keystore/$id $datadir/keystore" -# ls $root/keystore/$id/keystore/ $datadir/keystore - -# mkdir -p $datadir/keystore +# # mkdir -p $datadir/keystore # if [ ! -d "$datadir/keystore" ]; then -# echo "copying keys $root/keystore/$id $datadir/keystore" -cp -R $root/keystore/$id/keystore/ $datadir/keystore/ +# echo "copying keys $root/keystore/$id $datadir/keystore" +# cp -R $root/keystore/$id/keystore/ $datadir/keystore/ # fi @@ -63,10 +74,10 @@ else pattern='\[\:\:\]' fi -# echo -n "enode for instance $id... " +geth="$GETH --datadir $datadir --port $port" +# echo -n "enode for instance $id... " if [ ! "$GETH" = "" ] && [ ! -f $root/enodes/$id.enode ]; then - geth="$GETH --datadir $root/data/$id --port $port" cmd="$geth js <(echo 'console.log(admin.nodeInfo.enode); exit();') " # echo $cmd '2>/dev/null |grep enode | perl -pe "s/'$pattern'/'$ip_addr'/g" | perl -pe "s/^/\"/; s/\s*$/\"/;" > '$root/enodes/$id.enode eval $cmd 2>/dev/null |grep enode | perl -pe "s/$pattern/$ip_addr/g" | perl -pe "s/^/\"/; s/\s*\$/\"/;" > $root/enodes/$id.enode @@ -86,17 +97,17 @@ if [ ! -f $root/pids/$id.pid ]; then # - listening on port 303dd, (like 30300, 30301, ...) # - with the account unlocked # - launching json-rpc server on port 81dd (like 8100, 8101, 8102, ...) - BZZKEY=`$GETH --datadir=$datadir account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print $1'` + # echo "BZZKEY=$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'" + BZZKEY=`$geth account list|head -n1|perl -ne '/([a-f0-9]{40})/ && print \$1'` echo -n "starting instance $id ($BZZKEY @ $datadir )..." - # echo "$GETH --datadir=$datadir \ - # --identity=$id \ - # --bzzaccount=$BZZKEY --bzzport=$bzzport \ - # --port=$port \ - # --unlock=$BZZKEY \ - # --password=<(echo -n $id) \ - # --rpc --rpcport=$rpcport --rpccorsdomain='*' $* \ - # 2>&1 | tee "$stablelog" > "$log" & # comment out if you pipe it to a tty etc. - # " >&2 + echo "$geth \ + --identity=$id \ + --bzzaccount=$BZZKEY --bzzport=$bzzport \ + --unlock=$BZZKEY \ + --password=<(echo -n $id) \ + --rpc --rpcport=$rpcport --rpccorsdomain='*' $* \ + 2>&1 | tee "$stablelog" > "$log" & # comment out if you pipe it to a tty etc. + " >&2 if [ -f $log ] && [ -f $root/stablelog ]; then cp $stablelog `cat $root/prevlog` @@ -118,6 +129,7 @@ if [ ! -f $root/pids/$id.pid ]; then # echo $pid > $root/pids/$id.pid #echo $! > $root/pids/$id.pid while true; do + # echo $GETH --ipcpath=$datadir/geth.ipc --exec="net" attach $GETH --ipcpath=$datadir/geth.ipc --exec="net" attach > /dev/null 2>&1 && break sleep 1 echo -n "." diff --git a/swarm/cmd/swarm/netstatconf.sh b/swarm/cmd/swarm/netstatconf.sh index 131c033a26..82657cc900 100644 --- a/swarm/cmd/swarm/netstatconf.sh +++ b/swarm/cmd/swarm/netstatconf.sh @@ -1,8 +1,8 @@ # !/bin/bash -# bash intelligence +# bash netstatconf.sh # sets up a eth-net-intelligence app.json for a local ethereum network cluster of nodes -# - is the number of clusters +# - is the number of nodes in the cluster # - is a prefix for the node names as will appear in the listing # - is the eth-netstats server # - is the eth-netstats secret diff --git a/swarm/cmd/swarm/swarm.sh b/swarm/cmd/swarm/swarm.sh index d4a732d0d6..116fa7da23 100644 --- a/swarm/cmd/swarm/swarm.sh +++ b/swarm/cmd/swarm/swarm.sh @@ -37,31 +37,8 @@ shift # ip_addr=`curl ipecho.net/plain 2>/dev/null;echo ` # echo "external IP: $ip_addr" -swarmoptions='--vmdebug=false --maxpeers=20 --dev --shh=false --nodiscover --ipcexp' - - -function needs { - id=$1 - keyfile=$2 - target=$3 - dir=`dirname $3` - dest=$dir/down - mkdir -p $dest - file=$dest/`basename $target` - rm -f $file - echo -n "download to '$file'...waiting for root hash in '$keyfile'..." - while true; do - if [ -f $keyfile ] && [ ! -z $keyfile ]; then - break - fi - sleep 1 - echo -n "." - done - key=`cat $keyfile` - echo " => $key" - download $id $key $dest && cmp --silent $file $target && echo "PASS" || echo "FAIL" - # && ls -l $keyfile $file $target -} +swarmoptions='--vmdebug=false --maxpeers=20 --dev --shh=false --nodiscover' +tmpdir=/tmp function attach { id=$1 @@ -145,7 +122,6 @@ function init { reset all cluster $* stop all - sleep 5 cluster $* } @@ -163,7 +139,7 @@ function reset { function cluster { N=$1 shift -# --vmodule=netstore=6,depo=6,forwarding=6,hive=5,dpa=6,dpa=6,http=6,syncb=6,syncer=6,protocol=6,swap=6,chequebook=6 + vmodule='--vmodule=network/*=6,syncb=6,syncer=6,protocol=6,swap=6,chequebook=6' echo "launching cluster of $N instances" # cmd="bash $srcdir/gethcluster.sh $root $network_id $N '' $swarmoptions $*" # echo $cmd @@ -192,22 +168,46 @@ function cluster { id=`printf "%02d" $i` mkdir -p $dir/data/$id echo "launching node $i/$N ---> tail-f $dir/log/$id.log" - start $id $* + start $id $vmodule $* done } +function needs { + id=$1 + keyfile=$2 + target=$3 + dir=`dirname $3` + dest=$tmpdir/down + mkdir -p $dest + file=$dest/`basename $target` + rm -f $file + echo -n "waiting for root hash in '$keyfile'..." + while true; do + if [ -f $keyfile ] && [ ! -z $keyfile ]; then + break + fi + sleep 1 + echo -n "." + done + key=`cat $keyfile|tr -d \"` + echo " => $key" + download $id $key $dest && cmp --silent $file $target && echo "PASS" || echo "FAIL" + # && ls -l $keyfile $file $target +} + + function up { #port, file echo "Upload file '$2' to node $1... " 1>&2 file=`basename $2` - key=`attach $1 "--exec 'bzz.upload(\"$2\", \"$file\")'"|tail -n1` + attach $1 "--exec 'bzz.upload(\"$2\", \"$file\")'"|tail -n1> /tmp/key # key=`bash swarm/cmd/bzzup.sh $2 86$1` - echo "-> $key" - echo -n `eval echo $key` + cat /tmp/key } function download { echo "download to '$3'" + echo attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'" attach $1 "--exec 'bzz.download(\"$2\", \"$3\")'" > /dev/null } @@ -234,14 +234,22 @@ function clean { #index } function info { + echo "swarm node information" echo "ROOTDIR: $root" - echo "DATADIR: $root/data/$1" - echo "LOGFILE: $root/log/$1.log" + echo "DATADIR: $root/$network_id/data/$1" + echo "LOGFILE: $root/$network_id/log/$1.log" + echo "HTTPAPI: http://localhost:32$1" + echo "ETHPORT: 302$1" + echo "RPCPORT: 322$1" + echo "ACCOUNT:" 0x`ls -1 $root/$network_id/data/$1/bzz` + echo "CHEQUEB:" `cat $root/$network_id/data/$1/bzz/*/config.json|grep Contract|awk -F\" '{print $4}'` } case $cmd in "info" ) info $*;; + "clean" ) + clean $*;; "needs" ) needs $*;; "up" ) @@ -264,4 +272,6 @@ case $cmd in attach $*;; "log" ) log $*;; + "less" ) + less $*;; esac diff --git a/swarm/cmd/swarm/test.sh b/swarm/cmd/swarm/test.sh index 9321c581ec..30baeb278a 100644 --- a/swarm/cmd/swarm/test.sh +++ b/swarm/cmd/swarm/test.sh @@ -5,14 +5,12 @@ TEST_NAME=`basename $0 .sh` export SWARM_BIN=$TEST_DIR/../../cmd/swarm export GETH=$SWARM_BIN/../../../geth -export NETWORKID=622$TEST_NAME -export TMPDIR=/tmp/swarm-test +export NETWORKID=322$TEST_NAME +export TMPDIR=~/BZZ/swarm-test export DATA_ROOT=$TMPDIR/$NETWORKID # alias swarm='bash $SWARM_BIN/swarm.sh $DATA_ROOT $NETWORKID' EXTRA_ARGS=$* -rm -rf $DATA_ROOT - wait=1 function swarm { diff --git a/swarm/network/depo.go b/swarm/network/depo.go index eeb50f4595..3e3a410411 100644 --- a/swarm/network/depo.go +++ b/swarm/network/depo.go @@ -5,9 +5,9 @@ import ( "encoding/binary" "time" - "github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/swarm/storage" ) // Handler for storage/retrieval related protocol requests @@ -51,6 +51,7 @@ func (self *Depo) HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error if err != nil { return err } + // set peers state to persist p.syncState = req.State return nil } diff --git a/swarm/network/forwarding.go b/swarm/network/forwarding.go index c72fa0cc10..bc027b353e 100644 --- a/swarm/network/forwarding.go +++ b/swarm/network/forwarding.go @@ -42,25 +42,26 @@ var searchTimeout = 3 * time.Second func (self *forwarder) Retrieve(chunk *storage.Chunk) { peers := self.hive.getPeers(chunk.Key, 0) glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers)) +OUT: for _, p := range peers { glog.V(logger.Detail).Infof("[BZZ] forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p) - var req *retrieveRequestMsgData - OUT: for _, recipients := range chunk.Req.Requesters { for _, recipient := range recipients { req := recipient.(*retrieveRequestMsgData) if req.from.Addr() == p.Addr() { - break OUT + continue OUT } } } - if req != nil { - if err := p.swap.Add(-1); err == nil { - p.retrieve(req) - break - } else { - glog.V(logger.Warn).Infof("[BZZ] forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err) - } + req := &retrieveRequestMsgData{ + Key: chunk.Key, + Id: generateId(), + } + if err := p.swap.Add(-1); err == nil { + p.retrieve(req) + break OUT + } else { + glog.V(logger.Warn).Infof("[BZZ] forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err) } } } diff --git a/swarm/network/hive.go b/swarm/network/hive.go index 56330d6862..88c3e350f6 100644 --- a/swarm/network/hive.go +++ b/swarm/network/hive.go @@ -32,8 +32,12 @@ type Hive struct { path string toggle chan bool more chan bool - blockRead bool - blockWrite bool + + // for testing only + swapEnabled bool + syncEnabled bool + blockRead bool + blockWrite bool } const ( @@ -62,16 +66,26 @@ func NewHiveParams(path string) *HiveParams { } } -func NewHive(addr common.Hash, params *HiveParams) *Hive { +func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { kad := kademlia.New(kademlia.Address(addr), params.KadParams) return &Hive{ callInterval: params.CallInterval, kad: kad, addr: kad.Addr(), path: params.KadDbPath, + swapEnabled: swapEnabled, + syncEnabled: syncEnabled, } } +func (self *Hive) SyncEnabled(on bool) { + self.syncEnabled = on +} + +func (self *Hive) SwapEnabled(on bool) { + self.swapEnabled = on +} + func (self *Hive) BlockNetworkRead(on bool) { self.blockRead = on } diff --git a/swarm/network/messages.go b/swarm/network/messages.go index bf11479c6f..44edceaa41 100644 --- a/swarm/network/messages.go +++ b/swarm/network/messages.go @@ -109,7 +109,7 @@ type retrieveRequestMsgData struct { MaxSize uint64 // maximum size of delivery accepted MaxPeers uint64 // maximum number of peers returned Timeout uint64 // the longest time we are expecting a response - timeout *time.Time // [not serialied]} + timeout *time.Time // [not serialised]} from *peer // } @@ -234,7 +234,7 @@ peer/protocol instance when the node is registered by hive as online{ */ type syncRequestMsgData struct { - SyncState *syncState `rlp:"nil"` + SyncState *syncState `rlp:nil` } func (self *syncRequestMsgData) String() string { diff --git a/swarm/network/protocol.go b/swarm/network/protocol.go index 587148e1df..213b6ff266 100644 --- a/swarm/network/protocol.go +++ b/swarm/network/protocol.go @@ -78,11 +78,11 @@ type bzz struct { swap *swap.Swap // swap instance for the peer connection swapParams *bzzswap.SwapParams // swap settings both local and remote - swapEnabled bool // flag to switch off SWAP (will be via Caps in handshake) + swapEnabled bool // flag to enable SWAP (will be set via Caps in handshake) + syncEnabled bool // flag to enable SYNC (will be set via Caps in handshake) syncer *syncer // syncer instance for the peer connection syncParams *SyncParams // syncer params syncState *syncState // outgoing syncronisation state (contains reference to remote peers db counter) - syncEnabled bool // flag to enable syncing } // interface type for handler of storage/retrieval related requests coming @@ -152,7 +152,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, hive *Hive, dbacce }, swapParams: sp, syncParams: sy, - swapEnabled: true, + swapEnabled: hive.swapEnabled, syncEnabled: true, } @@ -273,8 +273,6 @@ func (self *bzz) handle() error { if err != nil { return self.protoError(ErrDecode, "->msg %v: %v", msg, err) } - // set peers state to persist - self.syncState = req.State case deliveryRequestMsg: // response to syncKeysMsg hashes filtered not existing in db @@ -291,12 +289,14 @@ func (self *bzz) handle() error { case paymentMsg: // swap protocol message for payment, Units paid for, Cheque paid with - var req paymentMsgData - if err := msg.Decode(&req); err != nil { - return self.protoError(ErrDecode, "->msg %v: %v", msg, err) + if self.swapEnabled { + var req paymentMsgData + if err := msg.Decode(&req); err != nil { + return self.protoError(ErrDecode, "->msg %v: %v", msg, err) + } + glog.V(logger.Debug).Infof("[BZZ] incoming payment: %s", req.String()) + self.swap.Receive(int(req.Units), req.Promise) } - glog.V(logger.Debug).Infof("[BZZ] incoming payment: %s", req.String()) - self.swap.Receive(int(req.Units), req.Promise) default: // no other message is allowed @@ -366,10 +366,9 @@ func (self *bzz) handleStatus() (err error) { self.hive.addPeer(&peer{bzz: self}) // hive sets syncstate so sync should start after node added - if self.syncEnabled { - glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState) - self.syncRequest() - } + glog.V(logger.Info).Infof("[BZZ] syncronisation request sent with %v", self.syncState) + self.syncRequest() + return nil } @@ -382,9 +381,10 @@ func (self *bzz) sync(state *syncState) error { cnt := self.dbAccess.counter() remoteaddr := self.remoteAddr.Addr start, stop := self.hive.kad.KeyRange(remoteaddr) + + // an explicitly received nil syncstate disables syncronisation if state == nil { - state = newSyncState(start, stop, cnt) - glog.V(logger.Warn).Infof("[BZZ] peer %08x provided no sync state, setting up full sync: %v\n", remoteaddr[:4], state) + self.syncEnabled = false } else { state.synced = make(chan bool) state.SessionAt = cnt @@ -399,7 +399,7 @@ func (self *bzz) sync(state *syncState) error { storage.Key(remoteaddr[:]), self.dbAccess, self.unsyncedKeys, self.store, - self.syncParams, state, + self.syncParams, state, func() bool { return self.syncEnabled }, ) if err != nil { return self.protoError(ErrSync, "%v", err) @@ -448,8 +448,9 @@ func (self *bzz) store(req *storeRequestMsgData) error { } func (self *bzz) syncRequest() error { - req := &syncRequestMsgData{ - SyncState: self.syncState, + req := &syncRequestMsgData{} + if self.hive.syncEnabled { + req.SyncState = self.syncState } return self.send(syncRequestMsg, req) } diff --git a/swarm/network/syncer.go b/swarm/network/syncer.go index 4a06655950..6a80995a90 100644 --- a/swarm/network/syncer.go +++ b/swarm/network/syncer.go @@ -6,7 +6,6 @@ import ( "fmt" "path/filepath" - "github.com/ethereum/go-ethereum/common/kademlia" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/swarm/storage" @@ -133,6 +132,7 @@ func NewSyncParams(bzzdir string) *SyncParams { // syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding type syncer struct { *SyncParams // sync parameters + syncF func() bool // if syncing is needed key storage.Key // remote peers address key state *syncState // sync state for our dbStore syncStates chan *syncState // different stages of sync @@ -165,6 +165,7 @@ func newSyncer( store func(*storeRequestMsgData) error, params *SyncParams, state *syncState, + syncF func() bool, ) (*syncer, error) { syncBufferSize := params.SyncBufferSize @@ -172,6 +173,7 @@ func newSyncer( dbBatchSize := params.RequestDbBatchSize self := &syncer{ + syncF: syncF, key: remotekey, dbAccess: dbAccess, syncStates: make(chan *syncState, 20), @@ -191,35 +193,19 @@ func newSyncer( // initialise a syncdb instance for each priority queue self.queues[i] = newSyncDb(db, remotekey, uint(i), syncBufferSize, dbBatchSize, self.deliver(uint(i))) } - self.state = state glog.V(logger.Info).Infof("[BZZ] syncer started: %v", state) // launch chunk delivery service go self.syncDeliveries() // launch sync task manager - go self.sync() + if self.syncF() { + go self.sync() + } // process unsynced keys to broadcast go self.syncUnsyncedKeys() return self, nil } -// newSyncState returns a default sync state given local and remote -// addresses and db count -func newSyncState(start, stop kademlia.Address, count uint64) *syncState { - // -> (] keyrange for db iterator -- interval open from the left - // storage count range --- interval open from the right - return &syncState{ - DbSyncState: &storage.DbSyncState{ - Start: storage.Key(start[:]), - Stop: storage.Key(stop[:]), - First: 0, - Last: count, - }, - SessionAt: count, - synced: make(chan bool), - } -} - // metadata serialisation func encodeSync(state *syncState) (*json.RawMessage, error) { data, err := json.MarshalIndent(state, "", " ") @@ -238,7 +224,7 @@ func decodeSync(meta *json.RawMessage) (*syncState, error) { if len(data) == 0 { return nil, fmt.Errorf("unable to deserialise sync state from ") } - state := newSyncState(kademlia.Address{}, kademlia.Address{}, 0) + state := &syncState{} err := json.Unmarshal(data, state) return state, err } @@ -625,10 +611,12 @@ func (self *syncer) addRequest(req interface{}, ty int) { priority := self.SyncPriorities[ty] // sync mode for this type ON - if self.SyncModes[ty] { - self.addKey(req, priority, self.quit) - } else { - self.addDelivery(req, priority, self.quit) + if self.syncF() || ty == DeliverReq { + if self.SyncModes[ty] { + self.addKey(req, priority, self.quit) + } else { + self.addDelivery(req, priority, self.quit) + } } } diff --git a/swarm/services/swap/swap.go b/swarm/services/swap/swap.go index cc44e263c2..5bd343efc6 100644 --- a/swarm/services/swap/swap.go +++ b/swarm/services/swap/swap.go @@ -254,7 +254,7 @@ func (self *SwapParams) newChequebookFromContract(path string, backend chequeboo } chbookpath := filepath.Join(path, "chequebooks", hexkey+".json") - self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend) + self.chbook, err = chequebook.LoadChequebook(chbookpath, self.privateKey, backend, true) if err != nil { self.chbook, err = chequebook.NewChequebook(chbookpath, self.Contract, self.privateKey, backend) diff --git a/swarm/swarm.go b/swarm/swarm.go index 13e862ddc9..645f9a986e 100644 --- a/swarm/swarm.go +++ b/swarm/swarm.go @@ -43,7 +43,7 @@ type Swarm struct { // creates a new swarm service instance // implements node.Service -func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool) (self *Swarm, err error) { +func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled, syncEnabled bool) (self *Swarm, err error) { if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { return nil, fmt.Errorf("empty public key") @@ -77,6 +77,8 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool) self.hive = network.NewHive( common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address) config.HiveParams, // configuration parameters + swapEnabled, // SWAP enabled + syncEnabled, // syncronisation enabled ) glog.V(logger.Debug).Infof("[BZZ] Set up swarm network with Kademlia hive") @@ -110,12 +112,15 @@ func NewSwarm(stack *node.ServiceContext, config *api.Config, swapEnabled bool) glog.V(logger.Debug).Infof("[BZZ] -> Web3 virtual server API") // set chequebook + if swapEnabled { err = self.SetChequebook(backend) if err != nil { return nil, fmt.Errorf("Unable to set chequebook for SWAP: %v", err) } - glog.V(logger.Debug).Infof("[BZZ] -> cheque book for SWAP") + glog.V(logger.Debug).Infof("[BZZ] -> cheque book for SWAP: %v", self.config.Swap.Chequebook()) + } else { + glog.V(logger.Debug).Infof("[BZZ] SWAP disabled: no cheque book set") } return self, nil } @@ -206,7 +211,7 @@ func (self *Swarm) APIs() []rpc.API { rpc.API{Namespace, Version, api.NewControl(self.api, self.hive), false}, // rpc.API{Namespace, Version, api.NewAdmin(self), false}, // TODO: external apis exposed - rpc.API{"chequebook", chequebook.Version, chequebook.NewApi(self.config.Swap.Chequebook()), true}, + rpc.API{"chequebook", chequebook.Version, chequebook.NewApi(self.config.Swap.Chequebook), true}, } } diff --git a/swarm/test/swap/00.sh b/swarm/test/swap/00.sh new file mode 100644 index 0000000000..9685915205 --- /dev/null +++ b/swarm/test/swap/00.sh @@ -0,0 +1,29 @@ +#!/bin/bash +echo "TEST swap/00:" +echo " two nodes that do not sync and do not have any funds" +echo " cannot retrieve content from each other" + +dir=`dirname $0` +source $dir/../../cmd/swarm/test.sh + +FILE_00=/tmp/1K.0 +randomfile 1 > $FILE_00 +ls -l $FILE_00 +mininginterval=50 +key=/tmp/key + +swarm init 2 +sleep $wait +swarm attach 00 -exec "'bzz.noSync(true)'" +swarm attach 01 -exec "'bzz.noSync(true)'" +swarm up 00 $FILE_00|tail -n1 > $key +swarm needs 00 $key $FILE_00 +swarm needs 01 $key $FILE_00 | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3" + +FILE_01=/tmp/1K.1 +randomfile 1 > $FILE_01 +swarm up 01 $FILE_01|tail -1 > $key +swarm needs 01 $key $FILE_01 +swarm needs 00 $key $FILE_01 | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3" + +swarm stop all \ No newline at end of file diff --git a/swarm/test/swap/01.sh b/swarm/test/swap/01.sh new file mode 100644 index 0000000000..d77a56b68f --- /dev/null +++ b/swarm/test/swap/01.sh @@ -0,0 +1,62 @@ +echo "TEST swap/01:" +echo " two nodes that do not sync but have enough funds" +echo " after syncing, can retrieve content from each other" + +dir=`dirname $0` +source $dir/../../cmd/swarm/test.sh + +file=/tmp/test.file +mininginterval=120 +key=/tmp/key +logargs="--verbosity=0 --vmodule='miner/*=0,swarm/services/*=6,swarm/swarm=6,common/chequebook/*=6,swarm/network/depo=6,swarm/network/forwarding=6'" +# logargs='--verbosity=6' + + +# swarm init 2 --mine --bzznosync $logargs +# swarm stop all + +swarm start 00 --mine --bzznosync $logargs +swarm start 01 --mine --bzznosync $logargs + +# echo "Mining some ether..." +# sleep $mininginterval + +swarm attach 00 -exec "'eth.getBalance(eth.accounts[0])'" +swarm attach 01 -exec "'eth.getBalance(eth.accounts[0])'" +swarm attach 00 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +swarm attach 01 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +swarm attach 00 -exec "'chequebook.balance'" +swarm attach 01 -exec "'chequebook.balance'" + +randomfile 10 > $file +swarm up 00 $file|tail -n1 > $key +swarm needs 01 $key $file +swarm info 01 + +swarm attach 00 -exec "'eth.getBalance(eth.accounts[0])'" +swarm attach 01 -exec "'eth.getBalance(eth.accounts[0])'" +swarm attach 00 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +swarm attach 01 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +swarm attach 00 -exec "'chequebook.balance'" +swarm attach 01 -exec "'chequebook.balance'" + +# randomfile 20 > $file +# swarm up 01 $file|tail -n1 > $key +# swarm needs 00 $key $file + +# swarm attach 00 -exec "'eth.getBalance(eth.accounts[0])'" +# swarm attach 01 -exec "'eth.getBalance(eth.accounts[0])'" +# swarm attach 00 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +# swarm attach 01 -exec "'eth.getBalance(bzz.info().Swap.Contract)'" +# swarm attach 00 -exec "'chequebook.balance'" +# swarm attach 01 -exec "'chequebook.balance'" + +# randomfile 10 > $file +# swarm up 00 $file|tail -n1 > $key +# swarm needs 01 $key $file | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3" + +# randomfile 10 > $file +# swarm up 01 $file|tail -n1 > $key +# swarm needs 00 $key $file | tail -1| grep -ql "PASS" && echo "FAIL" || echo "PASS <3" + +swarm stop all