Skip to content
Snippets Groups Projects
Unverified Commit 0be22ca3 authored by Luca Moser's avatar Luca Moser Committed by GitHub
Browse files

Merges v0.2.1 changes to master (#612)


* Fix setup wiki article link in readme (#594)

* add snapshot file to releaser archives (#592)

* Double spend check (#606)

* Fix: added a check for doublespends

* Fix: added a check for double spends

* Fix: fixed iport cycle

* Feat: replaced error with error variable

* Feat: store tangle in factory

* Update dapps/faucet/packages/faucet.go

Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>

* makes send tx web API handler synced

Co-authored-by: default avatarHans Moog <hm@mkjc.net>
Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
Co-authored-by: default avatarLuca Moser <moser.luca@gmail.com>

* Adds tips broadcaster to gossip plugin (#608)

* adds tips broadcaster to gossip plugin

* adds missing for loop

* addresses review comments

* bumps network and database versions (#610)

* Revert "bumps network and database versions (#610)"

This reverts commit 3e626eac.

* Revert "Revert "bumps network and database versions (#610)""

This reverts commit 9aa3d1bb.

* Adds blacklist and PoW requirement to the faucet (#609)

* adds blacklist and PoW requirement to the faucet

* fixes wrong condition to check pow

* fix faucet payload marhsaling

* don't test payload data if not payload data set

* updates changelog for v0.2.1 (#611)

Co-authored-by: default avatarRajiv Shah <rajivshah1@icloud.com>
Co-authored-by: default avatarWolfgang Welz <welzwo@gmail.com>
Co-authored-by: default avatarHans Moog <3293976+hmoog@users.noreply.github.com>
Co-authored-by: default avatarHans Moog <hm@mkjc.net>
parent f172c96f
No related branches found
No related tags found
No related merge requests found
Showing
with 340 additions and 64 deletions
...@@ -76,6 +76,7 @@ archives: ...@@ -76,6 +76,7 @@ archives:
- README.md - README.md
- LICENSE - LICENSE
- config.json - config.json
- snapshot.bin
# Checksum # Checksum
checksum: checksum:
......
# 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 # v0.2.0 - 2020-06-30
* Adds the value transfer dApp: * Adds the value transfer dApp:
* New binary transaction layout * New binary transaction layout
......
...@@ -91,7 +91,7 @@ You can find more info about this on our [client-lib](https://github.com/iotaled ...@@ -91,7 +91,7 @@ You can find more info about this on our [client-lib](https://github.com/iotaled
## Getting started ## 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 ## Supporting the project
......
...@@ -40,7 +40,10 @@ ...@@ -40,7 +40,10 @@
"bindAddress": "0.0.0.0:10895" "bindAddress": "0.0.0.0:10895"
}, },
"gossip": { "gossip": {
"port": 14666 "port": 14666,
"tipsBroadcaster": {
"interval": "10s"
}
}, },
"logger": { "logger": {
"level": "info", "level": "info",
......
package faucet package faucet
import ( import (
"crypto"
"runtime" "runtime"
"sync" "sync"
"time" "time"
...@@ -9,6 +10,7 @@ import ( ...@@ -9,6 +10,7 @@ import (
faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload" faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
"github.com/iotaledger/goshimmer/packages/pow"
"github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/shutdown"
"github.com/iotaledger/goshimmer/plugins/config" "github.com/iotaledger/goshimmer/plugins/config"
"github.com/iotaledger/goshimmer/plugins/messagelayer" "github.com/iotaledger/goshimmer/plugins/messagelayer"
...@@ -32,12 +34,19 @@ const ( ...@@ -32,12 +34,19 @@ const (
// CfgFaucetMaxTransactionBookedAwaitTimeSeconds defines the time to await for the transaction fulfilling a funding request // CfgFaucetMaxTransactionBookedAwaitTimeSeconds defines the time to await for the transaction fulfilling a funding request
// to become booked in the value layer. // to become booked in the value layer.
CfgFaucetMaxTransactionBookedAwaitTimeSeconds = "faucet.maxTransactionBookedAwaitTimeSeconds" 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() { func init() {
flag.String(CfgFaucetSeed, "", "the base58 encoded seed of the faucet, must be defined if this dApp is enabled") 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(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 ( var (
...@@ -47,6 +56,7 @@ var ( ...@@ -47,6 +56,7 @@ var (
_faucet *faucet.Faucet _faucet *faucet.Faucet
faucetOnce sync.Once faucetOnce sync.Once
log *logger.Logger log *logger.Logger
powVerifier = pow.New(crypto.BLAKE2b_512)
fundingWorkerPool *workerpool.WorkerPool fundingWorkerPool *workerpool.WorkerPool
fundingWorkerCount = runtime.GOMAXPROCS(0) fundingWorkerCount = runtime.GOMAXPROCS(0)
fundingWorkerQueueSize = 500 fundingWorkerQueueSize = 500
...@@ -79,7 +89,8 @@ func Faucet() *faucet.Faucet { ...@@ -79,7 +89,8 @@ func Faucet() *faucet.Faucet {
if maxTxBookedAwaitTime <= 0 { if maxTxBookedAwaitTime <= 0 {
log.Fatalf("the max transaction booked await time must be more than 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 return _faucet
} }
...@@ -93,7 +104,7 @@ func configure(*node.Plugin) { ...@@ -93,7 +104,7 @@ func configure(*node.Plugin) {
addr := msg.Payload().(*faucetpayload.Payload).Address() addr := msg.Payload().(*faucetpayload.Payload).Address()
msg, txID, err := Faucet().SendFunds(msg) msg, txID, err := Faucet().SendFunds(msg)
if err != nil { 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 return
} }
log.Infof("sent funds to address %s via tx %s and msg %s", addr, txID, msg.Id().String()) log.Infof("sent funds to address %s via tx %s and msg %s", addr, txID, msg.Id().String())
...@@ -122,7 +133,26 @@ func configureEvents() { ...@@ -122,7 +133,26 @@ func configureEvents() {
return 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) _, added := fundingWorkerPool.TrySubmit(msg)
if !added { if !added {
log.Info("dropped funding request for address %s as queue is full", addr) log.Info("dropped funding request for address %s as queue is full", addr)
......
...@@ -14,23 +14,27 @@ import ( ...@@ -14,23 +14,27 @@ import (
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/wallet" "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/packages/binary/messagelayer/message"
"github.com/iotaledger/goshimmer/plugins/issuer" "github.com/iotaledger/goshimmer/plugins/issuer"
"github.com/iotaledger/hive.go/events"
) )
var ( var (
// ErrFundingTxNotBookedInTime is returned when a funding transaction didn't get booked // 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. // 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") 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. // 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{ return &Faucet{
tokensPerRequest: tokensPerRequest, tokensPerRequest: tokensPerRequest,
wallet: wallet.New(seed), wallet: wallet.New(seed),
maxTxBookedAwaitTime: maxTxBookedAwaitTime, maxTxBookedAwaitTime: maxTxBookedAwaitTime,
blacklist: orderedmap.New(),
blacklistCapacity: blacklistCapacity,
} }
} }
...@@ -44,6 +48,28 @@ type Faucet struct { ...@@ -44,6 +48,28 @@ type Faucet struct {
// the time to await for the transaction fulfilling a funding request // the time to await for the transaction fulfilling a funding request
// to become booked in the value layer // to become booked in the value layer
maxTxBookedAwaitTime time.Duration 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. // 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 ...@@ -54,6 +80,10 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
addr := msg.Payload().(*faucetpayload.Payload).Address() addr := msg.Payload().(*faucetpayload.Payload).Address()
if f.IsAddressBlacklisted(addr) {
return nil, "", ErrAddressIsBlacklisted
}
// get the output ids for the inputs and remainder balance // get the output ids for the inputs and remainder balance
outputIds, addrsIndices, remainder := f.collectUTXOsForFunding() outputIds, addrsIndices, remainder := f.collectUTXOsForFunding()
...@@ -80,7 +110,10 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin ...@@ -80,7 +110,10 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin
} }
// prepare value payload with value factory // 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 // attach to message layer
msg, err = issuer.IssuePayload(payload) msg, err = issuer.IssuePayload(payload)
...@@ -91,40 +124,13 @@ func (f *Faucet) SendFunds(msg *message.Message) (m *message.Message, txID strin ...@@ -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 // block for a certain amount of time until we know that the transaction
// actually got booked by this node itself // actually got booked by this node itself
// TODO: replace with an actual more reactive way // TODO: replace with an actual more reactive way
bookedInTime := f.awaitTransactionBooked(tx.ID(), f.maxTxBookedAwaitTime) if err := valuetransfers.AwaitTransactionToBeBooked(tx.ID(), f.maxTxBookedAwaitTime); err != nil {
if !bookedInTime { return nil, "", fmt.Errorf("%w: tx %s", err, tx.ID().String())
return nil, "", fmt.Errorf("%w: tx %s", ErrFundingTxNotBookedInTime, tx.ID().String())
} }
return msg, tx.ID().String(), nil f.addAddressToBlacklist(addr)
}
// awaitTransactionBooked awaits maxAwait for the given transaction to get booked. return msg, tx.ID().String(), nil
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
}
} }
// collectUTXOsForFunding iterates over the faucet's UTXOs until the token threshold is reached. // collectUTXOsForFunding iterates over the faucet's UTXOs until the token threshold is reached.
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/identity"
...@@ -19,13 +20,18 @@ func TestIsFaucetReq(t *testing.T) { ...@@ -19,13 +20,18 @@ func TestIsFaucetReq(t *testing.T) {
keyPair := ed25519.GenerateKeyPair() keyPair := ed25519.GenerateKeyPair()
local := identity.NewLocalIdentity(keyPair.PublicKey, keyPair.PrivateKey) 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( faucetMsg := message.New(
message.EmptyId, message.EmptyId,
message.EmptyId, message.EmptyId,
time.Now(), time.Now(),
local.PublicKey(), local.PublicKey(),
0, 0,
faucet.New(address.Random()), faucetPayload,
0, 0,
ed25519.EmptySignature, ed25519.EmptySignature,
) )
......
package faucetpayload package faucetpayload
import ( import (
"context"
"crypto"
_ "golang.org/x/crypto/blake2b"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "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/marshalutil"
"github.com/iotaledger/hive.go/stringify" "github.com/iotaledger/hive.go/stringify"
...@@ -18,17 +24,28 @@ const ( ...@@ -18,17 +24,28 @@ const (
type Payload struct { type Payload struct {
payloadType payload.Type payloadType payload.Type
address address.Address address address.Address
nonce uint64
} }
// Type represents the identifier for the faucet Payload type. // Type represents the identifier for the faucet Payload type.
var Type = payload.Type(2) 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. // New is the constructor of a Payload and creates a new Payload object from the given details.
func New(addr address.Address) *Payload { func New(addr address.Address, powTarget int) (*Payload, error) {
return &Payload{ p := &Payload{
payloadType: Type, payloadType: Type,
address: addr, 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() { func init() {
...@@ -56,15 +73,22 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload, ...@@ -56,15 +73,22 @@ func FromBytes(bytes []byte, optionalTargetObject ...*Payload) (result *Payload,
if err != nil { if err != nil {
return return
} }
payloadBytes, err := marshalUtil.ReadUint32() if _, err = marshalUtil.ReadUint32(); err != nil {
return
}
addr, err := marshalUtil.ReadBytes(address.Length)
if err != nil { if err != nil {
return 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 { if err != nil {
return return
} }
result.address, _, _ = address.FromBytes(addr)
// return the number of bytes we processed // return the number of bytes we processed
consumedBytes = marshalUtil.ReadOffset() consumedBytes = marshalUtil.ReadOffset()
...@@ -89,8 +113,9 @@ func (faucetPayload *Payload) Bytes() []byte { ...@@ -89,8 +113,9 @@ func (faucetPayload *Payload) Bytes() []byte {
// marshal the payload specific information // marshal the payload specific information
marshalUtil.WriteUint32(faucetPayload.Type()) marshalUtil.WriteUint32(faucetPayload.Type())
marshalUtil.WriteUint32(uint32(len(faucetPayload.address))) marshalUtil.WriteUint32(uint32(address.Length + pow.NonceBytes))
marshalUtil.WriteBytes(faucetPayload.address.Bytes()) marshalUtil.WriteBytes(faucetPayload.address.Bytes())
marshalUtil.WriteUint64(faucetPayload.nonce)
// return result // return result
return marshalUtil.Bytes() return marshalUtil.Bytes()
......
...@@ -5,10 +5,9 @@ import ( ...@@ -5,10 +5,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/crypto/ed25519"
"github.com/iotaledger/hive.go/identity" "github.com/iotaledger/hive.go/identity"
"github.com/stretchr/testify/assert"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
...@@ -19,10 +18,10 @@ func ExamplePayload() { ...@@ -19,10 +18,10 @@ func ExamplePayload() {
local := identity.NewLocalIdentity(keyPair.PublicKey, keyPair.PrivateKey) local := identity.NewLocalIdentity(keyPair.PublicKey, keyPair.PrivateKey)
// 1. create faucet payload // 1. create faucet payload
faucetPayload := New( faucetPayload, err := New(address.Random(), 4)
// request address if err != nil {
address.Random(), panic(err)
) }
// 2. build actual message // 2. build actual message
tx := message.New( tx := message.New(
...@@ -39,7 +38,10 @@ func ExamplePayload() { ...@@ -39,7 +38,10 @@ func ExamplePayload() {
} }
func TestPayload(t *testing.T) { 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()) clonedPayload1, err, _ := FromBytes(originalPayload.Bytes())
if err != nil { if err != nil {
......
package valuetransfers package valuetransfers
import ( import (
"errors"
"os" "os"
"sync" "sync"
"time" "time"
...@@ -10,6 +11,7 @@ import ( ...@@ -10,6 +11,7 @@ import (
valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" valuepayload "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tipmanager" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tipmanager"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction"
"github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message"
messageTangle "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" messageTangle "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle"
"github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/shutdown"
...@@ -44,6 +46,10 @@ func init() { ...@@ -44,6 +46,10 @@ func init() {
} }
var ( 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 is the "plugin" instance of the value-transfers application.
app *node.Plugin app *node.Plugin
appOnce sync.Once appOnce sync.Once
...@@ -217,7 +223,35 @@ func TipManager() *tipmanager.TipManager { ...@@ -217,7 +223,35 @@ func TipManager() *tipmanager.TipManager {
// ValueObjectFactory returns the ValueObjectFactory singleton. // ValueObjectFactory returns the ValueObjectFactory singleton.
func ValueObjectFactory() *tangle.ValueObjectFactory { func ValueObjectFactory() *tangle.ValueObjectFactory {
valueObjectFactoryOnce.Do(func() { valueObjectFactoryOnce.Do(func() {
valueObjectFactory = tangle.NewValueObjectFactory(TipManager()) valueObjectFactory = tangle.NewValueObjectFactory(Tangle(), TipManager())
}) })
return valueObjectFactory 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
}
}
...@@ -11,4 +11,7 @@ var ( ...@@ -11,4 +11,7 @@ var (
// ErrPayloadInvalid represents an error type that is triggered when an invalid payload is detected. // ErrPayloadInvalid represents an error type that is triggered when an invalid payload is detected.
ErrPayloadInvalid = errors.New("payload invalid") 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")
) )
...@@ -9,13 +9,15 @@ import ( ...@@ -9,13 +9,15 @@ import (
// ValueObjectFactory acts as a factory to create new value objects. // ValueObjectFactory acts as a factory to create new value objects.
type ValueObjectFactory struct { type ValueObjectFactory struct {
tangle *Tangle
tipManager *tipmanager.TipManager tipManager *tipmanager.TipManager
Events *ValueObjectFactoryEvents Events *ValueObjectFactoryEvents
} }
// NewValueObjectFactory creates a new ValueObjectFactory. // NewValueObjectFactory creates a new ValueObjectFactory.
func NewValueObjectFactory(tipManager *tipmanager.TipManager) *ValueObjectFactory { func NewValueObjectFactory(tangle *Tangle, tipManager *tipmanager.TipManager) *ValueObjectFactory {
return &ValueObjectFactory{ return &ValueObjectFactory{
tangle: tangle,
tipManager: tipManager, tipManager: tipManager,
Events: &ValueObjectFactoryEvents{ Events: &ValueObjectFactoryEvents{
ValueObjectConstructed: events.NewEvent(valueObjectConstructedEvent), ValueObjectConstructed: events.NewEvent(valueObjectConstructedEvent),
...@@ -25,13 +27,27 @@ func NewValueObjectFactory(tipManager *tipmanager.TipManager) *ValueObjectFactor ...@@ -25,13 +27,27 @@ func NewValueObjectFactory(tipManager *tipmanager.TipManager) *ValueObjectFactor
// IssueTransaction creates a new value object including tip selection and returns it. // IssueTransaction creates a new value object including tip selection and returns it.
// It also triggers the ValueObjectConstructed event once it's done. // 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() 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) v.Events.ValueObjectConstructed.Trigger(valueObject)
return valueObject return
} }
// ValueObjectFactoryEvents represent events happening on a ValueObjectFactory. // ValueObjectFactoryEvents represent events happening on a ValueObjectFactory.
......
...@@ -104,7 +104,7 @@ func (inputs *Inputs) Bytes() (bytes []byte) { ...@@ -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 // 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. // 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 inputs.OrderedMap.ForEach(func(key, value interface{}) bool {
return value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool { return value.(*orderedmap.OrderedMap).ForEach(func(key, value interface{}) bool {
return consumer(value.(OutputID)) return consumer(value.(OutputID))
......
...@@ -25,7 +25,7 @@ import ( ...@@ -25,7 +25,7 @@ import (
// autopeering constants // autopeering constants
const ( const (
ProtocolVersion = 0 // update on protocol changes ProtocolVersion = 0 // update on protocol changes
NetworkVersion = 2 // update on network changes NetworkVersion = 3 // update on network changes
) )
var ( var (
......
...@@ -18,7 +18,7 @@ var ( ...@@ -18,7 +18,7 @@ var (
const ( const (
// AppVersion version number // AppVersion version number
AppVersion = "v0.2.0" AppVersion = "v0.2.1"
// AppName app code name // AppName app code name
AppName = "GoShimmer" AppName = "GoShimmer"
......
...@@ -2,9 +2,12 @@ package dashboard ...@@ -2,9 +2,12 @@ package dashboard
import ( import (
"net/http" "net/http"
"sync"
"github.com/iotaledger/goshimmer/dapps/faucet"
faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload" faucetpayload "github.com/iotaledger/goshimmer/dapps/faucet/packages/payload"
"github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" "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/messagelayer"
"github.com/labstack/echo" "github.com/labstack/echo"
...@@ -32,8 +35,16 @@ func setupFaucetRoutes(routeGroup *echo.Group) { ...@@ -32,8 +35,16 @@ func setupFaucetRoutes(routeGroup *echo.Group) {
}) })
} }
var fundingReqMu = sync.Mutex{}
func sendFaucetReq(addr address.Address) (res *ReqMsg, err error) { 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 { if msg == nil {
return nil, errors.Wrapf(ErrInternalError, "Fail to send faucet request") return nil, errors.Wrapf(ErrInternalError, "Fail to send faucet request")
} }
......
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
const ( const (
// DBVersion defines the version of the database schema this version of GoShimmer supports. // 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. // Every time there's a breaking change regarding the stored data, this version flag should be adjusted.
DBVersion = 3 DBVersion = 4
) )
var ( var (
......
package gossip package gossip
import ( import (
"time"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
const ( const (
// CfgGossipPort defines the config flag of the gossip port. // CfgGossipPort defines the config flag of the gossip port.
CfgGossipPort = "gossip.port" CfgGossipPort = "gossip.port"
// CfgGossipTipsBroadcastInterval the interval in which the oldest known tip is re-broadcasted.
CfgGossipTipsBroadcastInterval = "gossip.tipsBroadcaster.interval"
) )
func init() { func init() {
flag.Int(CfgGossipPort, 14666, "tcp port for gossip connection") 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")
} }
...@@ -42,6 +42,7 @@ func configure(*node.Plugin) { ...@@ -42,6 +42,7 @@ func configure(*node.Plugin) {
configureLogging() configureLogging()
configureMessageLayer() configureMessageLayer()
configureAutopeering() configureAutopeering()
configureTipBroadcaster()
} }
func run(*node.Plugin) { func run(*node.Plugin) {
......
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())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment