diff --git a/.goreleaser.yml b/.goreleaser.yml
index 50e6ac8059d29423f2765a50a12e22658c92f9c9..d2686e2681451bd882116ef60464ea8ac4063b2d 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -76,6 +76,7 @@ archives:
       - README.md
       - LICENSE
       - config.json
+      - snapshot.bin
 
 # Checksum
 checksum:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78697c4c20a859bd03bacd130665d5e65617c168..8e606a4cb72916dc9ba683aa089cd131eec7b2d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v0.2.1 - 2020-07-01
+* Adds PoW requirement to faucet payloads
+* Adds tips broadcaster to ensure that all chains are getting solidified
+* Fixes being able to send a double-spend via one node
+* **Breaking**: bumps network and database versions
+
 # v0.2.0 - 2020-06-30
 * Adds the value transfer dApp:
     * New binary transaction layout
diff --git a/README.md b/README.md
index ad69bd3d9a749169fe44a38bfeede66cfcbe093f..0349a0f82dea132ecc5858019b34f54f7806612e 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ You can find more info about this on our [client-lib](https://github.com/iotaled
 
 ## Getting started
 
-You can find tutorials on how to [setup a GoShimmer node](https://github.com/iotaledger/goshimmer/wiki/Setting-up-a-GoShimmer-node), [writing a dApp](https://github.com/iotaledger/goshimmer/wiki/How-to-create-a-simple-dApp), [obtaining tokens from the faucet](https://github.com/iotaledger/goshimmer/wiki/How-to-obtain-tokens-from-the-faucet) and more on our [wiki](https://github.com/iotaledger/goshimmer/wiki).
+You can find tutorials on how to [setup a GoShimmer node](https://github.com/iotaledger/goshimmer/wiki/Setup-up-a-GoShimmer-node-(Joining-the-pollen-testnet)), [writing a dApp](https://github.com/iotaledger/goshimmer/wiki/How-to-create-a-simple-dApp), [obtaining tokens from the faucet](https://github.com/iotaledger/goshimmer/wiki/How-to-obtain-tokens-from-the-faucet) and more on our [wiki](https://github.com/iotaledger/goshimmer/wiki).
 
 ## Supporting the project
 
diff --git a/config.default.json b/config.default.json
index 5f172cac790624ecfbedbd7aeb46c5d84ddcde82..924792efdddbda2959662dc680a7798257cd55a7 100644
--- a/config.default.json
+++ b/config.default.json
@@ -40,7 +40,10 @@
     "bindAddress": "0.0.0.0:10895"
   },
   "gossip": {
-    "port": 14666
+    "port": 14666,
+    "tipsBroadcaster": {
+      "interval": "10s"
+    }
   },
   "logger": {
     "level": "info",
diff --git a/dapps/faucet/dapp.go b/dapps/faucet/dapp.go
index fff78faa8d426dd150777ced9f246bc736df744a..2c930afc9dd428cd0fd6f0124a9f6a0d47642da0 100644
--- a/dapps/faucet/dapp.go
+++ b/dapps/faucet/dapp.go
@@ -1,6 +1,7 @@
 package faucet
 
 import (
+	"crypto"
 	"runtime"
 	"sync"
 	"time"
@@ -9,6 +10,7 @@ import (
 	faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
+	"github.com/iotaledger/goshimmer/packages/pow"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
 	"github.com/iotaledger/goshimmer/plugins/config"
 	"github.com/iotaledger/goshimmer/plugins/messagelayer"
@@ -32,12 +34,19 @@ const (
 	// CfgFaucetMaxTransactionBookedAwaitTimeSeconds defines the time to await for the transaction fulfilling a funding request
 	// to become booked in the value layer.
 	CfgFaucetMaxTransactionBookedAwaitTimeSeconds = "faucet.maxTransactionBookedAwaitTimeSeconds"
+	// CfgFaucetPoWDifficulty defines the PoW difficulty for faucet payloads.
+	CfgFaucetPoWDifficulty = "faucet.powDifficulty"
+	// CfgFaucetBlacklistCapacity holds the maximum amount the address blacklist holds.
+	// An address for which a funding was done in the past is added to the blacklist and eventually is removed from it.
+	CfgFaucetBlacklistCapacity = "faucet.blacklistCapacity"
 )
 
 func init() {
 	flag.String(CfgFaucetSeed, "", "the base58 encoded seed of the faucet, must be defined if this dApp is enabled")
 	flag.Int(CfgFaucetTokensPerRequest, 1337, "the amount of tokens the faucet should send for each request")
-	flag.Int(CfgFaucetMaxTransactionBookedAwaitTimeSeconds, 5, "the max amount of time for a funding transaction to become booked in the value layer.")
+	flag.Int(CfgFaucetMaxTransactionBookedAwaitTimeSeconds, 5, "the max amount of time for a funding transaction to become booked in the value layer")
+	flag.Int(CfgFaucetPoWDifficulty, 25, "defines the PoW difficulty for faucet payloads")
+	flag.Int(CfgFaucetBlacklistCapacity, 10000, "holds the maximum amount the address blacklist holds")
 }
 
 var (
@@ -47,6 +56,7 @@ var (
 	_faucet                *faucet.Faucet
 	faucetOnce             sync.Once
 	log                    *logger.Logger
+	powVerifier            = pow.New(crypto.BLAKE2b_512)
 	fundingWorkerPool      *workerpool.WorkerPool
 	fundingWorkerCount     = runtime.GOMAXPROCS(0)
 	fundingWorkerQueueSize = 500
@@ -79,7 +89,8 @@ func Faucet() *faucet.Faucet {
 		if maxTxBookedAwaitTime <= 0 {
 			log.Fatalf("the max transaction booked await time must be more than 0")
 		}
-		_faucet = faucet.New(seedBytes, tokensPerRequest, time.Duration(maxTxBookedAwaitTime)*time.Second)
+		blacklistCapacity := config.Node().GetInt(CfgFaucetBlacklistCapacity)
+		_faucet = faucet.New(seedBytes, tokensPerRequest, blacklistCapacity, time.Duration(maxTxBookedAwaitTime)*time.Second)
 	})
 	return _faucet
 }
@@ -93,7 +104,7 @@ func configure(*node.Plugin) {
 		addr := msg.Payload().(*faucetpayload.Payload).Address()
 		msg, txID, err := Faucet().SendFunds(msg)
 		if err != nil {
-			log.Errorf("couldn't fulfill funding request to %s: %s", addr, err)
+			log.Warnf("couldn't fulfill funding request to %s: %s", addr, err)
 			return
 		}
 		log.Infof("sent funds to address %s via tx %s and msg %s", addr, txID, msg.Id().String())
@@ -122,7 +133,26 @@ func configureEvents() {
 			return
 		}
 
-		addr := msg.Payload().(*faucetpayload.Payload).Address()
+		fundingRequest := msg.Payload().(*faucetpayload.Payload)
+		addr := fundingRequest.Address()
+		if Faucet().IsAddressBlacklisted(addr) {
+			log.Debugf("can't fund address %s since it is blacklisted", addr)
+			return
+		}
+
+		// verify PoW
+		leadingZeroes, err := powVerifier.LeadingZeros(fundingRequest.Bytes())
+		if err != nil {
+			log.Warnf("couldn't verify PoW of funding request for address %s", addr)
+			return
+		}
+		targetPoWDifficulty := config.Node().GetInt(CfgFaucetPoWDifficulty)
+		if leadingZeroes < targetPoWDifficulty {
+			log.Debugf("funding request for address %s doesn't fulfill PoW requirement %d vs. %d", addr, targetPoWDifficulty, leadingZeroes)
+			return
+		}
+
+		// finally add it to the faucet to be processed
 		_, added := fundingWorkerPool.TrySubmit(msg)
 		if !added {
 			log.Info("dropped funding request for address %s as queue is full", addr)
diff --git a/dapps/faucet/packages/faucet.go b/dapps/faucet/packages/faucet.go
index dd40e51fffa73802939bc5881ffed6fd8089dc6b..fbbcc66c034d5d6c556cd978dd7c1fb63c2b8556 100644
--- a/dapps/faucet/packages/faucet.go
+++ b/dapps/faucet/packages/faucet.go
@@ -14,23 +14,27 @@ import (
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet"
+	"github.com/iotaledger/goshimmer/packages/binary/datastructure/orderedmap"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	"github.com/iotaledger/goshimmer/plugins/issuer"
-	"github.com/iotaledger/hive.go/events"
 )
 
 var (
 	// ErrFundingTxNotBookedInTime is returned when a funding transaction didn't get booked
 	// by this node in the maximum defined await time for it to get booked.
 	ErrFundingTxNotBookedInTime = errors.New("funding transaction didn't get booked in time")
+	// ErrAddressIsBlacklisted is returned if a funding can't be processed since the address is blacklisted.
+	ErrAddressIsBlacklisted = errors.New("can't fund address as it is blacklisted")
 )
 
 // New creates a new faucet using the given seed and tokensPerRequest config.
-func New(seed []byte, tokensPerRequest int64, maxTxBookedAwaitTime time.Duration) *Faucet {
+func New(seed []byte, tokensPerRequest int64, blacklistCapacity int, maxTxBookedAwaitTime time.Duration) *Faucet {
 	return &Faucet{
 		tokensPerRequest:     tokensPerRequest,
 		wallet:               wallet.New(seed),
 		maxTxBookedAwaitTime: maxTxBookedAwaitTime,
+		blacklist:            orderedmap.New(),
+		blacklistCapacity:    blacklistCapacity,
 	}
 }
 
@@ -44,6 +48,28 @@ type Faucet struct {
 	// the time to await for the transaction fulfilling a funding request
 	// to become booked in the value layer
 	maxTxBookedAwaitTime time.Duration
+	blacklistCapacity    int
+	blacklist            *orderedmap.OrderedMap
+}
+
+// IsAddressBlacklisted checks whether the given address is currently blacklisted.
+func (f *Faucet) IsAddressBlacklisted(addr address.Address) bool {
+	_, blacklisted := f.blacklist.Get(addr)
+	return blacklisted
+}
+
+// adds the given address to the blacklist and removes the oldest blacklist entry
+// if it would go over capacity.
+func (f *Faucet) addAddressToBlacklist(addr address.Address) {
+	f.blacklist.Set(addr, true)
+	if f.blacklist.Size() > f.blacklistCapacity {
+		var headKey interface{}
+		f.blacklist.ForEach(func(key, value interface{}) bool {
+			headKey = key
+			return false
+		})
+		f.blacklist.Delete(headKey)
+	}
 }
 
 // SendFunds sends IOTA tokens to the address from faucet request.
@@ -54,6 +80,10 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
 
 	addr := msg.Payload().(*faucetpayload.Payload).Address()
 
+	if f.IsAddressBlacklisted(addr) {
+		return nil, "", ErrAddressIsBlacklisted
+	}
+
 	// get the output ids for the inputs and remainder balance
 	outputIds, addrsIndices, remainder := f.collectUTXOsForFunding()
 
@@ -80,7 +110,10 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
 	}
 
 	// prepare value payload with value factory
-	payload := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
+	payload, err := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
+	if err != nil {
+		return nil, "", fmt.Errorf("failed to issue transaction: %w", err)
+	}
 
 	// attach to message layer
 	msg, err = issuer.IssuePayload(payload)
@@ -91,40 +124,13 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
 	// block for a certain amount of time until we know that the transaction
 	// actually got booked by this node itself
 	// TODO: replace with an actual more reactive way
-	bookedInTime := f.awaitTransactionBooked(tx.ID(), f.maxTxBookedAwaitTime)
-	if !bookedInTime {
-		return nil, "", fmt.Errorf("%w: tx %s", ErrFundingTxNotBookedInTime, tx.ID().String())
+	if err := valuetransfers.AwaitTransactionToBeBooked(tx.ID(), f.maxTxBookedAwaitTime); err != nil {
+		return nil, "", fmt.Errorf("%w: tx %s", err, tx.ID().String())
 	}
 
-	return msg, tx.ID().String(), nil
-}
+	f.addAddressToBlacklist(addr)
 
-// awaitTransactionBooked awaits maxAwait for the given transaction to get booked.
-func (f *Faucet) awaitTransactionBooked(txID transaction.ID, maxAwait time.Duration) bool {
-	booked := make(chan struct{}, 1)
-	// exit is used to let the caller exit if for whatever
-	// reason the same transaction gets booked multiple times
-	exit := make(chan struct{})
-	defer close(exit)
-	closure := events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata, decisionPending bool) {
-		defer cachedTransaction.Release()
-		defer cachedTransactionMetadata.Release()
-		if cachedTransaction.Unwrap().ID() != txID {
-			return
-		}
-		select {
-		case booked <- struct{}{}:
-		case <-exit:
-		}
-	})
-	valuetransfers.Tangle().Events.TransactionBooked.Attach(closure)
-	defer valuetransfers.Tangle().Events.TransactionBooked.Detach(closure)
-	select {
-	case <-time.After(maxAwait):
-		return false
-	case <-booked:
-		return true
-	}
+	return msg, tx.ID().String(), nil
 }
 
 // collectUTXOsForFunding iterates over the faucet's UTXOs until the token threshold is reached.
diff --git a/dapps/faucet/packages/faucet_test.go b/dapps/faucet/packages/faucet_test.go
index 9f56fc2e130fef818d3a9dba590641ffb127048c..f321c74035f2e85a006312f0007f432cc8745397 100644
--- a/dapps/faucet/packages/faucet_test.go
+++ b/dapps/faucet/packages/faucet_test.go
@@ -5,6 +5,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/iotaledger/hive.go/identity"
@@ -19,13 +20,18 @@ func TestIsFaucetReq(t *testing.T) {
 	keyPair := ed25519.GenerateKeyPair()
 	local := identity.NewLocalIdentity(keyPair.PublicKey, keyPair.PrivateKey)
 
+	faucetPayload, err := faucet.New(address.Random(), 4)
+	if err != nil {
+		require.NoError(t, err)
+		return
+	}
 	faucetMsg := message.New(
 		message.EmptyId,
 		message.EmptyId,
 		time.Now(),
 		local.PublicKey(),
 		0,
-		faucet.New(address.Random()),
+		faucetPayload,
 		0,
 		ed25519.EmptySignature,
 	)
diff --git a/dapps/faucet/packages/payload/payload.go b/dapps/faucet/packages/payload/payload.go
index 264aa31134635cf2ba81670eadae8289efe16618..ba5474352b902c7c1cc3205aacdbe3728a216729 100644
--- a/dapps/faucet/packages/payload/payload.go
+++ b/dapps/faucet/packages/payload/payload.go
@@ -1,7 +1,13 @@
 package faucetpayload
 
 import (
+	"context"
+	"crypto"
+
+	_ "golang.org/x/crypto/blake2b"
+
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/pow"
 	"github.com/iotaledger/hive.go/marshalutil"
 	"github.com/iotaledger/hive.go/stringify"
 
@@ -18,17 +24,28 @@ const (
 type Payload struct {
 	payloadType payload.Type
 	address     address.Address
+	nonce       uint64
 }
 
 // Type represents the identifier for the faucet Payload type.
 var Type = payload.Type(2)
+var powWorker = pow.New(crypto.BLAKE2b_512, 1)
 
 // New is the constructor of a Payload and creates a new Payload object from the given details.
-func New(addr address.Address) *Payload {
-	return &Payload{
+func New(addr address.Address, powTarget int) (*Payload, error) {
+	p := &Payload{
 		payloadType: Type,
 		address:     addr,
 	}
+
+	payloadBytes := p.Bytes()
+	powRelevantBytes := payloadBytes[:len(payloadBytes)-pow.NonceBytes]
+	nonce, err := powWorker.Mine(context.Background(), powRelevantBytes, powTarget)
+	if err != nil {
+		return nil, err
+	}
+	p.nonce = nonce
+	return p, nil
 }
 
 func init() {
@@ -56,15 +73,22 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
 	if err != nil {
 		return
 	}
-	payloadBytes, err := marshalUtil.ReadUint32()
+	if _, err = marshalUtil.ReadUint32(); err != nil {
+		return
+	}
+	addr, err := marshalUtil.ReadBytes(address.Length)
 	if err != nil {
 		return
 	}
-	addr, err := marshalUtil.ReadBytes(int(payloadBytes))
+	result.address, _, err = address.FromBytes(addr)
+	if err != nil {
+		return
+	}
+
+	result.nonce, err = marshalUtil.ReadUint64()
 	if err != nil {
 		return
 	}
-	result.address, _, _ = address.FromBytes(addr)
 
 	// return the number of bytes we processed
 	consumedBytes = marshalUtil.ReadOffset()
@@ -89,8 +113,9 @@ func (faucetPayload *Payload) Bytes() []byte {
 
 	// marshal the payload specific information
 	marshalUtil.WriteUint32(faucetPayload.Type())
-	marshalUtil.WriteUint32(uint32(len(faucetPayload.address)))
+	marshalUtil.WriteUint32(uint32(address.Length + pow.NonceBytes))
 	marshalUtil.WriteBytes(faucetPayload.address.Bytes())
+	marshalUtil.WriteUint64(faucetPayload.nonce)
 
 	// return result
 	return marshalUtil.Bytes()
diff --git a/dapps/faucet/packages/payload/payload_test.go b/dapps/faucet/packages/payload/payload_test.go
index 40684e9879ba132e27508ace9dd9ce9c031d0376..fe9d3b8693acd62fd4885698560a5c6486ce8476 100644
--- a/dapps/faucet/packages/payload/payload_test.go
+++ b/dapps/faucet/packages/payload/payload_test.go
@@ -5,10 +5,9 @@ import (
 	"testing"
 	"time"
 
-	"github.com/stretchr/testify/assert"
-
 	"github.com/iotaledger/hive.go/crypto/ed25519"
 	"github.com/iotaledger/hive.go/identity"
+	"github.com/stretchr/testify/assert"
 
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
@@ -19,10 +18,10 @@ func ExamplePayload() {
 	local := identity.NewLocalIdentity(keyPair.PublicKey, keyPair.PrivateKey)
 
 	// 1. create faucet payload
-	faucetPayload := New(
-		// request address
-		address.Random(),
-	)
+	faucetPayload, err := New(address.Random(), 4)
+	if err != nil {
+		panic(err)
+	}
 
 	// 2. build actual message
 	tx := message.New(
@@ -39,7 +38,10 @@ func ExamplePayload() {
 }
 
 func TestPayload(t *testing.T) {
-	originalPayload := New(address.Random())
+	originalPayload, err := New(address.Random(), 4)
+	if err != nil {
+		panic(err)
+	}
 
 	clonedPayload1, err, _ := FromBytes(originalPayload.Bytes())
 	if err != nil {
diff --git a/dapps/valuetransfers/dapp.go b/dapps/valuetransfers/dapp.go
index e16014d0ac8a4aebb036418385108c67ea88275d..60adff59bb18cc52bec8690144c53a0dd2654bb8 100644
--- a/dapps/valuetransfers/dapp.go
+++ b/dapps/valuetransfers/dapp.go
@@ -1,6 +1,7 @@
 package valuetransfers
 
 import (
+	"errors"
 	"os"
 	"sync"
 	"time"
@@ -10,6 +11,7 @@ import (
 	valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tipmanager"
+	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
 	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
 	messageTangle "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
 	"github.com/iotaledger/goshimmer/packages/shutdown"
@@ -44,6 +46,10 @@ func init() {
 }
 
 var (
+	// ErrTransactionWasNotBookedInTime is returned if a transaction did not get booked
+	// within the defined await time.
+	ErrTransactionWasNotBookedInTime = errors.New("transaction could not be booked in time")
+
 	// app is the "plugin" instance of the value-transfers application.
 	app     *node.Plugin
 	appOnce sync.Once
@@ -217,7 +223,35 @@ func TipManager() *tipmanager.TipManager {
 // ValueObjectFactory returns the ValueObjectFactory singleton.
 func ValueObjectFactory() *tangle.ValueObjectFactory {
 	valueObjectFactoryOnce.Do(func() {
-		valueObjectFactory = tangle.NewValueObjectFactory(TipManager())
+		valueObjectFactory = tangle.NewValueObjectFactory(Tangle(), TipManager())
 	})
 	return valueObjectFactory
 }
+
+// AwaitTransactionToBeBooked awaits maxAwait for the given transaction to get booked.
+func AwaitTransactionToBeBooked(txID transaction.ID, maxAwait time.Duration) error {
+	booked := make(chan struct{}, 1)
+	// exit is used to let the caller exit if for whatever
+	// reason the same transaction gets booked multiple times
+	exit := make(chan struct{})
+	defer close(exit)
+	closure := events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *tangle.CachedTransactionMetadata, decisionPending bool) {
+		defer cachedTransaction.Release()
+		defer cachedTransactionMetadata.Release()
+		if cachedTransaction.Unwrap().ID() != txID {
+			return
+		}
+		select {
+		case booked <- struct{}{}:
+		case <-exit:
+		}
+	})
+	Tangle().Events.TransactionBooked.Attach(closure)
+	defer Tangle().Events.TransactionBooked.Detach(closure)
+	select {
+	case <-time.After(maxAwait):
+		return ErrTransactionWasNotBookedInTime
+	case <-booked:
+		return nil
+	}
+}
diff --git a/dapps/valuetransfers/packages/tangle/errors.go b/dapps/valuetransfers/packages/tangle/errors.go
index 3ada3b8ae0f57de974ed9bcc4a11012a6db3b5c4..935661ea0a2d659f9d37c6edad5ed3260af28983 100644
--- a/dapps/valuetransfers/packages/tangle/errors.go
+++ b/dapps/valuetransfers/packages/tangle/errors.go
@@ -11,4 +11,7 @@ var (
 
 	// ErrPayloadInvalid represents an error type that is triggered when an invalid payload is detected.
 	ErrPayloadInvalid = errors.New("payload invalid")
+
+	// ErrDoubleSpendForbidden represents an error that is triggered when a user tries to issue a double spend.
+	ErrDoubleSpendForbidden = errors.New("it is not allowed to issue a double spend")
 )
diff --git a/dapps/valuetransfers/packages/tangle/factory.go b/dapps/valuetransfers/packages/tangle/factory.go
index 6720e5d2f9c4361a3a4947706db17e848f16ffd1..106ee55a5b4ff1108e84e18d896dac6f2321e96b 100644
--- a/dapps/valuetransfers/packages/tangle/factory.go
+++ b/dapps/valuetransfers/packages/tangle/factory.go
@@ -9,13 +9,15 @@ import (
 
 // ValueObjectFactory acts as a factory to create new value objects.
 type ValueObjectFactory struct {
+	tangle     *Tangle
 	tipManager *tipmanager.TipManager
 	Events     *ValueObjectFactoryEvents
 }
 
 // NewValueObjectFactory creates a new ValueObjectFactory.
-func NewValueObjectFactory(tipManager *tipmanager.TipManager) *ValueObjectFactory {
+func NewValueObjectFactory(tangle *Tangle, tipManager *tipmanager.TipManager) *ValueObjectFactory {
 	return &ValueObjectFactory{
+		tangle:     tangle,
 		tipManager: tipManager,
 		Events: &ValueObjectFactoryEvents{
 			ValueObjectConstructed: events.NewEvent(valueObjectConstructedEvent),
@@ -25,13 +27,27 @@ func NewValueObjectFactory(tipManager *tipmanager.TipManager) *ValueObjectFactor
 
 // IssueTransaction creates a new value object including tip selection and returns it.
 // It also triggers the ValueObjectConstructed event once it's done.
-func (v *ValueObjectFactory) IssueTransaction(tx *transaction.Transaction) *payload.Payload {
+func (v *ValueObjectFactory) IssueTransaction(tx *transaction.Transaction) (valueObject *payload.Payload, err error) {
 	parent1, parent2 := v.tipManager.Tips()
 
-	valueObject := payload.New(parent1, parent2, tx)
+	// check if the tx that is supposed to be issued is a double spend
+	tx.Inputs().ForEach(func(outputId transaction.OutputID) bool {
+		v.tangle.TransactionOutput(outputId).Consume(func(output *Output) {
+			if output.ConsumerCount() >= 1 {
+				err = ErrDoubleSpendForbidden
+			}
+		})
+
+		return err == nil
+	})
+	if err != nil {
+		return
+	}
+
+	valueObject = payload.New(parent1, parent2, tx)
 	v.Events.ValueObjectConstructed.Trigger(valueObject)
 
-	return valueObject
+	return
 }
 
 // ValueObjectFactoryEvents represent events happening on a ValueObjectFactory.
diff --git a/dapps/valuetransfers/packages/transaction/inputs.go b/dapps/valuetransfers/packages/transaction/inputs.go
index bfc8ea13980fa6ead451063438495904ab2e51b9..f5071c62cebbed30e2410292a82166732d098a73 100644
--- a/dapps/valuetransfers/packages/transaction/inputs.go
+++ b/dapps/valuetransfers/packages/transaction/inputs.go
@@ -104,7 +104,7 @@ func (inputs *Inputs) Bytes() (bytes []byte) {
 
 // ForEach iterates through the referenced Outputs and calls the consumer function for every Output. The iteration can
 // be aborted by returning false in the consumer.
-func (inputs *Inputs) ForEach(consumer func(outputId OutputID) bool) bool {
+func (inputs *Inputs) ForEach(consumer func(outputID OutputID) bool) bool {
 	return inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
 		return value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
 			return consumer(value.(OutputID))
diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go
index 4b3707cc7acb3e7034f17e1c103256ffcc8ce7f0..49a78a06a472c99dd2520676324c54fc8061df68 100644
--- a/plugins/autopeering/autopeering.go
+++ b/plugins/autopeering/autopeering.go
@@ -25,7 +25,7 @@ import (
 // autopeering constants
 const (
 	ProtocolVersion = 0 // update on protocol changes
-	NetworkVersion  = 2 // update on network changes
+	NetworkVersion  = 3 // update on network changes
 )
 
 var (
diff --git a/plugins/banner/plugin.go b/plugins/banner/plugin.go
index 5603df69d13a2c1fadc32ce202ac620178fb142a..6229660976de1468d0ca3a0086b5654f208f8801 100644
--- a/plugins/banner/plugin.go
+++ b/plugins/banner/plugin.go
@@ -18,7 +18,7 @@ var (
 
 const (
 	// AppVersion version number
-	AppVersion = "v0.2.0"
+	AppVersion = "v0.2.1"
 
 	// AppName app code name
 	AppName = "GoShimmer"
diff --git a/plugins/dashboard/faucet_routes.go b/plugins/dashboard/faucet_routes.go
index efaf0c4945f61adad9d1e647f7fdb0f72e83221e..41e115493eb1bb001d5bca6bec65e35635bbdf4e 100644
--- a/plugins/dashboard/faucet_routes.go
+++ b/plugins/dashboard/faucet_routes.go
@@ -2,9 +2,12 @@ package dashboard
 
 import (
 	"net/http"
+	"sync"
 
+	"github.com/iotaledger/goshimmer/dapps/faucet"
 	faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/plugins/config"
 	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 
 	"github.com/labstack/echo"
@@ -32,8 +35,16 @@ func setupFaucetRoutes(routeGroup *echo.Group) {
 	})
 }
 
+var fundingReqMu = sync.Mutex{}
+
 func sendFaucetReq(addr address.Address) (res *ReqMsg, err error) {
-	msg := messagelayer.MessageFactory().IssuePayload(faucetpayload.New(addr))
+	fundingReqMu.Lock()
+	defer fundingReqMu.Unlock()
+	faucetPayload, err := faucetpayload.New(addr, config.Node().GetInt(faucet.CfgFaucetPoWDifficulty))
+	if err != nil {
+		return nil, err
+	}
+	msg := messagelayer.MessageFactory().IssuePayload(faucetPayload)
 	if msg == nil {
 		return nil, errors.Wrapf(ErrInternalError, "Fail to send faucet request")
 	}
diff --git a/plugins/database/versioning.go b/plugins/database/versioning.go
index 0b62a5bf481e5f8de14b2b54b6209a63f9e896ae..68b367b275958074535a8d2eef676a168f2c7ea2 100644
--- a/plugins/database/versioning.go
+++ b/plugins/database/versioning.go
@@ -10,7 +10,7 @@ import (
 const (
 	// DBVersion defines the version of the database schema this version of GoShimmer supports.
 	// Every time there's a breaking change regarding the stored data, this version flag should be adjusted.
-	DBVersion = 3
+	DBVersion = 4
 )
 
 var (
diff --git a/plugins/gossip/parameters.go b/plugins/gossip/parameters.go
index ade8e8a22e94808dd3c130be13c20fb509e107e4..44ac5efa94642e769de21b9d17cd9c1ce52adf94 100644
--- a/plugins/gossip/parameters.go
+++ b/plugins/gossip/parameters.go
@@ -1,14 +1,20 @@
 package gossip
 
 import (
+	"time"
+
 	flag "github.com/spf13/pflag"
 )
 
 const (
 	// CfgGossipPort defines the config flag of the gossip port.
 	CfgGossipPort = "gossip.port"
+
+	// CfgGossipTipsBroadcastInterval the interval in which the oldest known tip is re-broadcasted.
+	CfgGossipTipsBroadcastInterval = "gossip.tipsBroadcaster.interval"
 )
 
 func init() {
 	flag.Int(CfgGossipPort, 14666, "tcp port for gossip connection")
+	flag.Duration(CfgGossipTipsBroadcastInterval, 10*time.Second, "the interval in which the oldest known tip is re-broadcasted")
 }
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index d32ad25341a99bdb1c72d93a9f4046771ece4259..83f36f08c0aed6a6122d4176ba3faa608728597e 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -42,6 +42,7 @@ func configure(*node.Plugin) {
 	configureLogging()
 	configureMessageLayer()
 	configureAutopeering()
+	configureTipBroadcaster()
 }
 
 func run(*node.Plugin) {
diff --git a/plugins/gossip/tips_broadcaster.go b/plugins/gossip/tips_broadcaster.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ea6341f5e542e8d4f1f9600fbcf37292b675ac0
--- /dev/null
+++ b/plugins/gossip/tips_broadcaster.go
@@ -0,0 +1,126 @@
+package gossip
+
+import (
+	"container/list"
+	"sync"
+	"time"
+
+	"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
+	"github.com/iotaledger/goshimmer/packages/shutdown"
+	"github.com/iotaledger/goshimmer/plugins/config"
+	"github.com/iotaledger/goshimmer/plugins/messagelayer"
+	"github.com/iotaledger/hive.go/daemon"
+	"github.com/iotaledger/hive.go/events"
+)
+
+const (
+	// the amount of oldest tips in the tip pool to broadcast up on each interval
+	maxOldestTipsToBroadcastPerInterval = 2
+)
+
+var tips = tiplist{dict: make(map[message.Id]*list.Element)}
+
+type tiplist struct {
+	mu sync.Mutex
+
+	dict     map[message.Id]*list.Element
+	list     list.List
+	iterator *list.Element
+}
+
+func (s *tiplist) AddTip(id message.Id) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	if _, contains := s.dict[id]; contains {
+		return
+	}
+
+	elem := s.list.PushBack(id)
+	s.dict[id] = elem
+	if s.iterator == nil {
+		s.iterator = elem
+	}
+}
+
+func (s *tiplist) RemoveTip(id message.Id) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	elem, ok := s.dict[id]
+	if ok {
+		s.list.Remove(elem)
+		if s.iterator == elem {
+			s.next(elem)
+		}
+	}
+}
+
+func (s *tiplist) Next() (id message.Id) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	if s.iterator != nil {
+		id = s.iterator.Value.(message.Id)
+		s.next(s.iterator)
+	}
+	return
+}
+
+func (s *tiplist) next(elem *list.Element) {
+	s.iterator = elem.Next()
+	if s.iterator == nil {
+		s.iterator = s.list.Front()
+	}
+}
+
+func configureTipBroadcaster() {
+	tipSelector := messagelayer.TipSelector()
+	addedTipsClosure := events.NewClosure(tips.AddTip)
+	removedTipClosure := events.NewClosure(tips.RemoveTip)
+	tipSelector.Events.TipAdded.Attach(addedTipsClosure)
+	tipSelector.Events.TipRemoved.Attach(removedTipClosure)
+
+	if err := daemon.BackgroundWorker("Tips-Broadcaster", func(shutdownSignal <-chan struct{}) {
+		log.Info("broadcaster started")
+		defer log.Info("broadcaster stopped")
+		defer tipSelector.Events.TipAdded.Detach(addedTipsClosure)
+		defer tipSelector.Events.TipRemoved.Detach(removedTipClosure)
+		ticker := time.NewTicker(config.Node().GetDuration(CfgGossipTipsBroadcastInterval))
+		defer ticker.Stop()
+		for {
+			select {
+			case <-ticker.C:
+				broadcastOldestTips()
+			case <-shutdownSignal:
+				return
+			}
+		}
+	}, shutdown.PriorityGossip); err != nil {
+		log.Panicf("Couldn't create demon: %s", err)
+	}
+}
+
+// broadcasts up to maxOldestTipsToBroadcastPerInterval tips from the tip pool
+// to all connected neighbors.
+func broadcastOldestTips() {
+	for toBroadcast := maxOldestTipsToBroadcastPerInterval; toBroadcast > 0; toBroadcast-- {
+		msgID := tips.Next()
+		if msgID == message.EmptyId {
+			break
+		}
+		log.Debugf("broadcasting tip %s", msgID)
+		broadcastMessage(msgID)
+	}
+}
+
+// broadcasts the given message to all neighbors if it exists.
+func broadcastMessage(msgID message.Id) {
+	cachedMessage := messagelayer.Tangle().Message(msgID)
+	defer cachedMessage.Release()
+	if !cachedMessage.Exists() {
+		return
+	}
+	msg := cachedMessage.Unwrap()
+	Manager().SendMessage(msg.Bytes())
+}
diff --git a/plugins/webapi/faucet/plugin.go b/plugins/webapi/faucet/plugin.go
index 74d2c796f59c61d6e2dc1af8b31db619b3672ba3..791bc2e4767a6039038e91660a0343a905c456fe 100644
--- a/plugins/webapi/faucet/plugin.go
+++ b/plugins/webapi/faucet/plugin.go
@@ -4,8 +4,10 @@ import (
 	"net/http"
 	goSync "sync"
 
+	"github.com/iotaledger/goshimmer/dapps/faucet"
 	faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
+	"github.com/iotaledger/goshimmer/plugins/config"
 	"github.com/iotaledger/goshimmer/plugins/messagelayer"
 	"github.com/iotaledger/goshimmer/plugins/webapi"
 	"github.com/iotaledger/hive.go/logger"
@@ -20,9 +22,10 @@ const (
 
 var (
 	// plugin is the plugin instance of the web API info endpoint plugin.
-	plugin *node.Plugin
-	once   goSync.Once
-	log    *logger.Logger
+	plugin    *node.Plugin
+	once      goSync.Once
+	log       *logger.Logger
+	fundingMu goSync.Mutex
 )
 
 // Plugin gets the plugin instance.
@@ -41,6 +44,8 @@ func configure(plugin *node.Plugin) {
 // requestFunds creates a faucet request (0-value) message with the given destination address and
 // broadcasts it to the node's neighbors. It returns the message ID if successful.
 func requestFunds(c echo.Context) error {
+	fundingMu.Lock()
+	defer fundingMu.Unlock()
 	var request Request
 	var addr address.Address
 	if err := c.Bind(&request); err != nil {
@@ -55,8 +60,11 @@ func requestFunds(c echo.Context) error {
 		return c.JSON(http.StatusBadRequest, Response{Error: "Invalid address"})
 	}
 
-	// build faucet message with transaction factory
-	msg := messagelayer.MessageFactory().IssuePayload(faucetpayload.New(addr))
+	faucetPayload, err := faucetpayload.New(addr, config.Node().GetInt(faucet.CfgFaucetPoWDifficulty))
+	if err != nil {
+		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
+	}
+	msg := messagelayer.MessageFactory().IssuePayload(faucetPayload)
 	if msg == nil {
 		return c.JSON(http.StatusInternalServerError, Response{Error: "Fail to send faucetrequest"})
 	}
diff --git a/plugins/webapi/value/sendtransaction/handler.go b/plugins/webapi/value/sendtransaction/handler.go
index dda59ab8c3d80fbe5c3b69a08dd82254acc81b6c..56bc66b2a8c549ae5800a501485e640d7153f640 100644
--- a/plugins/webapi/value/sendtransaction/handler.go
+++ b/plugins/webapi/value/sendtransaction/handler.go
@@ -2,6 +2,8 @@ package sendtransaction
 
 import (
 	"net/http"
+	"sync"
+	"time"
 
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
@@ -9,8 +11,16 @@ import (
 	"github.com/labstack/echo"
 )
 
+var (
+	sendTxMu           sync.Mutex
+	maxBookedAwaitTime = 5 * time.Second
+)
+
 // Handler sends a transaction.
 func Handler(c echo.Context) error {
+	sendTxMu.Lock()
+	defer sendTxMu.Unlock()
+
 	var request Request
 	if err := c.Bind(&request); err != nil {
 		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
@@ -28,12 +38,18 @@ func Handler(c echo.Context) error {
 	}
 
 	// Prepare value payload and send the message to tangle
-	payload := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
+	payload, err := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
+	if err != nil {
+		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
+	}
 	_, err = issuer.IssuePayload(payload)
 	if err != nil {
 		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
 	}
 
+	if err := valuetransfers.AwaitTransactionToBeBooked(tx.ID(), maxBookedAwaitTime); err != nil {
+		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
+	}
 	return c.JSON(http.StatusOK, Response{TransactionID: tx.ID().String()})
 }
 
diff --git a/plugins/webapi/value/testsendtxn/handler.go b/plugins/webapi/value/testsendtxn/handler.go
index 222e919ede5d6b2390bd0540475b5f0cde0be4f4..15b7a7673bb58b16fe452f9d22412e827006f531 100644
--- a/plugins/webapi/value/testsendtxn/handler.go
+++ b/plugins/webapi/value/testsendtxn/handler.go
@@ -66,8 +66,11 @@ func Handler(c echo.Context) error {
 	tx := transaction.New(inputs, outputs)
 
 	// Prepare value payload and send the message to tangle
-	payload := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
-	_, err := issuer.IssuePayload(payload)
+	payload, err := valuetransfers.ValueObjectFactory().IssueTransaction(tx)
+	if err != nil {
+		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
+	}
+	_, err = issuer.IssuePayload(payload)
 	if err != nil {
 		return c.JSON(http.StatusBadRequest, Response{Error: err.Error()})
 	}
diff --git a/tools/integration-tests/tester/framework/docker.go b/tools/integration-tests/tester/framework/docker.go
index cbf017797caf4426122ff700ac075ecd6ce6be44..0b5d688f84d72d10b7cd092840df917c0923f83b 100644
--- a/tools/integration-tests/tester/framework/docker.go
+++ b/tools/integration-tests/tester/framework/docker.go
@@ -85,6 +85,7 @@ func (d *DockerContainer) CreateGoShimmerPeer(config GoShimmerConfig) error {
 			fmt.Sprintf("--valueLayer.fcob.averageNetworkDelay=%d", ParaFCoBAverageNetworkDelay),
 			fmt.Sprintf("--node.disablePlugins=%s", config.DisabledPlugins),
 			fmt.Sprintf("--pow.difficulty=%d", ParaPoWDifficulty),
+			fmt.Sprintf("--faucet.powDifficulty=%d", ParaPoWFaucetDifficulty),
 			fmt.Sprintf("--gracefulshutdown.waitToKillTime=%d", ParaWaitToKill),
 			fmt.Sprintf("--node.enablePlugins=%s", func() string {
 				var plugins []string
diff --git a/tools/integration-tests/tester/framework/parameters.go b/tools/integration-tests/tester/framework/parameters.go
index 9fa13fe839cefc05874ce95917f054d1e8d1f40d..9694ed97ec0c1f404f32583f8fa3305d1807036d 100644
--- a/tools/integration-tests/tester/framework/parameters.go
+++ b/tools/integration-tests/tester/framework/parameters.go
@@ -37,6 +37,8 @@ var (
 	ParaPoWDifficulty = 2
 	// ParaWaitToKill defines the time to wait before killing the node.
 	ParaWaitToKill = 60
+	// ParaPoWFaucetDifficulty defines the PoW difficulty for faucet payloads.
+	ParaPoWFaucetDifficulty = 2
 )
 
 var (
diff --git a/tools/integration-tests/tester/tests/testutil.go b/tools/integration-tests/tester/tests/testutil.go
index 42aa18741dbfbcd9967bab878feeb77b2e1a12a8..c337b7daac7eef6061cf9f208a00fd3c923a84a6 100644
--- a/tools/integration-tests/tester/tests/testutil.go
+++ b/tools/integration-tests/tester/tests/testutil.go
@@ -9,7 +9,6 @@ import (
 	"testing"
 	"time"
 
-	faucet_payload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address/signaturescheme"
 	"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/balance"
@@ -81,33 +80,28 @@ func SendDataMessage(t *testing.T, peer *framework.Peer, data []byte, number int
 func SendFaucetRequestOnRandomPeer(t *testing.T, peers []*framework.Peer, numMessages int) (ids map[string]DataMessageSent, addrBalance map[string]map[balance.Color]int64) {
 	ids = make(map[string]DataMessageSent, numMessages)
 	addrBalance = make(map[string]map[balance.Color]int64)
-	for _, p := range peers {
-		addr := p.Seed().Address(0).String()
-		addrBalance[addr] = make(map[balance.Color]int64)
-		addrBalance[addr][balance.ColorIOTA] = 0
-	}
 
 	for i := 0; i < numMessages; i++ {
 		peer := peers[rand.Intn(len(peers))]
-		id, sent := SendFaucetRequest(t, peer)
-
+		addr := peer.Seed().Address(uint64(i))
+		id, sent := SendFaucetRequest(t, peer, addr)
 		ids[id] = sent
-		addrBalance[peer.Seed().Address(0).String()][balance.ColorIOTA] += framework.ParaFaucetTokensPerRequest
+		addrBalance[addr.String()] = map[balance.Color]int64{
+			balance.ColorIOTA: framework.ParaFaucetTokensPerRequest,
+		}
 	}
 
 	return ids, addrBalance
 }
 
 // SendFaucetRequest sends a data message on a given peer and returns the id and a DataMessageSent struct.
-func SendFaucetRequest(t *testing.T, peer *framework.Peer) (string, DataMessageSent) {
-	addr := peer.Seed().Address(0)
+func SendFaucetRequest(t *testing.T, peer *framework.Peer, addr address.Address) (string, DataMessageSent) {
 	resp, err := peer.SendFaucetRequest(addr.String())
 	require.NoErrorf(t, err, "Could not send faucet request on %s", peer.String())
 
 	sent := DataMessageSent{
-		id: resp.ID,
-		// save payload to be able to compare API response
-		data:            faucet_payload.New(addr).Bytes(),
+		id:              resp.ID,
+		data:            nil,
 		issuerPublicKey: peer.Identity.PublicKey().String(),
 	}
 	return resp.ID, sent
@@ -143,7 +137,9 @@ func CheckForMessageIds(t *testing.T, peers []*framework.Peer, ids map[string]Da
 			msgSent := ids[msg.ID]
 
 			assert.Equalf(t, msgSent.issuerPublicKey, msg.IssuerPublicKey, "messageID=%s, issuer=%s not correct issuer in %s.", msgSent.id, msgSent.issuerPublicKey, peer.String())
-			assert.Equalf(t, msgSent.data, msg.Payload, "messageID=%s, issuer=%s data not equal in %s.", msgSent.id, msgSent.issuerPublicKey, peer.String())
+			if msgSent.data != nil {
+				assert.Equalf(t, msgSent.data, msg.Payload, "messageID=%s, issuer=%s data not equal in %s.", msgSent.id, msgSent.issuerPublicKey, peer.String())
+			}
 			assert.Truef(t, msg.Metadata.Solid, "messageID=%s, issuer=%s not solid in %s.", msgSent.id, msgSent.issuerPublicKey, peer.String())
 		}
 	}