diff --git a/clients/chainclient/chainclient.go b/clients/chainclient/chainclient.go index 7a0ad49444..3c72f4faa3 100644 --- a/clients/chainclient/chainclient.go +++ b/clients/chainclient/chainclient.go @@ -5,6 +5,7 @@ import ( "math" iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/clients/apiclient" "github.com/iotaledger/wasp/clients/apiextensions" "github.com/iotaledger/wasp/packages/cryptolib" @@ -47,6 +48,7 @@ type PostRequestParams struct { Allowance *isc.Assets gasBudget uint64 AutoAdjustStorageDeposit bool + OnlyUnlockedOutputs bool } func (par *PostRequestParams) GasBudget() uint64 { @@ -69,7 +71,16 @@ func (c *Client) Post1Request( entryPoint isc.Hname, params ...PostRequestParams, ) (*iotago.Transaction, error) { - outputsSet, err := c.Layer1Client.OutputMap(c.KeyPair.Address()) + par := defaultParams(params...) + var outputsSet iotago.OutputSet + var err error + + if par.OnlyUnlockedOutputs { + outputsSet, err = c.Layer1Client.OutputMapNonLocked(c.KeyPair.Address()) + } else { + outputsSet, err = c.Layer1Client.OutputMap(c.KeyPair.Address()) + } + if err != nil { return nil, err } @@ -83,8 +94,16 @@ func (c *Client) PostNRequests( requestsCount int, params ...PostRequestParams, ) ([]*iotago.Transaction, error) { + par := defaultParams(params...) + var outputs iotago.OutputSet var err error - outputs, err := c.Layer1Client.OutputMap(c.KeyPair.Address()) + + if par.OnlyUnlockedOutputs { + outputs, err = c.Layer1Client.OutputMapNonLocked(c.KeyPair.Address()) + } else { + outputs, err = c.Layer1Client.OutputMap(c.KeyPair.Address()) + } + if err != nil { return nil, err } diff --git a/packages/chain/mempool/mempool.go b/packages/chain/mempool/mempool.go index f6819f3a40..6ee92e1574 100644 --- a/packages/chain/mempool/mempool.go +++ b/packages/chain/mempool/mempool.go @@ -52,6 +52,7 @@ import ( "github.com/samber/lo" "github.com/iotaledger/hive.go/logger" + consGR "github.com/iotaledger/wasp/packages/chain/cons/cons_gr" "github.com/iotaledger/wasp/packages/chain/mempool/distsync" "github.com/iotaledger/wasp/packages/cryptolib" @@ -505,8 +506,9 @@ func (mpi *mempoolImpl) distSyncRequestReceivedCB(request isc.Request) bool { return false } if err := mpi.shouldAddOffledgerRequest(offLedgerReq); err == nil { - mpi.addOffledger(offLedgerReq) - return true + mpi.log.Warn("shouldAddOffledgerRequest: true, trying to add to offledger %T: %+v", request, request) + + return mpi.addOffledger(offLedgerReq) } return false } @@ -558,10 +560,13 @@ func (mpi *mempoolImpl) shouldAddOffledgerRequest(req isc.OffLedgerRequest) erro return nil } -func (mpi *mempoolImpl) addOffledger(request isc.OffLedgerRequest) { - mpi.offLedgerPool.Add(request) +func (mpi *mempoolImpl) addOffledger(request isc.OffLedgerRequest) bool { + if !mpi.offLedgerPool.Add(request) { + return false + } mpi.metrics.IncRequestsReceived(request) mpi.log.Debugf("accepted by the mempool, requestID: %s", request.ID().String()) + return true } func (mpi *mempoolImpl) handleServerNodesUpdated(recv *reqServerNodesUpdated) { @@ -806,8 +811,9 @@ func (mpi *mempoolImpl) handleReceiveOnLedgerRequest(request isc.OnLedgerRequest func (mpi *mempoolImpl) handleReceiveOffLedgerRequest(request isc.OffLedgerRequest) { mpi.log.Debugf("Received request %v from outside.", request.ID()) - mpi.addOffledger(request) - mpi.sendMessages(mpi.distSync.Input(distsync.NewInputPublishRequest(request))) + if mpi.addOffledger(request) { + mpi.sendMessages(mpi.distSync.Input(distsync.NewInputPublishRequest(request))) + } } func (mpi *mempoolImpl) handleTangleTimeUpdated(tangleTime time.Time) { diff --git a/packages/chain/mempool/offledger_pool.go b/packages/chain/mempool/offledger_pool.go index ef06097874..7fab9d414f 100644 --- a/packages/chain/mempool/offledger_pool.go +++ b/packages/chain/mempool/offledger_pool.go @@ -14,6 +14,7 @@ import ( "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/logger" + consGR "github.com/iotaledger/wasp/packages/chain/cons/cons_gr" "github.com/iotaledger/wasp/packages/isc" "github.com/iotaledger/wasp/packages/kv/codec" @@ -67,7 +68,7 @@ func (p *OffLedgerPool) Get(reqRef *isc.RequestRef) isc.OffLedgerRequest { return entry.req } -func (p *OffLedgerPool) Add(request isc.OffLedgerRequest) { +func (p *OffLedgerPool) Add(request isc.OffLedgerRequest) bool { ref := isc.RequestRefFromRequest(request) entry := &OrderedPoolEntry{req: request, ts: time.Now()} account := request.SenderAccount().String() @@ -75,8 +76,8 @@ func (p *OffLedgerPool) Add(request isc.OffLedgerRequest) { // // add the request to the "request ref" Lookup Table if !p.refLUT.Set(ref.AsKey(), entry) { - p.log.Debugf("NOT ADDED, already exists. reqID: %v as key=%v, senderAccount: ", request.ID(), ref, account) - return // not added already exists + p.log.Debugf("OffLedger Request NOT ADDED, already exists. reqID: %v as key=%v, senderAccount: %v", request.ID(), ref, account) + return true // not added already exists } // @@ -134,7 +135,8 @@ func (p *OffLedgerPool) Add(request isc.OffLedgerRequest) { deleted := p.LimitPoolSize() if lo.Contains(deleted, entry) { // this exact request was deleted from the pool, do not update metrics, or mark available - return + p.log.Debugf("OffLedger Request NOT ADDED, was removed already. reqID: %v as key=%v, senderAccount: %v", request.ID(), ref, account) + return false } // @@ -142,6 +144,7 @@ func (p *OffLedgerPool) Add(request isc.OffLedgerRequest) { p.log.Debugf("ADD %v as key=%v, senderAccount: %s", request.ID(), ref, account) p.sizeMetric(p.refLUT.Size()) p.waitReq.MarkAvailable(request) + return true } // LimitPoolSize drops the txs with the lowest price if the total number of requests is too big diff --git a/packages/l1connection/l1connection.go b/packages/l1connection/l1connection.go index 46609d614a..c96474b661 100644 --- a/packages/l1connection/l1connection.go +++ b/packages/l1connection/l1connection.go @@ -37,15 +37,17 @@ type Config struct { } type Client interface { - // requests funds from faucet, waits for confirmation + // RequestFunds requests funds from faucet, waits for confirmation RequestFunds(addr iotago.Address, timeout ...time.Duration) error - // sends a tx (including tipselection and local PoW if necessary) and waits for confirmation + // PostTxAndWaitUntilConfirmation sends a tx (including tipselection and local PoW if necessary) and waits for confirmation PostTxAndWaitUntilConfirmation(tx *iotago.Transaction, timeout ...time.Duration) (iotago.BlockID, error) - // returns the outputs owned by a given address + // OutputMap returns the outputs owned by a given address OutputMap(myAddress iotago.Address, timeout ...time.Duration) (iotago.OutputSet, error) - // output + // OutputMapNonLocked returns the outputs owned by a given address, excluding any locked outputs (mainly relevant for sending TXs from clients) + OutputMapNonLocked(myAddress iotago.Address, timeout ...time.Duration) (iotago.OutputSet, error) + // GetAliasOutput output GetAliasOutput(aliasID iotago.AliasID, timeout ...time.Duration) (iotago.OutputID, iotago.Output, error) - // used to query the health endpoint of the node + // Health used to query the health endpoint of the node Health(timeout ...time.Duration) (bool, error) } @@ -87,8 +89,31 @@ func NewClient(config Config, log *logger.Logger, timeout ...time.Duration) Clie } } -// OutputMap implements L1Connection -func (c *l1client) OutputMap(myAddress iotago.Address, timeout ...time.Duration) (iotago.OutputSet, error) { +func (c *l1client) readOutputs(ctx context.Context, queries []nodeclient.IndexerQuery) (iotago.OutputSet, error) { + result := make(map[iotago.OutputID]iotago.Output) + + for _, query := range queries { + res, err := c.indexerClient.Outputs(ctx, query) + if err != nil { + return nil, fmt.Errorf("failed to query address outputs: %w", err) + } + for res.Next() { + outputs, err := res.Outputs() + if err != nil { + return nil, fmt.Errorf("failed to fetch address outputs: %w", err) + } + + outputIDs := res.Response.Items.MustOutputIDs() + for i := range outputs { + result[outputIDs[i]] = outputs[i] + } + } + } + return result, nil +} + +// OutputMapNonLocked implements L1Connection +func (c *l1client) OutputMapNonLocked(myAddress iotago.Address, timeout ...time.Duration) (iotago.OutputSet, error) { ctxWithTimeout, cancelContext := newCtx(c.ctx, timeout...) defer cancelContext() @@ -103,44 +128,36 @@ func (c *l1client) OutputMap(myAddress iotago.Address, timeout ...time.Duration) HasTimelock: &trueParam, TimelockedBefore: uint32(time.Now().Unix()), }, - IndexerNativeTokenParas: nodeclient.IndexerNativeTokenParas{ - HasNativeTokens: &falseParam, - }, }, &nodeclient.BasicOutputsQuery{ AddressBech32: bech32Addr, IndexerTimelockParas: nodeclient.IndexerTimelockParas{ HasTimelock: &falseParam, }, - IndexerNativeTokenParas: nodeclient.IndexerNativeTokenParas{ - HasNativeTokens: &falseParam, - }, }, &nodeclient.FoundriesQuery{AliasAddressBech32: bech32Addr}, &nodeclient.NFTsQuery{AddressBech32: bech32Addr}, &nodeclient.AliasesQuery{GovernorBech32: bech32Addr}, } - result := make(map[iotago.OutputID]iotago.Output) + return c.readOutputs(ctxWithTimeout, queries) +} - for _, query := range queries { - res, err := c.indexerClient.Outputs(ctxWithTimeout, query) - if err != nil { - return nil, fmt.Errorf("failed to query address outputs: %w", err) - } - for res.Next() { - outputs, err := res.Outputs() - if err != nil { - return nil, fmt.Errorf("failed to fetch address outputs: %w", err) - } +// OutputMap implements L1Connection +func (c *l1client) OutputMap(myAddress iotago.Address, timeout ...time.Duration) (iotago.OutputSet, error) { + ctxWithTimeout, cancelContext := newCtx(c.ctx, timeout...) + defer cancelContext() - outputIDs := res.Response.Items.MustOutputIDs() - for i := range outputs { - result[outputIDs[i]] = outputs[i] - } - } + bech32Addr := myAddress.Bech32(parameters.L1().Protocol.Bech32HRP) + + queries := []nodeclient.IndexerQuery{ + &nodeclient.BasicOutputsQuery{AddressBech32: bech32Addr}, + &nodeclient.FoundriesQuery{AliasAddressBech32: bech32Addr}, + &nodeclient.NFTsQuery{AddressBech32: bech32Addr}, + &nodeclient.AliasesQuery{GovernorBech32: bech32Addr}, } - return result, nil + + return c.readOutputs(ctxWithTimeout, queries) } // postBlock sends a block (including tipselection and local PoW if necessary). diff --git a/tools/cluster/tests/wasp-cli_test.go b/tools/cluster/tests/wasp-cli_test.go index c3a7665d2f..cfbc91249e 100644 --- a/tools/cluster/tests/wasp-cli_test.go +++ b/tools/cluster/tests/wasp-cli_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/clients/apiclient" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/parameters" @@ -203,6 +204,14 @@ func TestWaspCLIDeposit(t *testing.T) { ) require.Regexp(t, `.*Error: \(empty\).*`, strings.Join(out, "")) + out = w.MustRun("balance") + for _, line := range out { + if strings.Contains(line, "0x") { + balance := strings.TrimSpace(strings.Split(line, ":")[1]) + require.Equal(t, balance, "2") + } + } + // deposit the native token to the chain (to an ethereum account) w.MustRun( "chain", "deposit", eth.String(), diff --git a/tools/wasp-cli/chain/postrequest.go b/tools/wasp-cli/chain/postrequest.go index 08c16485ce..67b76ee3a5 100644 --- a/tools/wasp-cli/chain/postrequest.go +++ b/tools/wasp-cli/chain/postrequest.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/clients/chainclient" "github.com/iotaledger/wasp/packages/isc" "github.com/iotaledger/wasp/packages/transaction" @@ -46,7 +47,7 @@ func postRequest(nodeName, chain, hname, fname string, params chainclient.PostRe } util.WithSCTransaction(config.GetChain(chain), nodeName, func() (*iotago.Transaction, error) { - return scClient.PostRequest(fname, params) + return scClient.PostRequest(fname, params, chainclient.PostRequestParams{OnlyUnlockedOutputs: true}) }) }