Skip to content

Commit

Permalink
Merges v0.2.1 changes to master (#612)
Browse files Browse the repository at this point in the history
* 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: Wolfgang Welz <welzwo@gmail.com>

* makes send tx web API handler synced

Co-authored-by: Hans Moog <hm@mkjc.net>
Co-authored-by: Wolfgang Welz <welzwo@gmail.com>
Co-authored-by: Luca 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 3e626ea.

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

This reverts commit 9aa3d1b.

* 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: Rajiv Shah <rajivshah1@icloud.com>
Co-authored-by: Wolfgang Welz <welzwo@gmail.com>
Co-authored-by: Hans Moog <3293976+hmoog@users.noreply.github.com>
Co-authored-by: Hans Moog <hm@mkjc.net>
  • Loading branch information
5 people authored Jul 1, 2020
1 parent f172c96 commit 0be22ca
Show file tree
Hide file tree
Showing 26 changed files with 389 additions and 87 deletions.
1 change: 1 addition & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ archives:
- README.md
- LICENSE
- config.json
- snapshot.bin

# Checksum
checksum:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 4 additions & 1 deletion config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
"bindAddress": "0.0.0.0:10895"
},
"gossip": {
"port": 14666
"port": 14666,
"tipsBroadcaster": {
"interval": "10s"
}
},
"logger": {
"level": "info",
Expand Down
38 changes: 34 additions & 4 deletions dapps/faucet/dapp.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package faucet

import (
"crypto"
"runtime"
"sync"
"time"
Expand All @@ -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"
Expand All @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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())
Expand Down Expand Up @@ -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)
Expand Down
74 changes: 40 additions & 34 deletions dapps/faucet/packages/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -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.
Expand All @@ -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()

Expand All @@ -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)
Expand All @@ -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.
Expand Down
8 changes: 7 additions & 1 deletion dapps/faucet/packages/faucet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
)
Expand Down
37 changes: 31 additions & 6 deletions dapps/faucet/packages/payload/payload.go
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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() {
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down
Loading

0 comments on commit 0be22ca

Please sign in to comment.