Commit ffc450d5 authored by Zahoor Mohamed's avatar Zahoor Mohamed Committed by GitHub

fix forever loop timeout issue (#511)

* fix forever loop timeout issue
parent cc5412e0
...@@ -11,8 +11,9 @@ import ( ...@@ -11,8 +11,9 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/ethersphere/bee/pkg/swarm"
bmtlegacy "github.com/ethersphere/bmt/legacy" bmtlegacy "github.com/ethersphere/bmt/legacy"
"github.com/ethersphere/bee/pkg/swarm"
) )
// Topic is an alias for a 32 byte fixed-size array which contains an encoding of a message topic // Topic is an alias for a 32 byte fixed-size array which contains an encoding of a message topic
...@@ -41,7 +42,7 @@ const ( ...@@ -41,7 +42,7 @@ const (
NonceSize = 32 NonceSize = 32
LengthSize = 2 LengthSize = 2
TopicSize = 32 TopicSize = 32
MinerTimeout = 2 // seconds MinerTimeout = 5 // seconds after which the mining will fail
) )
// NewTopic creates a new Topic variable with the given input string // NewTopic creates a new Topic variable with the given input string
...@@ -163,32 +164,46 @@ func (m *Message) toChunk(targets Targets, span []byte) (swarm.Chunk, error) { ...@@ -163,32 +164,46 @@ func (m *Message) toChunk(targets Targets, span []byte) (swarm.Chunk, error) {
return nil, err return nil, err
} }
// hash chunk fields with different nonces until an acceptable one is found errC := make(chan error)
for start := time.Now(); ; { var hash, s []byte
s := append(append(span, nonce...), b...) // serialize chunk fields go func() {
hash1, err := hashBytes(s) defer close(errC)
// mining operation: hash chunk fields with different nonces until an acceptable one is found
for {
s = append(append(span, nonce...), b...) // serialize chunk fields
hash, err = hashBytes(s)
if err != nil { if err != nil {
return nil, err errC <- err
return
} }
// take as much of the hash as the targets are long // take as much of the hash as the targets are long
if contains(targets, hash1[:targetsLen]) { if contains(targets, hash[:targetsLen]) {
// if nonce found, stop loop and return chunk // if nonce found, stop loop and return chunk
return swarm.NewChunk(swarm.NewAddress(hash1), s), nil errC <- nil
return
} }
// else, add 1 to nonce and try again // else, add 1 to nonce and try again
nonceInt.Add(nonceInt, big.NewInt(1)) nonceInt.Add(nonceInt, big.NewInt(1))
// loop around in case of overflow after 256 bits // loop around in case of overflow after 256 bits
if nonceInt.BitLen() > (NonceSize * swarm.SpanSize) { if nonceInt.BitLen() > (NonceSize * swarm.SpanSize) {
// Test if timeout after after every 256 iteration
if time.Since(start) > (MinerTimeout * time.Second) {
break
}
nonceInt = big.NewInt(0) nonceInt = big.NewInt(0)
} }
nonce = padBytesLeft(nonceInt.Bytes()) // pad in case Bytes call is not 32 bytes long nonce = padBytesLeft(nonceInt.Bytes()) // pad in case Bytes call is not 32 bytes long
} }
}()
// checks whether the mining is completed or times out
select {
case err := <-errC:
if err == nil {
return swarm.NewChunk(swarm.NewAddress(hash), s), nil
}
return nil, err
case <-time.After(MinerTimeout * time.Second):
return nil, ErrMinerTimeout return nil, ErrMinerTimeout
}
} }
// hashBytes hashes the given serialization of chunk fields with the hashing func // hashBytes hashes the given serialization of chunk fields with the hashing func
......
...@@ -6,6 +6,7 @@ package trojan_test ...@@ -6,6 +6,7 @@ package trojan_test
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/binary" "encoding/binary"
"reflect" "reflect"
"testing" "testing"
...@@ -16,11 +17,11 @@ import ( ...@@ -16,11 +17,11 @@ import (
) )
// arbitrary targets for tests // arbitrary targets for tests
var t1 = trojan.Target([]byte{57, 120}) var t1 = trojan.Target([]byte{57})
var t2 = trojan.Target([]byte{209, 156}) var t2 = trojan.Target([]byte{209})
var t3 = trojan.Target([]byte{156, 38}) var t3 = trojan.Target([]byte{156})
var t4 = trojan.Target([]byte{89, 19}) var t4 = trojan.Target([]byte{89})
var t5 = trojan.Target([]byte{22, 129}) var t5 = trojan.Target([]byte{22})
var testTargets = trojan.Targets([]trojan.Target{t1, t2, t3, t4, t5}) var testTargets = trojan.Targets([]trojan.Target{t1, t2, t3, t4, t5})
// arbitrary topic for tests // arbitrary topic for tests
...@@ -124,6 +125,24 @@ func TestWrapFail(t *testing.T) { ...@@ -124,6 +125,24 @@ func TestWrapFail(t *testing.T) {
} }
} }
// TestWrapTimeout tests for mining timeout and avoid forever loop
func TestWrapTimeout(t *testing.T) {
m := newTestMessage(t)
// a large target will take more than MinerTimeout seconds, so timeout error will be triggered
buf := make([]byte, swarm.SectionSize)
_, err := rand.Read(buf)
if err != nil {
t.Fatal(err)
}
target := trojan.Target(buf)
targets := trojan.Targets([]trojan.Target{target})
if _, err := m.Wrap(targets); err != trojan.ErrMinerTimeout {
t.Fatalf("expected error when having lengthy target to be %q, but got %v", trojan.ErrMinerTimeout, err)
}
}
// TestPadBytes tests that different types of byte slices are correctly padded with leading 0s // TestPadBytes tests that different types of byte slices are correctly padded with leading 0s
// all slices are interpreted as big-endian // all slices are interpreted as big-endian
func TestPadBytes(t *testing.T) { func TestPadBytes(t *testing.T) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment