From 0308aea76857ff27484946fce99004ebf10a3cb8 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 11 Jun 2024 14:36:38 +0200 Subject: [PATCH] fix(state): Integrate new Signer for concurrent tx submission --- blob/service_test.go | 11 +- nodebuilder/module.go | 6 +- nodebuilder/state/core.go | 19 ++- nodebuilder/state/keyring.go | 22 +-- nodebuilder/state/module.go | 6 +- nodebuilder/state/opts.go | 14 +- nodebuilder/testing.go | 19 +-- nodebuilder/tests/blob_test.go | 2 - nodebuilder/tests/swamp/swamp.go | 5 +- state/core_access.go | 244 ++++++++++++++++++------------- state/core_access_test.go | 7 +- state/integration_test.go | 7 +- 12 files changed, 203 insertions(+), 159 deletions(-) diff --git a/blob/service_test.go b/blob/service_test.go index 46c8794682..7a99e92e06 100644 --- a/blob/service_test.go +++ b/blob/service_test.go @@ -9,15 +9,16 @@ import ( "testing" "time" - "github.com/celestiaorg/celestia-app/pkg/appconsts" - "github.com/celestiaorg/celestia-app/pkg/shares" - "github.com/celestiaorg/go-header/store" ds "github.com/ipfs/go-datastore" ds_sync "github.com/ipfs/go-datastore/sync" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/pkg/shares" + "github.com/celestiaorg/go-header/store" + "github.com/celestiaorg/celestia-node/blob/blobtest" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/header/headertest" @@ -425,8 +426,8 @@ func TestService_Get(t *testing.T) { } // TestService_GetAllWithoutPadding it retrieves all blobs under the given namespace: -// the amount of the blobs is known and equal to 5. Then it ensures that each blob has a correct index inside the eds -// by requesting share and comparing them. +// the amount of the blobs is known and equal to 5. Then it ensures that each blob has a correct +// index inside the eds by requesting share and comparing them. func TestService_GetAllWithoutPadding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) t.Cleanup(cancel) diff --git a/nodebuilder/module.go b/nodebuilder/module.go index 916dccf34d..e8614a7a18 100644 --- a/nodebuilder/module.go +++ b/nodebuilder/module.go @@ -28,14 +28,11 @@ func ConstructModule(tp node.Type, network p2p.Network, cfg *Config, store Store if err != nil { return fx.Error(err) } - signer, err := state.KeyringSigner(cfg.State, ks, network) - if err != nil { - return fx.Error(err) - } baseComponents := fx.Options( fx.Supply(tp), fx.Supply(network), + fx.Supply(ks), fx.Provide(p2p.BootstrappersFor), fx.Provide(func(lc fx.Lifecycle) context.Context { return fxutil.WithLifecycle(context.Background(), lc) @@ -45,7 +42,6 @@ func ConstructModule(tp node.Type, network p2p.Network, cfg *Config, store Store fx.Provide(store.Datastore), fx.Provide(store.Keystore), fx.Supply(node.StorePath(store.Path())), - fx.Supply(signer), // modules provided by the node p2p.ConstructModule(tp, &cfg.P2P), state.ConstructModule(tp, &cfg.State, &cfg.Core), diff --git a/nodebuilder/state/core.go b/nodebuilder/state/core.go index f8f8508540..8121358d2a 100644 --- a/nodebuilder/state/core.go +++ b/nodebuilder/state/core.go @@ -1,7 +1,8 @@ package state import ( - apptypes "github.com/celestiaorg/celestia-app/x/blob/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + libfraud "github.com/celestiaorg/go-fraud" "github.com/celestiaorg/go-header/sync" @@ -16,15 +17,23 @@ import ( // a celestia-core connection. func coreAccessor( corecfg core.Config, - signer *apptypes.KeyringSigner, + keyring keyring.Keyring, + keyname AccountName, sync *sync.Syncer[*header.ExtendedHeader], fraudServ libfraud.Service[*header.ExtendedHeader], -) (*state.CoreAccessor, Module, *modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader]) { - ca := state.NewCoreAccessor(signer, sync, corecfg.IP, corecfg.RPCPort, corecfg.GRPCPort) +) ( + *state.CoreAccessor, + Module, + *modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader], + error, +) { + ca, err := state.NewCoreAccessor(keyring, string(keyname), sync, corecfg.IP, corecfg.RPCPort, corecfg.GRPCPort) - return ca, ca, &modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader]{ + sBreaker := &modfraud.ServiceBreaker[*state.CoreAccessor, *header.ExtendedHeader]{ Service: ca, FraudType: byzantine.BadEncoding, FraudServ: fraudServ, } + + return ca, ca, sBreaker, err } diff --git a/nodebuilder/state/keyring.go b/nodebuilder/state/keyring.go index 5aeaff69e2..1b65d27efe 100644 --- a/nodebuilder/state/keyring.go +++ b/nodebuilder/state/keyring.go @@ -3,18 +3,17 @@ package state import ( kr "github.com/cosmos/cosmos-sdk/crypto/keyring" - apptypes "github.com/celestiaorg/celestia-app/x/blob/types" - "github.com/celestiaorg/celestia-node/libs/keystore" - "github.com/celestiaorg/celestia-node/nodebuilder/p2p" ) const DefaultAccountName = "my_celes_key" -// KeyringSigner constructs a new keyring signer. -// NOTE: we construct keyring signer before constructing node for easier UX +type AccountName string + +// Keyring constructs a new keyring. +// NOTE: we construct keyring before constructing node for easier UX // as having keyring-backend set to `file` prompts user for password. -func KeyringSigner(cfg Config, ks keystore.Keystore, net p2p.Network) (*apptypes.KeyringSigner, error) { +func Keyring(cfg Config, ks keystore.Keystore) (kr.Keyring, AccountName, error) { ring := ks.Keyring() var info *kr.Record // if custom keyringAccName provided, find key for that name @@ -22,7 +21,7 @@ func KeyringSigner(cfg Config, ks keystore.Keystore, net p2p.Network) (*apptypes keyInfo, err := ring.Key(cfg.KeyringAccName) if err != nil { log.Errorw("failed to find key by given name", "keyring.accname", cfg.KeyringAccName) - return nil, err + return nil, "", err } info = keyInfo } else { @@ -30,15 +29,10 @@ func KeyringSigner(cfg Config, ks keystore.Keystore, net p2p.Network) (*apptypes keyInfo, err := ring.Key(DefaultAccountName) if err != nil { log.Errorw("could not access key in keyring", "name", DefaultAccountName) - return nil, err + return nil, "", err } info = keyInfo } - // construct signer using the default key found / generated above - signer := apptypes.NewKeyringSigner(ring, info.Name, string(net)) - signerInfo := signer.GetSignerInfo() - log.Infow("constructed keyring signer", "backend", cfg.KeyringBackend, "path", ks.Path(), - "key name", signerInfo.Name, "chain-id", string(net)) - return signer, nil + return ring, AccountName(info.Name), nil } diff --git a/nodebuilder/state/module.go b/nodebuilder/state/module.go index 3f136ab764..0e80ab3209 100644 --- a/nodebuilder/state/module.go +++ b/nodebuilder/state/module.go @@ -3,11 +3,13 @@ package state import ( "context" + "github.com/cosmos/cosmos-sdk/crypto/keyring" logging "github.com/ipfs/go-log/v2" "go.uber.org/fx" "github.com/celestiaorg/celestia-node/header" "github.com/celestiaorg/celestia-node/libs/fxutil" + "github.com/celestiaorg/celestia-node/libs/keystore" "github.com/celestiaorg/celestia-node/nodebuilder/core" modfraud "github.com/celestiaorg/celestia-node/nodebuilder/fraud" "github.com/celestiaorg/celestia-node/nodebuilder/node" @@ -21,10 +23,12 @@ var log = logging.Logger("module/state") func ConstructModule(tp node.Type, cfg *Config, coreCfg *core.Config) fx.Option { // sanitize config values before constructing module cfgErr := cfg.Validate() - baseComponents := fx.Options( fx.Supply(*cfg), fx.Error(cfgErr), + fx.Provide(func(ks keystore.Keystore) (keyring.Keyring, AccountName, error) { + return Keyring(*cfg, ks) + }), fxutil.ProvideIf(coreCfg.IsEndpointConfigured(), fx.Annotate( coreAccessor, fx.OnStart(func(ctx context.Context, diff --git a/nodebuilder/state/opts.go b/nodebuilder/state/opts.go index 0b357b8396..cc3ee537c1 100644 --- a/nodebuilder/state/opts.go +++ b/nodebuilder/state/opts.go @@ -1,13 +1,19 @@ package state import ( + kr "github.com/cosmos/cosmos-sdk/crypto/keyring" "go.uber.org/fx" - "github.com/celestiaorg/celestia-app/x/blob/types" + "github.com/celestiaorg/celestia-node/libs/fxutil" ) -// WithKeyringSigner overrides the default keyring signer constructed +// WithKeyring overrides the default keyring constructed // by the node. -func WithKeyringSigner(signer *types.KeyringSigner) fx.Option { - return fx.Replace(signer) +func WithKeyring(keyring kr.Keyring) fx.Option { + return fxutil.ReplaceAs(keyring, new(kr.Keyring)) +} + +// WithKeyName configures the signer to use the given key. +func WithKeyName(name AccountName) fx.Option { + return fx.Replace(name) } diff --git a/nodebuilder/testing.go b/nodebuilder/testing.go index c4b3ed4bde..a4cfc94e32 100644 --- a/nodebuilder/testing.go +++ b/nodebuilder/testing.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/fx" - apptypes "github.com/celestiaorg/celestia-app/x/blob/types" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/core" @@ -17,9 +16,10 @@ import ( "github.com/celestiaorg/celestia-node/libs/fxutil" "github.com/celestiaorg/celestia-node/nodebuilder/node" "github.com/celestiaorg/celestia-node/nodebuilder/p2p" - "github.com/celestiaorg/celestia-node/nodebuilder/state" ) +const TestKeyringName = "test_celes" + // MockStore provides mock in memory Store for testing purposes. func MockStore(t *testing.T, cfg *Config) Store { t.Helper() @@ -48,10 +48,13 @@ func TestNodeWithConfig(t *testing.T, tp node.Type, cfg *Config, opts ...fx.Opti store := MockStore(t, cfg) ks, err := store.Keystore() require.NoError(t, err) + kr := ks.Keyring() + // create a key in the keystore to be used by the core accessor + _, _, err = kr.NewMnemonic(TestKeyringName, keyring.English, "", "", hd.Secp256k1) + require.NoError(t, err) + cfg.State.KeyringAccName = TestKeyringName opts = append(opts, - // avoid writing keyring on disk - state.WithKeyringSigner(TestKeyringSigner(t, ks.Keyring())), // temp dir for the eds store FIXME: Should be in mem fx.Replace(node.StorePath(t.TempDir())), // avoid requesting trustedPeer during initialization @@ -71,11 +74,3 @@ func TestNodeWithConfig(t *testing.T, tp node.Type, cfg *Config, opts ...fx.Opti require.NoError(t, err) return nd } - -func TestKeyringSigner(t *testing.T, ring keyring.Keyring) *apptypes.KeyringSigner { - signer := apptypes.NewKeyringSigner(ring, "", string(p2p.Private)) - _, _, err := signer.NewMnemonic("my_celes_key", keyring.English, "", - "", hd.Secp256k1) - require.NoError(t, err) - return signer -} diff --git a/nodebuilder/tests/blob_test.go b/nodebuilder/tests/blob_test.go index cbd29de920..60af146f67 100644 --- a/nodebuilder/tests/blob_test.go +++ b/nodebuilder/tests/blob_test.go @@ -37,10 +37,8 @@ func TestBlobModule(t *testing.T) { blobs = append(blobs, blob) } - require.NoError(t, err) bridge := sw.NewBridgeNode() require.NoError(t, bridge.Start(ctx)) - addrs, err := peer.AddrInfoToP2pAddrs(host.InfoFromHost(bridge.Host)) require.NoError(t, err) diff --git a/nodebuilder/tests/swamp/swamp.go b/nodebuilder/tests/swamp/swamp.go index 9faf69744d..93879c4425 100644 --- a/nodebuilder/tests/swamp/swamp.go +++ b/nodebuilder/tests/swamp/swamp.go @@ -22,7 +22,6 @@ import ( "golang.org/x/exp/maps" "github.com/celestiaorg/celestia-app/test/util/testnode" - apptypes "github.com/celestiaorg/celestia-app/x/blob/types" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/core" @@ -258,9 +257,9 @@ func (s *Swamp) NewNodeWithStore( store nodebuilder.Store, options ...fx.Option, ) *nodebuilder.Node { - signer := apptypes.NewKeyringSigner(s.ClientContext.Keyring, s.Accounts[0], s.ClientContext.ChainID) options = append(options, - state.WithKeyringSigner(signer), + state.WithKeyring(s.ClientContext.Keyring), + state.WithKeyName(state.AccountName(s.Accounts[0])), ) switch tp { diff --git a/state/core_access.go b/state/core_access.go index 7a8e369b9a..a9e0e9c5b5 100644 --- a/state/core_access.go +++ b/state/core_access.go @@ -11,6 +11,7 @@ import ( sdkErrors "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/api/tendermint/abci" nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + "github.com/cosmos/cosmos-sdk/crypto/keyring" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdktypes "github.com/cosmos/cosmos-sdk/types" sdktx "github.com/cosmos/cosmos-sdk/types/tx" @@ -25,9 +26,10 @@ import ( "google.golang.org/grpc/credentials/insecure" "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" apperrors "github.com/celestiaorg/celestia-app/app/errors" "github.com/celestiaorg/celestia-app/pkg/appconsts" - appblob "github.com/celestiaorg/celestia-app/x/blob" + "github.com/celestiaorg/celestia-app/pkg/user" apptypes "github.com/celestiaorg/celestia-app/x/blob/types" libhead "github.com/celestiaorg/go-header" @@ -35,12 +37,17 @@ import ( "github.com/celestiaorg/celestia-node/header" ) +const ( + // gasMultiplier is used to increase gas limit in case if tx has additional options. + gasMultiplier = 1.1 + maxRetries = 5 +) + var ( - log = logging.Logger("state") ErrInvalidAmount = errors.New("state: amount must be greater than zero") -) -const maxRetries = 5 + log = logging.Logger("state") +) // CoreAccessor implements service over a gRPC connection // with a celestia-core node. @@ -48,7 +55,14 @@ type CoreAccessor struct { ctx context.Context cancel context.CancelFunc - signer *apptypes.KeyringSigner + keyring keyring.Keyring + addr sdktypes.AccAddress + // TODO: (@cmwaters) - once multiple keys within a signer is supported, + // this will no longer be necessary. + // ref: https://github.com/celestiaorg/celestia-app/issues/3259 + signerMu sync.Mutex + signer *user.Signer + getter libhead.Head[*header.ExtendedHeader] stakingCli stakingtypes.QueryClient @@ -76,24 +90,36 @@ type CoreAccessor struct { // constructs and returns a new CoreAccessor (state service) with the active // connection. func NewCoreAccessor( - signer *apptypes.KeyringSigner, + keyring keyring.Keyring, + keyname string, getter libhead.Head[*header.ExtendedHeader], coreIP, rpcPort string, grpcPort string, -) *CoreAccessor { +) (*CoreAccessor, error) { // create verifier prt := merkle.DefaultProofRuntime() prt.RegisterOpDecoder(storetypes.ProofOpIAVLCommitment, storetypes.CommitmentOpDecoder) prt.RegisterOpDecoder(storetypes.ProofOpSimpleMerkleCommitment, storetypes.CommitmentOpDecoder) + + record, err := keyring.Key(keyname) + if err != nil { + return nil, fmt.Errorf("getting key %s: %w", keyname, err) + } + addr, err := record.GetAddress() + if err != nil { + return nil, fmt.Errorf("getting address for key %s: %w", keyname, err) + } + return &CoreAccessor{ - signer: signer, + keyring: keyring, + addr: addr, getter: getter, coreIP: coreIP, rpcPort: rpcPort, grpcPort: grpcPort, prt: prt, - } + }, nil } func (ca *CoreAccessor) Start(ctx context.Context) error { @@ -124,6 +150,11 @@ func (ca *CoreAccessor) Start(ctx context.Context) error { } ca.rpcCli = cli + ca.signer, err = ca.setupSigner(ctx) + if err != nil { + log.Warnw("failed to set up signer, check if node's account is funded", "err", err) + } + ca.minGasPrice, err = ca.queryMinimumGasPrice(ctx) if err != nil { return fmt.Errorf("querying minimum gas price: %w", err) @@ -158,24 +189,6 @@ func (ca *CoreAccessor) cancelCtx() { ca.cancel = nil } -func (ca *CoreAccessor) constructSignedTx( - ctx context.Context, - msg sdktypes.Msg, - opts ...apptypes.TxBuilderOption, -) ([]byte, error) { - // should be called first in order to make a valid tx - err := ca.signer.QueryAccountNumber(ctx, ca.coreConn) - if err != nil { - return nil, err - } - - tx, err := ca.signer.BuildSignedTx(ca.signer.NewTxBuilder(opts...), msg) - if err != nil { - return nil, err - } - return ca.signer.EncodeTx(tx) -} - // SubmitPayForBlob builds, signs, and synchronously submits a MsgPayForBlob. It blocks until the // transaction is committed and returns the TxResponse. If gasLim is set to 0, the method will // automatically estimate the gas limit. If the fee is negative, the method will use the nodes min @@ -186,6 +199,11 @@ func (ca *CoreAccessor) SubmitPayForBlob( gasLim uint64, blobs []*blob.Blob, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if len(blobs) == 0 { return nil, errors.New("state: no blobs provided") } @@ -223,13 +241,10 @@ func (ca *CoreAccessor) SubmitPayForBlob( var lastErr error for attempt := 0; attempt < maxRetries; attempt++ { - response, err := appblob.SubmitPayForBlob( + response, err := signer.SubmitPayForBlob( ctx, - ca.signer, - ca.coreConn, - sdktx.BroadcastMode_BROADCAST_MODE_BLOCK, appblobs, - apptypes.SetGasLimit(gasLim), + user.SetGasLimit(gasLim), withFee(fee), ) @@ -262,19 +277,11 @@ func (ca *CoreAccessor) SubmitPayForBlob( } func (ca *CoreAccessor) AccountAddress(context.Context) (Address, error) { - addr, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return Address{nil}, err - } - return Address{addr}, nil + return Address{ca.addr}, nil } func (ca *CoreAccessor) Balance(ctx context.Context) (*Balance, error) { - addr, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } - return ca.BalanceForAddress(ctx, Address{addr}) + return ca.BalanceForAddress(ctx, Address{ca.addr}) } func (ca *CoreAccessor) BalanceForAddress(ctx context.Context, addr Address) (*Balance, error) { @@ -365,21 +372,26 @@ func (ca *CoreAccessor) Transfer( fee Int, gasLim uint64, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if amount.IsNil() || amount.Int64() <= 0 { return nil, ErrInvalidAmount } - from, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } coins := sdktypes.NewCoins(sdktypes.NewCoin(app.BondDenom, amount)) - msg := banktypes.NewMsgSend(from, addr, coins) - signedTx, err := ca.constructSignedTx(ctx, msg, apptypes.SetGasLimit(gasLim), withFee(fee)) - if err != nil { - return nil, err + msg := banktypes.NewMsgSend(signer.Address(), addr, coins) + if gasLim == 0 { + var err error + gasLim, err = signer.EstimateGas(ctx, []sdktypes.Msg{msg}) + if err != nil { + return nil, fmt.Errorf("estimating gas: %w", err) + } } - return ca.SubmitTx(ctx, signedTx) + + return signer.SubmitTx(ctx, []sdktypes.Msg{msg}, user.SetGasLimit(gasLim), user.SetFee(fee.Uint64())) } func (ca *CoreAccessor) CancelUnbondingDelegation( @@ -390,21 +402,26 @@ func (ca *CoreAccessor) CancelUnbondingDelegation( fee Int, gasLim uint64, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if amount.IsNil() || amount.Int64() <= 0 { return nil, ErrInvalidAmount } - from, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } coins := sdktypes.NewCoin(app.BondDenom, amount) - msg := stakingtypes.NewMsgCancelUnbondingDelegation(from, valAddr, height.Int64(), coins) - signedTx, err := ca.constructSignedTx(ctx, msg, apptypes.SetGasLimit(gasLim), withFee(fee)) - if err != nil { - return nil, err + msg := stakingtypes.NewMsgCancelUnbondingDelegation(signer.Address(), valAddr, height.Int64(), coins) + if gasLim == 0 { + var err error + gasLim, err = signer.EstimateGas(ctx, []sdktypes.Msg{msg}) + if err != nil { + return nil, fmt.Errorf("estimating gas: %w", err) + } } - return ca.SubmitTx(ctx, signedTx) + + return signer.SubmitTx(ctx, []sdktypes.Msg{msg}, user.SetGasLimit(gasLim), user.SetFee(fee.Uint64())) } func (ca *CoreAccessor) BeginRedelegate( @@ -415,21 +432,26 @@ func (ca *CoreAccessor) BeginRedelegate( fee Int, gasLim uint64, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if amount.IsNil() || amount.Int64() <= 0 { return nil, ErrInvalidAmount } - from, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } coins := sdktypes.NewCoin(app.BondDenom, amount) - msg := stakingtypes.NewMsgBeginRedelegate(from, srcValAddr, dstValAddr, coins) - signedTx, err := ca.constructSignedTx(ctx, msg, apptypes.SetGasLimit(gasLim), withFee(fee)) - if err != nil { - return nil, err + msg := stakingtypes.NewMsgBeginRedelegate(signer.Address(), srcValAddr, dstValAddr, coins) + if gasLim == 0 { + var err error + gasLim, err = signer.EstimateGas(ctx, []sdktypes.Msg{msg}) + if err != nil { + return nil, fmt.Errorf("estimating gas: %w", err) + } } - return ca.SubmitTx(ctx, signedTx) + + return signer.SubmitTx(ctx, []sdktypes.Msg{msg}, user.SetGasLimit(gasLim), user.SetFee(fee.Uint64())) } func (ca *CoreAccessor) Undelegate( @@ -439,21 +461,25 @@ func (ca *CoreAccessor) Undelegate( fee Int, gasLim uint64, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if amount.IsNil() || amount.Int64() <= 0 { return nil, ErrInvalidAmount } - from, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } coins := sdktypes.NewCoin(app.BondDenom, amount) - msg := stakingtypes.NewMsgUndelegate(from, delAddr, coins) - signedTx, err := ca.constructSignedTx(ctx, msg, apptypes.SetGasLimit(gasLim), withFee(fee)) - if err != nil { - return nil, err + msg := stakingtypes.NewMsgUndelegate(signer.Address(), delAddr, coins) + if gasLim == 0 { + var err error + gasLim, err = signer.EstimateGas(ctx, []sdktypes.Msg{msg}) + if err != nil { + return nil, fmt.Errorf("estimating gas: %w", err) + } } - return ca.SubmitTx(ctx, signedTx) + return signer.SubmitTx(ctx, []sdktypes.Msg{msg}, user.SetGasLimit(gasLim), user.SetFee(fee.Uint64())) } func (ca *CoreAccessor) Delegate( @@ -463,31 +489,32 @@ func (ca *CoreAccessor) Delegate( fee Int, gasLim uint64, ) (*TxResponse, error) { + signer, err := ca.getSigner(ctx) + if err != nil { + return nil, err + } + if amount.IsNil() || amount.Int64() <= 0 { return nil, ErrInvalidAmount } - from, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } coins := sdktypes.NewCoin(app.BondDenom, amount) - msg := stakingtypes.NewMsgDelegate(from, delAddr, coins) - signedTx, err := ca.constructSignedTx(ctx, msg, apptypes.SetGasLimit(gasLim), withFee(fee)) - if err != nil { - return nil, err + msg := stakingtypes.NewMsgDelegate(signer.Address(), delAddr, coins) + if gasLim == 0 { + var err error + gasLim, err = signer.EstimateGas(ctx, []sdktypes.Msg{msg}) + if err != nil { + return nil, fmt.Errorf("estimating gas: %w", err) + } } - return ca.SubmitTx(ctx, signedTx) + return signer.SubmitTx(ctx, []sdktypes.Msg{msg}, user.SetGasLimit(gasLim), user.SetFee(fee.Uint64())) } func (ca *CoreAccessor) QueryDelegation( ctx context.Context, valAddr ValAddress, ) (*stakingtypes.QueryDelegationResponse, error) { - delAddr, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } + delAddr := ca.addr return ca.stakingCli.Delegation(ctx, &stakingtypes.QueryDelegationRequest{ DelegatorAddr: delAddr.String(), ValidatorAddr: valAddr.String(), @@ -498,10 +525,7 @@ func (ca *CoreAccessor) QueryUnbonding( ctx context.Context, valAddr ValAddress, ) (*stakingtypes.QueryUnbondingDelegationResponse, error) { - delAddr, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } + delAddr := ca.addr return ca.stakingCli.UnbondingDelegation(ctx, &stakingtypes.QueryUnbondingDelegationRequest{ DelegatorAddr: delAddr.String(), ValidatorAddr: valAddr.String(), @@ -513,10 +537,7 @@ func (ca *CoreAccessor) QueryRedelegations( srcValAddr, dstValAddr ValAddress, ) (*stakingtypes.QueryRedelegationsResponse, error) { - delAddr, err := ca.signer.GetSignerInfo().GetAddress() - if err != nil { - return nil, err - } + delAddr := ca.addr return ca.stakingCli.Redelegations(ctx, &stakingtypes.QueryRedelegationsRequest{ DelegatorAddr: delAddr.String(), SrcValidatorAddr: srcValAddr.String(), @@ -571,7 +592,28 @@ func (ca *CoreAccessor) queryMinimumGasPrice( return coins.AmountOf(app.BondDenom).MustFloat64(), nil } -func withFee(fee Int) apptypes.TxBuilderOption { +// getSigner returns the signer if it has already been constructed, otherwise +// it will attempt to set it up. The signer can only be constructed if the account +// exists / is funded. +func (ca *CoreAccessor) getSigner(ctx context.Context) (*user.Signer, error) { + ca.signerMu.Lock() + defer ca.signerMu.Unlock() + + if ca.signer != nil { + return ca.signer, nil + } + + var err error + ca.signer, err = ca.setupSigner(ctx) + return ca.signer, err +} + +func (ca *CoreAccessor) setupSigner(ctx context.Context) (*user.Signer, error) { + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + return user.SetupSigner(ctx, ca.keyring, ca.coreConn, ca.addr, encCfg) +} + +func withFee(fee Int) user.TxOption { gasFee := sdktypes.NewCoins(sdktypes.NewCoin(app.BondDenom, fee)) - return apptypes.SetFeeAmount(gasFee) + return user.SetFeeAmount(gasFee) } diff --git a/state/core_access_test.go b/state/core_access_test.go index d6697ce6c6..0f9b0f5318 100644 --- a/state/core_access_test.go +++ b/state/core_access_test.go @@ -36,10 +36,11 @@ func TestSubmitPayForBlob(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - signer := blobtypes.NewKeyringSigner(cctx.Keyring, accounts[0], cctx.ChainID) - ca := NewCoreAccessor(signer, nil, "127.0.0.1", extractPort(rpcAddr), extractPort(grpcAddr)) + ca, err := NewCoreAccessor(cctx.Keyring, accounts[0], nil, "127.0.0.1", extractPort(rpcAddr), + extractPort(grpcAddr)) + require.NoError(t, err) // start the accessor - err := ca.Start(ctx) + err = ca.Start(ctx) require.NoError(t, err) t.Cleanup(func() { _ = ca.Stop(ctx) diff --git a/state/integration_test.go b/state/integration_test.go index 1aa342649c..6d2a38c8a7 100644 --- a/state/integration_test.go +++ b/state/integration_test.go @@ -18,7 +18,6 @@ import ( "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/test/util/testfactory" "github.com/celestiaorg/celestia-app/test/util/testnode" - blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" libhead "github.com/celestiaorg/go-header" "github.com/celestiaorg/celestia-node/core" @@ -49,13 +48,13 @@ func (s *IntegrationTestSuite) SetupSuite() { s.cctx = core.StartTestNodeWithConfig(s.T(), cfg) s.accounts = cfg.Accounts - signer := blobtypes.NewKeyringSigner(s.cctx.Keyring, s.accounts[0], s.cctx.ChainID) - accessor := NewCoreAccessor(signer, localHeader{s.cctx.Client}, "", "", "") + accessor, err := NewCoreAccessor(s.cctx.Keyring, s.accounts[0], localHeader{s.cctx.Client}, "", "", "") + require.NoError(s.T(), err) setClients(accessor, s.cctx.GRPCClient, s.cctx.Client) s.accessor = accessor // required to ensure the Head request is non-nil - _, err := s.cctx.WaitForHeight(3) + _, err = s.cctx.WaitForHeight(3) require.NoError(s.T(), err) }