swap integration test

* add bzz.syncEnabled (hive.syncEnabled -> testapi/SyncEnabled | bzznosync param)) triggers nil sync State request to peer
* syncEnabled false then historical items are not delivered
* add swap initial scenarios, no funds, no retrieval; chequebook autodeployment; available funds -> retrieval
* remove -ipcexp option from swarm script
* tweaks to swarm/cmd files
* add Balance to chequebook api
* set chequebook balance from blockchain
* fix chequebook tests after balance check
* log balance when chequebook created
* fix state setting in ethereum backend
* add balanceAt to backend
This commit is contained in:
zelig 2016-02-05 13:53:52 +08:00
parent 863fbb4c37
commit 5714724bab
22 changed files with 360 additions and 163 deletions

View file

@ -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,

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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',

View file

@ -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)))
}

View file

@ -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)
}

View file

@ -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 `<rootdir>/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 "."

View file

@ -1,8 +1,8 @@
# !/bin/bash
# bash intelligence <destination_app_json_path> <number_of_nodes> <name_prefix> <ws_server> <ws_secret>
# bash netstatconf.sh <number_of_nodes> <name_prefix> <ws_server> <ws_secret>
# sets up a eth-net-intelligence app.json for a local ethereum network cluster of nodes
# - <number_of_clusters> is the number of clusters
# - <number_of_nodes> is the number of nodes in the cluster
# - <name_prefix> is a prefix for the node names as will appear in the listing
# - <ws_server> is the eth-netstats server
# - <ws_secret> is the eth-netstats secret

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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 <nil>")
}
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)
}
}
}

View file

@ -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)

View file

@ -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},
}
}

29
swarm/test/swap/00.sh Normal file
View file

@ -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

62
swarm/test/swap/01.sh Normal file
View file

@ -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