Skip to content

Commit d4aef1c

Browse files
authored
v0.9.7
2 parents 7d42ba3 + 14b62d8 commit d4aef1c

File tree

15 files changed

+100
-39
lines changed

15 files changed

+100
-39
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# v0.9.7 - 2022-09-05
2+
3+
> This release introduces major bugfixes to the networking and the faucet.
4+
5+
- Fix notarization and networking bugs (#2417)
6+
- Make wallet stateless to prevent faucet getting stuck (#2415)
7+
- Fix: /healthz endpoint (#2379)
8+
19
# v0.9.6 - 2022-09-01
210

311
> This release introduces major bugfixes to epoch notarization and networking.

client/wallet/options.go

+7
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,10 @@ func GenericConnector(connector Connector) Option {
8181
wallet.connector = connector
8282
}
8383
}
84+
85+
// Stateless allows to run the wallet in a stateless mode, meaning outputs will always be refreshed from the connector.
86+
func Stateless(stateless bool) Option {
87+
return func(wallet *Wallet) {
88+
wallet.Stateless = stateless
89+
}
90+
}

client/wallet/output_manager.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ type OutputManager struct {
1010
addressManager *AddressManager
1111
connector Connector
1212
unspentOutputs OutputsByAddressAndOutputID
13+
14+
optsStateless bool
1315
}
1416

1517
// NewUnspentOutputManager creates a new UnspentOutputManager.
16-
func NewUnspentOutputManager(addressManager *AddressManager, connector Connector) (outputManager *OutputManager) {
18+
func NewUnspentOutputManager(addressManager *AddressManager, connector Connector, stateless bool) (outputManager *OutputManager) {
1719
outputManager = &OutputManager{
1820
addressManager: addressManager,
1921
connector: connector,
2022
unspentOutputs: NewAddressToOutputs(),
23+
optsStateless: stateless,
2124
}
2225

2326
if err := outputManager.Refresh(true); err != nil {
@@ -44,9 +47,12 @@ func (o *OutputManager) Refresh(includeSpentAddresses ...bool) error {
4447
if _, addressExists := o.unspentOutputs[addy]; !addressExists {
4548
o.unspentOutputs[addy] = make(map[utxo.OutputID]*Output)
4649
}
47-
// mark the output as spent if we already marked it as spent locally
48-
if existingOutput, outputExists := o.unspentOutputs[addy][outputID]; outputExists && existingOutput.Spent {
49-
output.Spent = true
50+
51+
// mark the output as spent if we already marked it as spent locally, only in stateful mode.
52+
if !o.optsStateless {
53+
if existingOutput, outputExists := o.unspentOutputs[addy][outputID]; outputExists && existingOutput.Spent {
54+
output.Spent = true
55+
}
5056
}
5157
o.unspentOutputs[addy][outputID] = output
5258
}

client/wallet/wallet.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type Wallet struct {
5656
reusableAddress bool
5757
ConfirmationPollInterval time.Duration
5858
ConfirmationTimeout time.Duration
59+
Stateless bool
5960
}
6061

6162
// New is the factory method of the wallet. It either creates a new wallet or restores the wallet backup that is handed
@@ -93,7 +94,7 @@ func New(options ...Option) (wallet *Wallet) {
9394
}
9495

9596
// initialize output manager
96-
wallet.outputManager = NewUnspentOutputManager(wallet.addressManager, wallet.connector)
97+
wallet.outputManager = NewUnspentOutputManager(wallet.addressManager, wallet.connector, wallet.Stateless)
9798
err := wallet.outputManager.Refresh(true)
9899
if err != nil {
99100
panic(err)

packages/core/epoch/types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
var (
2121
// GenesisTime is the time (Unix in seconds) of the genesis.
22-
GenesisTime int64 = 1662035280
22+
GenesisTime int64 = 1662385954
2323
// Duration is the default epoch duration in seconds.
2424
Duration int64 = 10
2525
)

packages/core/notarization/commitments.go

+19-2
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ func (f *EpochCommitmentFactory) removeStateMutationLeaf(ei epoch.Index, txID ut
170170
return removeLeaf(commitment.stateMutationTree, txID.Bytes())
171171
}
172172

173+
// hasStateMutationLeaf returns if the leaf is part of the state mutation sparse merkle tree.
174+
func (f *EpochCommitmentFactory) hasStateMutationLeaf(ei epoch.Index, txID utxo.TransactionID) (has bool, err error) {
175+
commitment, err := f.getCommitmentTrees(ei)
176+
if err != nil {
177+
return false, errors.Wrap(err, "could not get commitment while deleting state mutation leaf")
178+
}
179+
return commitment.stateMutationTree.Has(txID.Bytes())
180+
}
181+
173182
// insertTangleLeaf inserts blk to the Tangle sparse merkle tree.
174183
func (f *EpochCommitmentFactory) insertTangleLeaf(ei epoch.Index, blkID tangleold.BlockID) error {
175184
commitment, err := f.getCommitmentTrees(ei)
@@ -250,11 +259,19 @@ func (f *EpochCommitmentFactory) storeDiffUTXOs(ei epoch.Index, spent, created [
250259
epochDiffStorage := f.storage.getEpochDiffStorage(ei)
251260

252261
for _, spentOutputWithMetadata := range spent {
253-
epochDiffStorage.spent.Store(spentOutputWithMetadata).Release()
262+
cachedObj, stored := epochDiffStorage.spent.StoreIfAbsent(spentOutputWithMetadata)
263+
if !stored {
264+
continue
265+
}
266+
cachedObj.Release()
254267
}
255268

256269
for _, createdOutputWithMetadata := range created {
257-
epochDiffStorage.created.Store(createdOutputWithMetadata).Release()
270+
cachedObj, stored := epochDiffStorage.created.StoreIfAbsent(createdOutputWithMetadata)
271+
if !stored {
272+
continue
273+
}
274+
cachedObj.Release()
258275
}
259276
}
260277

packages/core/notarization/manager.go

+15
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,15 @@ func (m *Manager) OnTransactionInclusionUpdated(event *ledger.TransactionInclusi
430430

431431
txID := event.TransactionID
432432

433+
has, err := m.isTransactionInEpoch(event.TransactionID, oldEpoch);
434+
if err != nil {
435+
m.log.Error(err)
436+
return
437+
}
438+
if !has {
439+
return
440+
}
441+
433442
var spent, created []*ledger.OutputWithMetadata
434443
m.tangle.Ledger.Storage.CachedTransaction(txID).Consume(func(tx utxo.Transaction) {
435444
spent, created = m.resolveOutputs(tx)
@@ -576,6 +585,8 @@ func (m *Manager) includeTransactionInEpoch(txID utxo.TransactionID, ei epoch.In
576585
if err := m.epochCommitmentFactory.insertStateMutationLeaf(ei, txID); err != nil {
577586
return err
578587
}
588+
// TODO: in case of a reorg, a transaction spending the same output of another TX will cause a duplicate element
589+
// in cache in the objectstorage if we don't hook to the reorged transaction "orphanage".
579590
m.epochCommitmentFactory.storeDiffUTXOs(ei, spent, created)
580591

581592
m.Events.StateMutationTreeInserted.Trigger(&StateMutationTreeUpdatedEvent{TransactionID: txID})
@@ -596,6 +607,10 @@ func (m *Manager) removeTransactionFromEpoch(txID utxo.TransactionID, ei epoch.I
596607
return nil
597608
}
598609

610+
func (m *Manager) isTransactionInEpoch(txID utxo.TransactionID, ei epoch.Index) (has bool, err error) {
611+
return m.epochCommitmentFactory.hasStateMutationLeaf(ei, txID)
612+
}
613+
599614
// isCommittable returns if the epoch is committable, if all conflicts are resolved and the epoch is old enough.
600615
func (m *Manager) isCommittable(ei epoch.Index) bool {
601616
return m.isOldEnough(ei) && m.allPastConflictsAreResolved(ei)

packages/node/p2p/manager.go

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ func (m *Manager) Send(packet proto.Message, protocolID protocol.ID, to ...ident
179179
stream := nbr.GetStream(protocolID)
180180
if stream == nil {
181181
m.log.Warnw("send error, no stream for protocol", "peer-id", nbr.ID(), "protocol", protocolID)
182+
nbr.Close()
182183
continue
183184
}
184185
if err := stream.WritePacket(packet); err != nil {

packages/node/p2p/stream.go

+29-19
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (m *Manager) dialPeer(ctx context.Context, p *peer.Peer, opts []ConnectPeer
8181
}
8282

8383
if len(streams) == 0 {
84-
return nil, fmt.Errorf("no streams initiated with peer %s / %s", address, p.ID())
84+
return nil, errors.Errorf("no streams initiated with peer %s / %s", address, p.ID())
8585
}
8686

8787
return streams, nil
@@ -102,7 +102,7 @@ func (m *Manager) acceptPeer(ctx context.Context, p *peer.Peer, opts []ConnectPe
102102
ctx, cancel = context.WithTimeout(ctx, defaultConnectionTimeout)
103103
defer cancel()
104104
}
105-
am, err := m.newAcceptMatcher(p, protocolID)
105+
amCtx, am, err := m.newAcceptMatcher(ctx, p, protocolID)
106106
if err != nil {
107107
return nil, errors.WithStack(err)
108108
}
@@ -118,11 +118,11 @@ func (m *Manager) acceptPeer(ctx context.Context, p *peer.Peer, opts []ConnectPe
118118
select {
119119
case ps := <-streamCh:
120120
if ps.Protocol() != protocolID {
121-
return nil, fmt.Errorf("accepted stream has wrong protocol: %s != %s", ps.Protocol(), protocolID)
121+
return nil, errors.Errorf("accepted stream has wrong protocol: %s != %s", ps.Protocol(), protocolID)
122122
}
123123
return ps, nil
124-
case <-ctx.Done():
125-
err := ctx.Err()
124+
case <-amCtx.Done():
125+
err := amCtx.Err()
126126
if errors.Is(err, context.DeadlineExceeded) {
127127
m.log.Debugw("accept timeout", "id", am.Peer.ID(), "proto", protocolID)
128128
return nil, errors.WithStack(ErrTimeout)
@@ -166,7 +166,7 @@ func (m *Manager) acceptPeer(ctx context.Context, p *peer.Peer, opts []ConnectPe
166166
}
167167

168168
if len(streams) == 0 {
169-
return nil, fmt.Errorf("no streams accepted from peer %s", p.ID())
169+
return nil, errors.Errorf("no streams accepted from peer %s", p.ID())
170170
}
171171

172172
return streams, nil
@@ -175,7 +175,7 @@ func (m *Manager) acceptPeer(ctx context.Context, p *peer.Peer, opts []ConnectPe
175175
func (m *Manager) initiateStream(ctx context.Context, libp2pID libp2ppeer.ID, protocolID protocol.ID) (*PacketsStream, error) {
176176
protocolHandler, registered := m.registeredProtocols[protocolID]
177177
if !registered {
178-
return nil, fmt.Errorf("cannot initiate stream protocol %s is not registered", protocolID)
178+
return nil, errors.Errorf("cannot initiate stream protocol %s is not registered", protocolID)
179179
}
180180
stream, err := m.GetP2PHost().NewStream(ctx, libp2pID, protocolID)
181181
if err != nil {
@@ -210,11 +210,14 @@ func (m *Manager) handleStream(stream network.Stream) {
210210
am := m.matchNewStream(stream)
211211
if am != nil {
212212
am.StreamChMutex.RLock()
213+
defer am.StreamChMutex.RUnlock()
213214
streamCh := am.StreamCh[protocolID]
214-
am.StreamChMutex.RUnlock()
215215

216-
m.log.Debugw("incoming stream matched", "id", am.Peer.ID(), "proto", protocolID)
217-
streamCh <- ps
216+
select {
217+
case <-am.Ctx.Done():
218+
case streamCh <- ps:
219+
m.log.Debugw("incoming stream matched", "id", am.Peer.ID(), "proto", protocolID)
220+
}
218221
} else {
219222
// close the connection if not matched
220223
m.log.Debugw("unexpected connection", "addr", stream.Conn().RemoteMultiaddr(),
@@ -230,54 +233,61 @@ type AcceptMatcher struct {
230233
Libp2pID libp2ppeer.ID
231234
StreamChMutex sync.RWMutex
232235
StreamCh map[protocol.ID]chan *PacketsStream
236+
Ctx context.Context
237+
CtxCancel context.CancelFunc
233238
}
234239

235-
func (m *Manager) newAcceptMatcher(p *peer.Peer, protocolID protocol.ID) (*AcceptMatcher, error) {
240+
func (m *Manager) newAcceptMatcher(ctx context.Context, p *peer.Peer, protocolID protocol.ID) (context.Context, *AcceptMatcher, error) {
236241
m.acceptMutex.Lock()
237242
defer m.acceptMutex.Unlock()
238243

239244
libp2pID, err := libp2putil.ToLibp2pPeerID(p)
240245
if err != nil {
241-
return nil, errors.WithStack(err)
246+
return nil, nil, errors.WithStack(err)
242247
}
243248

244249
acceptMatcher, acceptExists := m.acceptMap[libp2pID]
245250
if acceptExists {
246251
acceptMatcher.StreamChMutex.Lock()
247252
defer acceptMatcher.StreamChMutex.Unlock()
248253
if _, streamChanExists := acceptMatcher.StreamCh[protocolID]; streamChanExists {
249-
return nil, nil
254+
return nil, nil, nil
250255
}
251256
acceptMatcher.StreamCh[protocolID] = make(chan *PacketsStream)
252-
return acceptMatcher, nil
257+
return acceptMatcher.Ctx, acceptMatcher, nil
253258
}
254259

260+
cancelCtx, cancelCtxFunc := context.WithCancel(ctx)
261+
255262
am := &AcceptMatcher{
256-
Peer: p,
257-
Libp2pID: libp2pID,
258-
StreamCh: make(map[protocol.ID]chan *PacketsStream),
263+
Peer: p,
264+
Libp2pID: libp2pID,
265+
StreamCh: make(map[protocol.ID]chan *PacketsStream),
266+
Ctx: cancelCtx,
267+
CtxCancel: cancelCtxFunc,
259268
}
260269

261270
am.StreamCh[protocolID] = make(chan *PacketsStream)
262271

263272
m.acceptMap[libp2pID] = am
264273

265-
return am, nil
274+
return cancelCtx, am, nil
266275
}
267276

268277
func (m *Manager) removeAcceptMatcher(am *AcceptMatcher, protocolID protocol.ID) {
269278
m.acceptMutex.Lock()
270279
defer m.acceptMutex.Unlock()
271280

272281
existingAm := m.acceptMap[am.Libp2pID]
282+
273283
existingAm.StreamChMutex.Lock()
274284
defer existingAm.StreamChMutex.Unlock()
275285

276-
close(existingAm.StreamCh[protocolID])
277286
delete(existingAm.StreamCh, protocolID)
278287

279288
if len(existingAm.StreamCh) == 0 {
280289
delete(m.acceptMap, am.Libp2pID)
290+
existingAm.CtxCancel()
281291
}
282292
}
283293

plugins/autopeering/discovery/parameters.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import "github.com/iotaledger/goshimmer/plugins/config"
55
// ParametersDefinitionDiscovery contains the definition of configuration parameters used by the autopeering peer discovery.
66
type ParametersDefinitionDiscovery struct {
77
// NetworkVersion defines the config flag of the network version.
8-
NetworkVersion uint32 `default:"64" usage:"autopeering network version"`
8+
NetworkVersion uint32 `default:"65" usage:"autopeering network version"`
99

1010
// EntryNodes defines the config flag of the entry nodes.
1111
EntryNodes []string `default:"2PV5487xMw5rasGBXXWeqSi4hLz7r19YBt8Y1TGAsQbj@analysisentry-01.devnet.shimmer.iota.cafe:15626,5EDH4uY78EA6wrBkHHAVBWBMDt7EcksRq6pjzipoW15B@entry-0.devnet.tanglebay.com:14646,CAB87iQZR6BjBrCgEBupQJ4gpEBgvGKKv3uuGVRBKb4n@entry-1.devnet.tanglebay.com:14646" usage:"list of trusted entry nodes for auto peering"`

plugins/banner/plugin.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ var (
1515
Plugin = node.NewPlugin(PluginName, nil, node.Enabled, configure, run)
1616

1717
// AppVersion version number
18-
AppVersion = "v0.9.6"
18+
AppVersion = "v0.9.7"
1919
// SimplifiedAppVersion is the version number without commit hash
2020
SimplifiedAppVersion = simplifiedVersion(AppVersion)
2121
)

plugins/database/versioning.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
const (
1212
// DBVersion defines the version of the database schema this version of GoShimmer supports.
1313
// Every time there's a breaking change regarding the stored data, this version flag should be adjusted.
14-
DBVersion = 64
14+
DBVersion = 65
1515
)
1616

1717
var (

plugins/faucet/connector.go

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package faucet
22

33
import (
4-
"fmt"
5-
64
"github.com/iotaledger/hive.go/core/types/confirmation"
75
"github.com/pkg/errors"
86

@@ -33,10 +31,8 @@ func (f *FaucetConnector) UnspentOutputs(addresses ...address.Address) (unspentO
3331
unspentOutputs = make(map[address.Address]map[utxo.OutputID]*wallet.Output)
3432

3533
for _, addr := range addresses {
36-
fmt.Println("> Getting unspent outputs for ", addr.Base58())
3734
f.indexer.CachedAddressOutputMappings(addr.Address()).Consume(func(mapping *indexer.AddressOutputMapping) {
3835
f.tangle.Ledger.Storage.CachedOutput(mapping.OutputID()).Consume(func(output utxo.Output) {
39-
fmt.Println("> > Found output ", output.String())
4036
if typedOutput, ok := output.(devnetvm.Output); ok {
4137
f.tangle.Ledger.Storage.CachedOutputMetadata(typedOutput.ID()).Consume(func(outputMetadata *ledger.OutputMetadata) {
4238
if !outputMetadata.IsSpent() {
@@ -61,7 +57,6 @@ func (f *FaucetConnector) UnspentOutputs(addresses ...address.Address) (unspentO
6157
})
6258
})
6359
}
64-
fmt.Printf("%+v\n", unspentOutputs)
6560
return
6661
}
6762

plugins/faucet/faucet.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func NewFaucet(faucetSeed *seed.Seed) (f *Faucet) {
3131
wallet.FaucetPowDifficulty(Parameters.PowDifficulty),
3232
wallet.ConfirmationTimeout(Parameters.MaxAwait),
3333
wallet.ConfirmationPollingInterval(500*time.Millisecond),
34+
wallet.Stateless(true),
3435
)}
3536
// We use index 1 as a proxy address from which we send the funds to the requester.
3637
f.Wallet.NewReceiveAddress()

plugins/webapi/healthz/plugin.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/labstack/echo"
1010
"go.uber.org/dig"
1111

12-
"github.com/iotaledger/goshimmer/packages/core/tangleold"
12+
"github.com/iotaledger/goshimmer/packages/core/bootstrapmanager"
1313

1414
"github.com/iotaledger/goshimmer/packages/node/shutdown"
1515
)
@@ -20,8 +20,8 @@ const PluginName = "WebAPIHealthzEndpoint"
2020
type dependencies struct {
2121
dig.In
2222

23-
Server *echo.Echo
24-
Tangle *tangleold.Tangle `optional:"true"`
23+
Server *echo.Echo
24+
BootstrapManager *bootstrapmanager.Manager `optional:"true"`
2525
}
2626

2727
var (
@@ -50,7 +50,7 @@ func worker(ctx context.Context) {
5050
}
5151

5252
func getHealthz(c echo.Context) error {
53-
if deps.Tangle != nil {
53+
if deps.BootstrapManager != nil && !deps.BootstrapManager.Bootstrapped() {
5454
return c.NoContent(http.StatusServiceUnavailable)
5555
}
5656
return c.NoContent(http.StatusOK)

0 commit comments

Comments
 (0)