From 39cc28dd0883b339935daba972069dce168fddae Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 14:06:05 -0700 Subject: [PATCH 01/13] fix: end exclusive parse namespace end share range (backport #2208) (#3491) This is an automatic backport of pull request #2208 done by [Mergify](https://mergify.com). Motivated by this [Slack thread](https://celestia-team.slack.com/archives/C03MP6DPLAD/p1715891011859219). It seems like we forgot to backport this fix. cc: @rach-id --------- Co-authored-by: CHAMI Rachid Co-authored-by: Rootul Patel --- pkg/proof/proof_test.go | 32 ++++++++++++++++++++++++++++++++ pkg/proof/querier.go | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/proof/proof_test.go b/pkg/proof/proof_test.go index 405c630929..a8d8ae0756 100644 --- a/pkg/proof/proof_test.go +++ b/pkg/proof/proof_test.go @@ -242,3 +242,35 @@ func TestQueryTxInclusionProofRejectsNegativeValues(t *testing.T) { t.Fatal("no rawProof expected") } } + +// TestAllSharesInclusionProof creates a proof for all shares in the data +// square. Since we can't prove multiple namespaces at the moment, all the +// shares use the same namespace. +func TestAllSharesInclusionProof(t *testing.T) { + txs := testfactory.GenerateRandomTxs(243, 500) + + dataSquare, err := square.Construct(txs.ToSliceOfBytes(), appconsts.LatestVersion, 128) + require.NoError(t, err) + assert.Equal(t, 256, len(dataSquare)) + + // erasure the data square which we use to create the data root. + eds, err := da.ExtendShares(shares.ToBytes(dataSquare)) + require.NoError(t, err) + + // create the new data root by creating the data availability header (merkle + // roots of each row and col of the erasure data). + dah, err := da.NewDataAvailabilityHeader(eds) + require.NoError(t, err) + dataRoot := dah.Hash() + + actualNamespace, err := proof.ParseNamespace(dataSquare, 0, 256) + require.NoError(t, err) + require.Equal(t, appns.TxNamespace, actualNamespace) + proof, err := proof.NewShareInclusionProof( + dataSquare, + appns.TxNamespace, + shares.NewRange(0, 256), + ) + require.NoError(t, err) + assert.NoError(t, proof.Validate(dataRoot)) +} diff --git a/pkg/proof/querier.go b/pkg/proof/querier.go index 7bca88f114..9fca6c5afa 100644 --- a/pkg/proof/querier.go +++ b/pkg/proof/querier.go @@ -136,7 +136,7 @@ func ParseNamespace(rawShares []shares.Share, startShare, endShare int) (appns.N return appns.Namespace{}, fmt.Errorf("end share %d cannot be lower than starting share %d", endShare, startShare) } - if endShare >= len(rawShares) { + if endShare > len(rawShares) { return appns.Namespace{}, fmt.Errorf("end share %d is higher than block shares %d", endShare, len(rawShares)) } From 6157752003d7a1971ddf966de5d88b59c966fdac Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 29 May 2024 21:29:46 +0200 Subject: [PATCH 02/13] feat: add support for multiple accounts (backport) (#3504) --- Makefile | 2 +- pkg/user/account.go | 79 +++ pkg/user/legacy_signer.go | 615 ++++++++++++++++++ .../{signer_test.go => legacy_signer_test.go} | 0 pkg/user/signer.go | 603 ++++------------- pkg/user/tx_client.go | 463 +++++++++++++ pkg/user/tx_client_test.go | 168 +++++ 7 files changed, 1467 insertions(+), 463 deletions(-) create mode 100644 pkg/user/account.go create mode 100644 pkg/user/legacy_signer.go rename pkg/user/{signer_test.go => legacy_signer_test.go} (100%) create mode 100644 pkg/user/tx_client.go create mode 100644 pkg/user/tx_client_test.go diff --git a/Makefile b/Makefile index f4055e9f0a..e26233681a 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ test-short: ## test-race: Run unit tests in race mode. test-race: @echo "--> Running tests in race mode" - @go test ./... -v -race -skip "TestPrepareProposalConsistency|TestIntegrationTestSuite|TestQGBRPCQueries|TestSquareSizeIntegrationTest|TestStandardSDKIntegrationTestSuite|TestTxsimCommandFlags|TestTxsimCommandEnvVar|TestMintIntegrationTestSuite|TestQGBCLI|TestUpgrade|TestMaliciousTestNode|TestMaxTotalBlobSizeSuite|TestQGBIntegrationSuite|TestSignerTestSuite|TestPriorityTestSuite|TestTimeInPrepareProposalContext|TestConcurrentTxSubmission" + @go test ./... -v -race -skip "TestPrepareProposalConsistency|TestIntegrationTestSuite|TestQGBRPCQueries|TestSquareSizeIntegrationTest|TestStandardSDKIntegrationTestSuite|TestTxsimCommandFlags|TestTxsimCommandEnvVar|TestMintIntegrationTestSuite|TestQGBCLI|TestUpgrade|TestMaliciousTestNode|TestMaxTotalBlobSizeSuite|TestQGBIntegrationSuite|TestSignerTestSuite|TestPriorityTestSuite|TestTimeInPrepareProposalContext|TestConcurrentTxSubmission|TestTxClientTestSuite" .PHONY: test-race ## test-bench: Run unit tests in bench mode. diff --git a/pkg/user/account.go b/pkg/user/account.go new file mode 100644 index 0000000000..8c52d195a0 --- /dev/null +++ b/pkg/user/account.go @@ -0,0 +1,79 @@ +package user + +import ( + "context" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "google.golang.org/grpc" +) + +type Account struct { + name string + address types.AccAddress + pubKey cryptotypes.PubKey + accountNumber uint64 + + // the signers local view of the sequence number + sequence uint64 +} + +func NewAccount(keyName string, accountNumber, sequenceNumber uint64) *Account { + return &Account{ + name: keyName, + accountNumber: accountNumber, + sequence: sequenceNumber, + } +} + +func (a Account) Name() string { + return a.name +} + +func (a Account) Address() types.AccAddress { + return a.address +} + +func (a Account) PubKey() cryptotypes.PubKey { + return a.pubKey +} + +// Sequence returns the sequence number of the account. +// This is locally tracked +func (a Account) Sequence() uint64 { + return a.sequence +} + +func (a *Account) Copy() *Account { + return &Account{ + name: a.name, + address: a.address, + pubKey: a.pubKey, + accountNumber: a.accountNumber, + sequence: a.sequence, + } +} + +// QueryAccountInfo fetches the account number and sequence number from the celestia-app node. +func QueryAccountInfo(ctx context.Context, conn *grpc.ClientConn, registry codectypes.InterfaceRegistry, address types.AccAddress) (accNum uint64, seqNum uint64, err error) { + qclient := authtypes.NewQueryClient(conn) + // TODO: ideally we add a way to prove that the accounts rather than simply trusting the full node we are connected with + resp, err := qclient.Account( + ctx, + &authtypes.QueryAccountRequest{Address: address.String()}, + ) + if err != nil { + return accNum, seqNum, err + } + + var acc authtypes.AccountI + err = registry.UnpackAny(resp.Account, &acc) + if err != nil { + return accNum, seqNum, err + } + + accNum, seqNum = acc.GetAccountNumber(), acc.GetSequence() + return accNum, seqNum, nil +} diff --git a/pkg/user/legacy_signer.go b/pkg/user/legacy_signer.go new file mode 100644 index 0000000000..618344dc5d --- /dev/null +++ b/pkg/user/legacy_signer.go @@ -0,0 +1,615 @@ +package user + +import ( + "context" + "errors" + "fmt" + "strings" + "sync" + "time" + + "github.com/celestiaorg/celestia-app/app/encoding" + apperrors "github.com/celestiaorg/celestia-app/app/errors" + blob "github.com/celestiaorg/celestia-app/x/blob/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + "google.golang.org/grpc" +) + +// Signer is an abstraction for building, signing, and broadcasting Celestia transactions +type Signer struct { + keys keyring.Keyring + address sdktypes.AccAddress + enc client.TxConfig + grpc *grpc.ClientConn + pk cryptotypes.PubKey + chainID string + accountNumber uint64 + + mtx sync.RWMutex + // how often to poll the network for confirmation of a transaction + pollTime time.Duration + // the signers local view of the sequence number + localSequence uint64 + // the chains last known sequence number + networkSequence uint64 + // lookup map of all pending and yet to be confirmed outbound transactions + outboundSequences map[uint64]struct{} + // a reverse map for confirming which sequence numbers have been committed + reverseTxHashSequenceMap map[string]uint64 +} + +// NewSigner returns a new signer using the provided keyring +func NewSigner( + keys keyring.Keyring, + conn *grpc.ClientConn, + address sdktypes.AccAddress, + enc client.TxConfig, + chainID string, + accountNumber uint64, + sequence uint64, +) (*Signer, error) { + // check that the address exists + record, err := keys.KeyByAddress(address) + if err != nil { + return nil, err + } + + pk, err := record.GetPubKey() + if err != nil { + return nil, err + } + + return &Signer{ + keys: keys, + address: address, + grpc: conn, + enc: enc, + pk: pk, + chainID: chainID, + accountNumber: accountNumber, + localSequence: sequence, + networkSequence: sequence, + pollTime: DefaultPollTime, + outboundSequences: make(map[uint64]struct{}), + reverseTxHashSequenceMap: make(map[string]uint64), + }, nil +} + +// SetupSingleSigner sets up a signer based on the provided keyring. The keyring +// must contain exactly one key. It extracts the address from the key and uses +// the grpc connection to populate the chainID, account number, and sequence +// number. +func SetupSingleSigner(ctx context.Context, keys keyring.Keyring, conn *grpc.ClientConn, encCfg encoding.Config) (*Signer, error) { + records, err := keys.List() + if err != nil { + return nil, err + } + + if len(records) != 1 { + return nil, errors.New("keyring must contain exactly one key") + } + + address, err := records[0].GetAddress() + if err != nil { + return nil, err + } + + return SetupSigner(ctx, keys, conn, address, encCfg) +} + +// SetupSigner uses the underlying grpc connection to populate the chainID, accountNumber and sequence number of the +// account. +func SetupSigner( + ctx context.Context, + keys keyring.Keyring, + conn *grpc.ClientConn, + address sdktypes.AccAddress, + encCfg encoding.Config, +) (*Signer, error) { + resp, err := tmservice.NewServiceClient(conn).GetLatestBlock(ctx, &tmservice.GetLatestBlockRequest{}) + if err != nil { + return nil, err + } + + chainID := resp.SdkBlock.Header.ChainID + accNum, seqNum, err := QueryAccount(ctx, conn, encCfg, address.String()) + if err != nil { + return nil, err + } + + return NewSigner(keys, conn, address, encCfg.TxConfig, chainID, accNum, seqNum) +} + +// SubmitTx forms a transaction from the provided messages, signs it, and submits it to the chain. TxOptions +// may be provided to set the fee and gas limit. +func (s *Signer) SubmitTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { + tx, err := s.CreateTx(msgs, opts...) + if err != nil { + return nil, err + } + + resp, err := s.BroadcastTx(ctx, tx) + if err != nil { + return resp, err + } + + return s.ConfirmTx(ctx, resp.TxHash) +} + +// SubmitPayForBlob forms a transaction from the provided blobs, signs it, and submits it to the chain. +// TxOptions may be provided to set the fee and gas limit. +func (s *Signer) SubmitPayForBlob(ctx context.Context, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + resp, err := s.broadcastPayForBlob(ctx, blobs, opts...) + if err != nil { + return resp, err + } + + return s.ConfirmTx(ctx, resp.TxHash) +} + +func (s *Signer) broadcastPayForBlob(ctx context.Context, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + txBytes, seqNum, err := s.createPayForBlobs(blobs, opts...) + if err != nil { + return nil, err + } + + return s.broadcastTx(ctx, txBytes, seqNum) +} + +// CreateTx forms a transaction from the provided messages and signs it. TxOptions may be optionally +// used to set the gas limit and fee. +func (s *Signer) CreateTx(msgs []sdktypes.Msg, opts ...TxOption) (authsigning.Tx, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + + return s.createTx(msgs, opts...) +} + +func (s *Signer) createTx(msgs []sdktypes.Msg, opts ...TxOption) (authsigning.Tx, error) { + txBuilder := s.txBuilder(opts...) + if err := txBuilder.SetMsgs(msgs...); err != nil { + return nil, err + } + + if err := s.signTransaction(txBuilder, s.getAndIncrementSequence()); err != nil { + return nil, err + } + + return txBuilder.GetTx(), nil +} + +func (s *Signer) CreatePayForBlob(blobs []*tmproto.Blob, opts ...TxOption) ([]byte, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + blobTx, _, err := s.createPayForBlobs(blobs, opts...) + return blobTx, err +} + +func (s *Signer) createPayForBlobs(blobs []*tmproto.Blob, opts ...TxOption) ([]byte, uint64, error) { + msg, err := blob.NewMsgPayForBlobs(s.address.String(), blobs...) + if err != nil { + return nil, 0, err + } + + tx, err := s.createTx([]sdktypes.Msg{msg}, opts...) + if err != nil { + return nil, 0, err + } + + seqNum, err := getSequenceNumber(tx) + if err != nil { + panic(err) + } + + txBytes, err := s.EncodeTx(tx) + if err != nil { + return nil, 0, err + } + + blobTx, err := tmtypes.MarshalBlobTx(txBytes, blobs...) + return blobTx, seqNum, err +} + +func (s *Signer) EncodeTx(tx sdktypes.Tx) ([]byte, error) { + return s.enc.TxEncoder()(tx) +} + +func (s *Signer) DecodeTx(txBytes []byte) (authsigning.Tx, error) { + tx, err := s.enc.TxDecoder()(txBytes) + if err != nil { + return nil, err + } + authTx, ok := tx.(authsigning.Tx) + if !ok { + return nil, errors.New("not an authsigning transaction") + } + return authTx, nil +} + +// BroadcastTx submits the provided transaction bytes to the chain and returns the response. +func (s *Signer) BroadcastTx(ctx context.Context, tx authsigning.Tx) (*sdktypes.TxResponse, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + txBytes, err := s.EncodeTx(tx) + if err != nil { + return nil, err + } + sequence, err := getSequenceNumber(tx) + if err != nil { + return nil, err + } + return s.broadcastTx(ctx, txBytes, sequence) +} + +// CONTRACT: assumes the caller has the lock +func (s *Signer) broadcastTx(ctx context.Context, txBytes []byte, sequence uint64) (*sdktypes.TxResponse, error) { + if _, exists := s.outboundSequences[sequence]; exists { + return s.retryBroadcastingTx(ctx, txBytes, sequence+1) + } + + if sequence < s.networkSequence { + s.localSequence = s.networkSequence + return s.retryBroadcastingTx(ctx, txBytes, s.localSequence) + } + + txClient := sdktx.NewServiceClient(s.grpc) + resp, err := txClient.BroadcastTx( + ctx, + &sdktx.BroadcastTxRequest{ + Mode: sdktx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, + ) + if err != nil { + return nil, err + } + if apperrors.IsNonceMismatchCode(resp.TxResponse.Code) { + // extract what the lastCommittedNonce on chain is + nextSequence, err := apperrors.ParseExpectedSequence(resp.TxResponse.RawLog) + if err != nil { + return nil, fmt.Errorf("parsing nonce mismatch upon retry: %w", err) + } + s.networkSequence = nextSequence + s.localSequence = nextSequence + // FIXME: We can't actually resign the transaction. A malicious node + // may manipulate us into signing the same transaction several times + // and then executing them. We need some proof of what the last network + // sequence is rather than relying on an error provided by the node + // return s.retryBroadcastingTx(ctx, txBytes, nextSequence) + // Ref: https://github.com/celestiaorg/celestia-app/issues/3256 + // return s.retryBroadcastingTx(ctx, txBytes, nextSequence) + } else if resp.TxResponse.Code == abci.CodeTypeOK { + s.outboundSequences[sequence] = struct{}{} + s.reverseTxHashSequenceMap[resp.TxResponse.TxHash] = sequence + return resp.TxResponse, nil + } + return resp.TxResponse, fmt.Errorf("tx failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) +} + +// retryBroadcastingTx creates a new transaction by copying over an existing transaction but creates a new signature with the +// new sequence number. It then calls `broadcastTx` and attempts to submit the transaction +func (s *Signer) retryBroadcastingTx(ctx context.Context, txBytes []byte, newSequenceNumber uint64) (*sdktypes.TxResponse, error) { + blobTx, isBlobTx := tmtypes.UnmarshalBlobTx(txBytes) + if isBlobTx { + txBytes = blobTx.Tx + } + tx, err := s.DecodeTx(txBytes) + if err != nil { + return nil, err + } + txBuilder := s.txBuilder() + if err := txBuilder.SetMsgs(tx.GetMsgs()...); err != nil { + return nil, err + } + if granter := tx.FeeGranter(); granter != nil { + txBuilder.SetFeeGranter(granter) + } + if payer := tx.FeePayer(); payer != nil { + txBuilder.SetFeePayer(payer) + } + if memo := tx.GetMemo(); memo != "" { + txBuilder.SetMemo(memo) + } + if fee := tx.GetFee(); fee != nil { + txBuilder.SetFeeAmount(fee) + } + if gas := tx.GetGas(); gas > 0 { + txBuilder.SetGasLimit(gas) + } + + if err := s.signTransaction(txBuilder, newSequenceNumber); err != nil { + return nil, fmt.Errorf("resigning transaction: %w", err) + } + + newTxBytes, err := s.EncodeTx(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + // rewrap the blob tx if it was originally a blob tx + if isBlobTx { + newTxBytes, err = tmtypes.MarshalBlobTx(newTxBytes, blobTx.Blobs...) + if err != nil { + return nil, err + } + } + + return s.broadcastTx(ctx, newTxBytes, newSequenceNumber) +} + +// ConfirmTx periodically pings the provided node for the commitment of a transaction by its +// hash. It will continually loop until the context is cancelled, the tx is found or an error +// is encountered. +func (s *Signer) ConfirmTx(ctx context.Context, txHash string) (*sdktypes.TxResponse, error) { + txClient := sdktx.NewServiceClient(s.grpc) + + pollTime := s.getPollTime() + timer := time.NewTimer(0) + defer timer.Stop() + + for { + select { + case <-ctx.Done(): + return &sdktypes.TxResponse{}, ctx.Err() + case <-timer.C: + resp, err := txClient.GetTx(ctx, &sdktx.GetTxRequest{Hash: txHash}) + if err == nil { + if resp.TxResponse.Code != 0 { + s.updateNetworkSequence(txHash, false) + return resp.TxResponse, fmt.Errorf("tx was included but failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) + } + s.updateNetworkSequence(txHash, true) + return resp.TxResponse, nil + } + // FIXME: this is a relatively brittle of working out whether to retry or not. The tx might be not found for other + // reasons. It may have been removed from the mempool at a later point. We should build an endpoint that gives the + // signer more information on the status of their transaction and then update the logic here + if !strings.Contains(err.Error(), "not found") { + return &sdktypes.TxResponse{}, err + } + + timer.Reset(pollTime) + } + } +} + +func (s *Signer) EstimateGas(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (uint64, error) { + txBuilder := s.txBuilder(opts...) + if err := txBuilder.SetMsgs(msgs...); err != nil { + return 0, err + } + + if err := s.signTransaction(txBuilder, s.LocalSequence()); err != nil { + return 0, err + } + + txBytes, err := s.enc.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return 0, err + } + + resp, err := sdktx.NewServiceClient(s.grpc).Simulate(ctx, &sdktx.SimulateRequest{ + TxBytes: txBytes, + }) + if err != nil { + return 0, err + } + + return resp.GasInfo.GasUsed, nil +} + +// ChainID returns the chain ID of the signer. +func (s *Signer) ChainID() string { + return s.chainID +} + +// AccountNumber returns the account number of the signer. +func (s *Signer) AccountNumber() uint64 { + return s.accountNumber +} + +// Address returns the address of the signer. +func (s *Signer) Address() sdktypes.AccAddress { + return s.address +} + +// SetPollTime sets how often the signer should poll for the confirmation of the transaction +func (s *Signer) SetPollTime(pollTime time.Duration) { + s.mtx.Lock() + defer s.mtx.Unlock() + s.pollTime = pollTime +} + +func (s *Signer) getPollTime() time.Duration { + s.mtx.Lock() + defer s.mtx.Unlock() + return s.pollTime +} + +// PubKey returns the public key of the signer +func (s *Signer) PubKey() cryptotypes.PubKey { + return s.pk +} + +// DEPRECATED: use Sequence instead +func (s *Signer) GetSequence() uint64 { + return s.getAndIncrementSequence() +} + +// LocalSequence returns the next sequence number of the signers +// locally saved +func (s *Signer) LocalSequence() uint64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.localSequence +} + +func (s *Signer) NetworkSequence() uint64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.networkSequence +} + +// getAndIncrementSequence gets the latest signed sequence and increments the +// local sequence number +func (s *Signer) getAndIncrementSequence() uint64 { + defer func() { s.localSequence++ }() + return s.localSequence +} + +// ForceSetSequence manually overrides the current local and network level +// sequence number. Be careful when invoking this as it may cause the +// transactions to reject the sequence if it doesn't match the one in state +func (s *Signer) ForceSetSequence(seq uint64) { + s.mtx.Lock() + defer s.mtx.Unlock() + s.localSequence = seq + s.networkSequence = seq +} + +// updateNetworkSequence is called once a transaction is confirmed +// and updates the chains last known sequence number +func (s *Signer) updateNetworkSequence(txHash string, success bool) { + s.mtx.Lock() + defer s.mtx.Unlock() + sequence, exists := s.reverseTxHashSequenceMap[txHash] + if !exists { + return + } + if success && sequence >= s.networkSequence { + s.networkSequence = sequence + 1 + } + delete(s.outboundSequences, sequence) + delete(s.reverseTxHashSequenceMap, txHash) +} + +// Keyring exposes the signers underlying keyring +func (s *Signer) Keyring() keyring.Keyring { + return s.keys +} + +func (s *Signer) signTransaction(builder client.TxBuilder, sequence uint64) error { + signers := builder.GetTx().GetSigners() + if len(signers) != 1 { + return fmt.Errorf("expected 1 signer, got %d", len(signers)) + } + + if !s.address.Equals(signers[0]) { + return fmt.Errorf("expected signer %s, got %s", s.address.String(), signers[0].String()) + } + + // To ensure we have the correct bytes to sign over we produce + // a dry run of the signing data + err := builder.SetSignatures(s.getSignatureV2(sequence, nil)) + if err != nil { + return fmt.Errorf("error setting draft signatures: %w", err) + } + + // now we can use the data to produce the signature from the signer + signature, err := s.createSignature(builder, sequence) + if err != nil { + return fmt.Errorf("error creating signature: %w", err) + } + + err = builder.SetSignatures(s.getSignatureV2(sequence, signature)) + if err != nil { + return fmt.Errorf("error setting signatures: %w", err) + } + + return nil +} + +func (s *Signer) createSignature(builder client.TxBuilder, sequence uint64) ([]byte, error) { + signerData := authsigning.SignerData{ + Address: s.address.String(), + ChainID: s.ChainID(), + AccountNumber: s.accountNumber, + Sequence: sequence, + PubKey: s.pk, + } + + bytesToSign, err := s.enc.SignModeHandler().GetSignBytes( + signing.SignMode_SIGN_MODE_DIRECT, + signerData, + builder.GetTx(), + ) + if err != nil { + return nil, fmt.Errorf("error getting sign bytes: %w", err) + } + + signature, _, err := s.keys.SignByAddress(s.address, bytesToSign) + if err != nil { + return nil, fmt.Errorf("error signing bytes: %w", err) + } + + return signature, nil +} + +// txBuilder returns the default sdk Tx builder using the celestia-app encoding config +func (s *Signer) txBuilder(opts ...TxOption) client.TxBuilder { + builder := s.enc.NewTxBuilder() + for _, opt := range opts { + builder = opt(builder) + } + return builder +} + +// QueryAccount fetches the account number and sequence number from the celestia-app node. +func QueryAccount(ctx context.Context, conn *grpc.ClientConn, encCfg encoding.Config, address string) (accNum uint64, seqNum uint64, err error) { + qclient := authtypes.NewQueryClient(conn) + resp, err := qclient.Account( + ctx, + &authtypes.QueryAccountRequest{Address: address}, + ) + if err != nil { + return accNum, seqNum, err + } + + var acc authtypes.AccountI + err = encCfg.InterfaceRegistry.UnpackAny(resp.Account, &acc) + if err != nil { + return accNum, seqNum, err + } + + accNum, seqNum = acc.GetAccountNumber(), acc.GetSequence() + return accNum, seqNum, nil +} + +func (s *Signer) getSignatureV2(sequence uint64, signature []byte) signing.SignatureV2 { + sigV2 := signing.SignatureV2{ + Data: &signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: signature, + }, + Sequence: sequence, + } + if sequence == 0 { + sigV2.PubKey = s.pk + } + return sigV2 +} + +func getSequenceNumber(tx authsigning.Tx) (uint64, error) { + sigs, err := tx.GetSignaturesV2() + if err != nil { + return 0, err + } + if len(sigs) > 1 { + return 0, fmt.Errorf("only a signle signature is supported, got %d", len(sigs)) + } + + return sigs[0].Sequence, nil +} diff --git a/pkg/user/signer_test.go b/pkg/user/legacy_signer_test.go similarity index 100% rename from pkg/user/signer_test.go rename to pkg/user/legacy_signer_test.go diff --git a/pkg/user/signer.go b/pkg/user/signer.go index 0f84556bed..1bc450a7bc 100644 --- a/pkg/user/signer.go +++ b/pkg/user/signer.go @@ -1,219 +1,105 @@ package user import ( - "context" "errors" "fmt" - "strings" - "sync" - "time" - "github.com/celestiaorg/celestia-app/app/encoding" - apperrors "github.com/celestiaorg/celestia-app/app/errors" - blob "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" "github.com/cosmos/cosmos-sdk/crypto/keyring" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdktypes "github.com/cosmos/cosmos-sdk/types" - sdktx "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" - "google.golang.org/grpc" -) -const DefaultPollTime = 3 * time.Second - -// Signer is an abstraction for building, signing, and broadcasting Celestia transactions -type Signer struct { - keys keyring.Keyring - address sdktypes.AccAddress - enc client.TxConfig - grpc *grpc.ClientConn - pk cryptotypes.PubKey - chainID string - accountNumber uint64 - - mtx sync.RWMutex - // how often to poll the network for confirmation of a transaction - pollTime time.Duration - // the signers local view of the sequence number - localSequence uint64 - // the chains last known sequence number - networkSequence uint64 - // lookup map of all pending and yet to be confirmed outbound transactions - outboundSequences map[uint64]struct{} - // a reverse map for confirming which sequence numbers have been committed - reverseTxHashSequenceMap map[string]uint64 -} + blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" +) -// NewSigner returns a new signer using the provided keyring -func NewSigner( +// TxSigner is struct for building and signing Celestia transactions +// It supports multiple accounts wrapping a Keyring. +// NOTE: All transactions may only have a single signer +// TxSigner is not thread-safe. +type TxSigner struct { + keys keyring.Keyring + enc client.TxConfig + chainID string + // FIXME: the signer is currently incapable of detecting an appversion + // change and could produce incorrect PFBs if it the network is at an + // appVersion that the signer does not support + appVersion uint64 + + // set of accounts that the signer can manage. Should match the keys on the keyring + accounts map[string]*Account + addressToAccountMap map[string]string +} + +// NewTxSigner returns a new signer using the provided keyring +// There must be at least one account in the keyring +// The first account provided will be set as the default +func NewTxSigner( keys keyring.Keyring, - conn *grpc.ClientConn, - address sdktypes.AccAddress, - enc client.TxConfig, + encCfg client.TxConfig, chainID string, - accountNumber uint64, - sequence uint64, -) (*Signer, error) { - // check that the address exists - record, err := keys.KeyByAddress(address) - if err != nil { - return nil, err - } - - pk, err := record.GetPubKey() - if err != nil { - return nil, err - } - - return &Signer{ - keys: keys, - address: address, - grpc: conn, - enc: enc, - pk: pk, - chainID: chainID, - accountNumber: accountNumber, - localSequence: sequence, - networkSequence: sequence, - pollTime: DefaultPollTime, - outboundSequences: make(map[uint64]struct{}), - reverseTxHashSequenceMap: make(map[string]uint64), - }, nil -} - -// SetupSingleSigner sets up a signer based on the provided keyring. The keyring -// must contain exactly one key. It extracts the address from the key and uses -// the grpc connection to populate the chainID, account number, and sequence -// number. -func SetupSingleSigner(ctx context.Context, keys keyring.Keyring, conn *grpc.ClientConn, encCfg encoding.Config) (*Signer, error) { - records, err := keys.List() - if err != nil { - return nil, err - } - - if len(records) != 1 { - return nil, errors.New("keyring must contain exactly one key") - } - - address, err := records[0].GetAddress() - if err != nil { - return nil, err - } - - return SetupSigner(ctx, keys, conn, address, encCfg) -} - -// SetupSigner uses the underlying grpc connection to populate the chainID, accountNumber and sequence number of the -// account. -func SetupSigner( - ctx context.Context, - keys keyring.Keyring, - conn *grpc.ClientConn, - address sdktypes.AccAddress, - encCfg encoding.Config, -) (*Signer, error) { - resp, err := tmservice.NewServiceClient(conn).GetLatestBlock(ctx, &tmservice.GetLatestBlockRequest{}) - if err != nil { - return nil, err - } - - chainID := resp.SdkBlock.Header.ChainID - accNum, seqNum, err := QueryAccount(ctx, conn, encCfg, address.String()) - if err != nil { - return nil, err - } - - return NewSigner(keys, conn, address, encCfg.TxConfig, chainID, accNum, seqNum) -} - -// SubmitTx forms a transaction from the provided messages, signs it, and submits it to the chain. TxOptions -// may be provided to set the fee and gas limit. -func (s *Signer) SubmitTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { - tx, err := s.CreateTx(msgs, opts...) - if err != nil { - return nil, err - } - - resp, err := s.BroadcastTx(ctx, tx) - if err != nil { - return resp, err - } - - return s.ConfirmTx(ctx, resp.TxHash) -} - -// SubmitPayForBlob forms a transaction from the provided blobs, signs it, and submits it to the chain. -// TxOptions may be provided to set the fee and gas limit. -func (s *Signer) SubmitPayForBlob(ctx context.Context, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { - resp, err := s.broadcastPayForBlob(ctx, blobs, opts...) - if err != nil { - return resp, err + appVersion uint64, + accounts ...*Account, +) (*TxSigner, error) { + s := &TxSigner{ + keys: keys, + chainID: chainID, + enc: encCfg, + accounts: make(map[string]*Account), + addressToAccountMap: make(map[string]string), + appVersion: appVersion, + } + + for _, acc := range accounts { + if err := s.AddAccount(acc); err != nil { + return nil, err + } } - return s.ConfirmTx(ctx, resp.TxHash) + return s, nil } -func (s *Signer) broadcastPayForBlob(ctx context.Context, blobs []*blob.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { - s.mtx.Lock() - defer s.mtx.Unlock() - txBytes, seqNum, err := s.createPayForBlobs(blobs, opts...) +// CreateTx forms a transaction from the provided messages and signs it. +// TxOptions may be optionally used to set the gas limit and fee. +func (s *TxSigner) CreateTx(msgs []sdktypes.Msg, opts ...TxOption) ([]byte, error) { + tx, _, _, err := s.SignTx(msgs, opts...) if err != nil { return nil, err } - - return s.broadcastTx(ctx, txBytes, seqNum) + return s.EncodeTx(tx) } -// CreateTx forms a transaction from the provided messages and signs it. TxOptions may be optionally -// used to set the gas limit and fee. -func (s *Signer) CreateTx(msgs []sdktypes.Msg, opts ...TxOption) (authsigning.Tx, error) { - s.mtx.Lock() - defer s.mtx.Unlock() - - return s.createTx(msgs, opts...) -} - -func (s *Signer) createTx(msgs []sdktypes.Msg, opts ...TxOption) (authsigning.Tx, error) { +func (s *TxSigner) SignTx(msgs []sdktypes.Msg, opts ...TxOption) (authsigning.Tx, string, uint64, error) { txBuilder := s.txBuilder(opts...) if err := txBuilder.SetMsgs(msgs...); err != nil { - return nil, err + return nil, "", 0, err } - if err := s.signTransaction(txBuilder, s.getAndIncrementSequence()); err != nil { - return nil, err + signer, sequence, err := s.signTransaction(txBuilder) + if err != nil { + return nil, "", 0, err } - return txBuilder.GetTx(), nil -} - -func (s *Signer) CreatePayForBlob(blobs []*tmproto.Blob, opts ...TxOption) ([]byte, error) { - s.mtx.Lock() - defer s.mtx.Unlock() - blobTx, _, err := s.createPayForBlobs(blobs, opts...) - return blobTx, err + return txBuilder.GetTx(), signer, sequence, nil } -func (s *Signer) createPayForBlobs(blobs []*tmproto.Blob, opts ...TxOption) ([]byte, uint64, error) { - msg, err := blob.NewMsgPayForBlobs(s.address.String(), blobs...) - if err != nil { - return nil, 0, err +func (s *TxSigner) CreatePayForBlobs(accountName string, blobs []*tmproto.Blob, opts ...TxOption) ([]byte, uint64, error) { + acc, exists := s.accounts[accountName] + if !exists { + return nil, 0, fmt.Errorf("account %s not found", accountName) } - tx, err := s.createTx([]sdktypes.Msg{msg}, opts...) + msg, err := blobtypes.NewMsgPayForBlobs(acc.address.String(), blobs...) if err != nil { return nil, 0, err } - seqNum, err := getSequenceNumber(tx) + tx, _, sequence, err := s.SignTx([]sdktypes.Msg{msg}, opts...) if err != nil { - panic(err) + return nil, 0, err } txBytes, err := s.EncodeTx(tx) @@ -222,14 +108,14 @@ func (s *Signer) createPayForBlobs(blobs []*tmproto.Blob, opts ...TxOption) ([]b } blobTx, err := tmtypes.MarshalBlobTx(txBytes, blobs...) - return blobTx, seqNum, err + return blobTx, sequence, err } -func (s *Signer) EncodeTx(tx sdktypes.Tx) ([]byte, error) { +func (s *TxSigner) EncodeTx(tx sdktypes.Tx) ([]byte, error) { return s.enc.TxEncoder()(tx) } -func (s *Signer) DecodeTx(txBytes []byte) (authsigning.Tx, error) { +func (s *TxSigner) DecodeTx(txBytes []byte) (authsigning.Tx, error) { tx, err := s.enc.TxDecoder()(txBytes) if err != nil { return nil, err @@ -241,306 +127,132 @@ func (s *Signer) DecodeTx(txBytes []byte) (authsigning.Tx, error) { return authTx, nil } -// BroadcastTx submits the provided transaction bytes to the chain and returns the response. -func (s *Signer) BroadcastTx(ctx context.Context, tx authsigning.Tx) (*sdktypes.TxResponse, error) { - s.mtx.Lock() - defer s.mtx.Unlock() - txBytes, err := s.EncodeTx(tx) - if err != nil { - return nil, err - } - sequence, err := getSequenceNumber(tx) - if err != nil { - return nil, err - } - return s.broadcastTx(ctx, txBytes, sequence) +// ChainID returns the chain ID of the signer. +func (s *TxSigner) ChainID() string { + return s.chainID } -// CONTRACT: assumes the caller has the lock -func (s *Signer) broadcastTx(ctx context.Context, txBytes []byte, sequence uint64) (*sdktypes.TxResponse, error) { - if _, exists := s.outboundSequences[sequence]; exists { - return s.retryBroadcastingTx(ctx, txBytes, sequence+1) - } - - if sequence < s.networkSequence { - s.localSequence = s.networkSequence - return s.retryBroadcastingTx(ctx, txBytes, s.localSequence) - } +// Account returns an account of the signer from the key name +func (s *TxSigner) Account(name string) *Account { + return s.accounts[name] +} - txClient := sdktx.NewServiceClient(s.grpc) - resp, err := txClient.BroadcastTx( - ctx, - &sdktx.BroadcastTxRequest{ - Mode: sdktx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, - }, - ) - if err != nil { - return nil, err - } - if apperrors.IsNonceMismatchCode(resp.TxResponse.Code) { - // extract what the lastCommittedNonce on chain is - nextSequence, err := apperrors.ParseExpectedSequence(resp.TxResponse.RawLog) - if err != nil { - return nil, fmt.Errorf("parsing nonce mismatch upon retry: %w", err) - } - s.networkSequence = nextSequence - s.localSequence = nextSequence - // FIXME: We can't actually resign the transaction. A malicious node - // may manipulate us into signing the same transaction several times - // and then executing them. We need some proof of what the last network - // sequence is rather than relying on an error provided by the node - // return s.retryBroadcastingTx(ctx, txBytes, nextSequence) - // Ref: https://github.com/celestiaorg/celestia-app/issues/3256 - // return s.retryBroadcastingTx(ctx, txBytes, nextSequence) - } else if resp.TxResponse.Code == abci.CodeTypeOK { - s.outboundSequences[sequence] = struct{}{} - s.reverseTxHashSequenceMap[resp.TxResponse.TxHash] = sequence - return resp.TxResponse, nil +// AccountByAddress returns the account associated with the given address +func (s *TxSigner) AccountByAddress(address sdktypes.AccAddress) *Account { + accountName, exists := s.addressToAccountMap[address.String()] + if !exists { + return nil } - return resp.TxResponse, fmt.Errorf("tx failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) + return s.accounts[accountName] } -// retryBroadcastingTx creates a new transaction by copying over an existing transaction but creates a new signature with the -// new sequence number. It then calls `broadcastTx` and attempts to submit the transaction -func (s *Signer) retryBroadcastingTx(ctx context.Context, txBytes []byte, newSequenceNumber uint64) (*sdktypes.TxResponse, error) { - blobTx, isBlobTx := tmtypes.UnmarshalBlobTx(txBytes) - if isBlobTx { - txBytes = blobTx.Tx - } - tx, err := s.DecodeTx(txBytes) - if err != nil { - return nil, err - } - txBuilder := s.txBuilder() - if err := txBuilder.SetMsgs(tx.GetMsgs()...); err != nil { - return nil, err - } - if granter := tx.FeeGranter(); granter != nil { - txBuilder.SetFeeGranter(granter) - } - if payer := tx.FeePayer(); payer != nil { - txBuilder.SetFeePayer(payer) - } - if memo := tx.GetMemo(); memo != "" { - txBuilder.SetMemo(memo) - } - if fee := tx.GetFee(); fee != nil { - txBuilder.SetFeeAmount(fee) - } - if gas := tx.GetGas(); gas > 0 { - txBuilder.SetGasLimit(gas) +func (s *TxSigner) Accounts() []*Account { + accounts := make([]*Account, len(s.accounts)) + i := 0 + for _, acc := range s.accounts { + accounts[i] = acc + i++ } + return accounts +} - if err := s.signTransaction(txBuilder, newSequenceNumber); err != nil { - return nil, fmt.Errorf("resigning transaction: %w", err) +func (s *TxSigner) findAccount(txbuilder client.TxBuilder) (*Account, error) { + signers := txbuilder.GetTx().GetSigners() + if len(signers) == 0 { + return nil, fmt.Errorf("message has no signer") } - - newTxBytes, err := s.EncodeTx(txBuilder.GetTx()) - if err != nil { - return nil, err + accountName, exists := s.addressToAccountMap[signers[0].String()] + if !exists { + return nil, fmt.Errorf("account %s not found", signers[0].String()) } + return s.accounts[accountName], nil +} - // rewrap the blob tx if it was originally a blob tx - if isBlobTx { - newTxBytes, err = tmtypes.MarshalBlobTx(newTxBytes, blobTx.Blobs...) - if err != nil { - return nil, err - } +func (s *TxSigner) IncrementSequence(accountName string) error { + acc, exists := s.accounts[accountName] + if !exists { + return fmt.Errorf("account %s does not exist", accountName) } - - return s.broadcastTx(ctx, newTxBytes, newSequenceNumber) + acc.sequence++ + return nil } -// ConfirmTx periodically pings the provided node for the commitment of a transaction by its -// hash. It will continually loop until the context is cancelled, the tx is found or an error -// is encountered. -func (s *Signer) ConfirmTx(ctx context.Context, txHash string) (*sdktypes.TxResponse, error) { - txClient := sdktx.NewServiceClient(s.grpc) - - pollTime := s.getPollTime() - timer := time.NewTimer(0) - defer timer.Stop() - - for { - select { - case <-ctx.Done(): - return &sdktypes.TxResponse{}, ctx.Err() - case <-timer.C: - resp, err := txClient.GetTx(ctx, &sdktx.GetTxRequest{Hash: txHash}) - if err == nil { - if resp.TxResponse.Code != 0 { - s.updateNetworkSequence(txHash, false) - return resp.TxResponse, fmt.Errorf("tx was included but failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) - } - s.updateNetworkSequence(txHash, true) - return resp.TxResponse, nil - } - // FIXME: this is a relatively brittle of working out whether to retry or not. The tx might be not found for other - // reasons. It may have been removed from the mempool at a later point. We should build an endpoint that gives the - // signer more information on the status of their transaction and then update the logic here - if !strings.Contains(err.Error(), "not found") { - return &sdktypes.TxResponse{}, err - } - - timer.Reset(pollTime) - } +func (s *TxSigner) SetSequence(accountName string, seq uint64) error { + acc, exists := s.accounts[accountName] + if !exists { + return fmt.Errorf("account %s does not exist", accountName) } + + acc.sequence = seq + return nil } -func (s *Signer) EstimateGas(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (uint64, error) { - txBuilder := s.txBuilder(opts...) - if err := txBuilder.SetMsgs(msgs...); err != nil { - return 0, err +func (s *TxSigner) AddAccount(acc *Account) error { + if acc == nil { + return errors.New("account is nil") } - if err := s.signTransaction(txBuilder, s.LocalSequence()); err != nil { - return 0, err + record, err := s.keys.Key(acc.name) + if err != nil { + return fmt.Errorf("retrieving key for account %s: %w", acc.name, err) } - txBytes, err := s.enc.TxEncoder()(txBuilder.GetTx()) + addr, err := record.GetAddress() if err != nil { - return 0, err + return fmt.Errorf("getting address for key %s: %w", acc.pubKey, err) } - resp, err := sdktx.NewServiceClient(s.grpc).Simulate(ctx, &sdktx.SimulateRequest{ - TxBytes: txBytes, - }) + pk, err := record.GetPubKey() if err != nil { - return 0, err + return fmt.Errorf("getting public key for account %s: %w", acc.name, err) } - return resp.GasInfo.GasUsed, nil -} - -// ChainID returns the chain ID of the signer. -func (s *Signer) ChainID() string { - return s.chainID -} - -// AccountNumber returns the account number of the signer. -func (s *Signer) AccountNumber() uint64 { - return s.accountNumber -} - -// Address returns the address of the signer. -func (s *Signer) Address() sdktypes.AccAddress { - return s.address -} - -// SetPollTime sets how often the signer should poll for the confirmation of the transaction -func (s *Signer) SetPollTime(pollTime time.Duration) { - s.mtx.Lock() - defer s.mtx.Unlock() - s.pollTime = pollTime -} - -func (s *Signer) getPollTime() time.Duration { - s.mtx.Lock() - defer s.mtx.Unlock() - return s.pollTime -} - -// PubKey returns the public key of the signer -func (s *Signer) PubKey() cryptotypes.PubKey { - return s.pk -} - -// DEPRECATED: use Sequence instead -func (s *Signer) GetSequence() uint64 { - return s.getAndIncrementSequence() -} - -// LocalSequence returns the next sequence number of the signers -// locally saved -func (s *Signer) LocalSequence() uint64 { - s.mtx.RLock() - defer s.mtx.RUnlock() - return s.localSequence -} - -func (s *Signer) NetworkSequence() uint64 { - s.mtx.RLock() - defer s.mtx.RUnlock() - return s.networkSequence -} - -// getAndIncrementSequence gets the latest signed sequence and increments the -// local sequence number -func (s *Signer) getAndIncrementSequence() uint64 { - defer func() { s.localSequence++ }() - return s.localSequence -} - -// ForceSetSequence manually overrides the current local and network level -// sequence number. Be careful when invoking this as it may cause the -// transactions to reject the sequence if it doesn't match the one in state -func (s *Signer) ForceSetSequence(seq uint64) { - s.mtx.Lock() - defer s.mtx.Unlock() - s.localSequence = seq - s.networkSequence = seq -} - -// updateNetworkSequence is called once a transaction is confirmed -// and updates the chains last known sequence number -func (s *Signer) updateNetworkSequence(txHash string, success bool) { - s.mtx.Lock() - defer s.mtx.Unlock() - sequence, exists := s.reverseTxHashSequenceMap[txHash] - if !exists { - return - } - if success && sequence >= s.networkSequence { - s.networkSequence = sequence + 1 - } - delete(s.outboundSequences, sequence) - delete(s.reverseTxHashSequenceMap, txHash) + acc.address = addr + acc.pubKey = pk + s.accounts[acc.name] = acc + s.addressToAccountMap[addr.String()] = acc.name + return nil } // Keyring exposes the signers underlying keyring -func (s *Signer) Keyring() keyring.Keyring { +func (s *TxSigner) Keyring() keyring.Keyring { return s.keys } -func (s *Signer) signTransaction(builder client.TxBuilder, sequence uint64) error { - signers := builder.GetTx().GetSigners() - if len(signers) != 1 { - return fmt.Errorf("expected 1 signer, got %d", len(signers)) - } - - if !s.address.Equals(signers[0]) { - return fmt.Errorf("expected signer %s, got %s", s.address.String(), signers[0].String()) +func (s *TxSigner) signTransaction(builder client.TxBuilder) (string, uint64, error) { + account, err := s.findAccount(builder) + if err != nil { + return "", 0, err } // To ensure we have the correct bytes to sign over we produce // a dry run of the signing data - err := builder.SetSignatures(s.getSignatureV2(sequence, nil)) + err = builder.SetSignatures(s.getSignatureV2(account.sequence, account.pubKey, nil)) if err != nil { - return fmt.Errorf("error setting draft signatures: %w", err) + return "", 0, fmt.Errorf("error setting draft signatures: %w", err) } // now we can use the data to produce the signature from the signer - signature, err := s.createSignature(builder, sequence) + signature, err := s.createSignature(builder, account, account.sequence) if err != nil { - return fmt.Errorf("error creating signature: %w", err) + return "", 0, fmt.Errorf("error creating signature: %w", err) } - err = builder.SetSignatures(s.getSignatureV2(sequence, signature)) + err = builder.SetSignatures(s.getSignatureV2(account.sequence, account.pubKey, signature)) if err != nil { - return fmt.Errorf("error setting signatures: %w", err) + return "", 0, fmt.Errorf("error setting signatures: %w", err) } - return nil + return account.name, account.sequence, nil } -func (s *Signer) createSignature(builder client.TxBuilder, sequence uint64) ([]byte, error) { +func (s *TxSigner) createSignature(builder client.TxBuilder, account *Account, sequence uint64) ([]byte, error) { signerData := authsigning.SignerData{ - Address: s.address.String(), + Address: account.address.String(), ChainID: s.ChainID(), - AccountNumber: s.accountNumber, + AccountNumber: account.accountNumber, Sequence: sequence, - PubKey: s.pk, + PubKey: account.pubKey, } bytesToSign, err := s.enc.SignModeHandler().GetSignBytes( @@ -552,7 +264,7 @@ func (s *Signer) createSignature(builder client.TxBuilder, sequence uint64) ([]b return nil, fmt.Errorf("error getting sign bytes: %w", err) } - signature, _, err := s.keys.SignByAddress(s.address, bytesToSign) + signature, _, err := s.keys.Sign(account.name, bytesToSign) if err != nil { return nil, fmt.Errorf("error signing bytes: %w", err) } @@ -561,7 +273,7 @@ func (s *Signer) createSignature(builder client.TxBuilder, sequence uint64) ([]b } // txBuilder returns the default sdk Tx builder using the celestia-app encoding config -func (s *Signer) txBuilder(opts ...TxOption) client.TxBuilder { +func (s *TxSigner) txBuilder(opts ...TxOption) client.TxBuilder { builder := s.enc.NewTxBuilder() for _, opt := range opts { builder = opt(builder) @@ -569,28 +281,7 @@ func (s *Signer) txBuilder(opts ...TxOption) client.TxBuilder { return builder } -// QueryAccount fetches the account number and sequence number from the celestia-app node. -func QueryAccount(ctx context.Context, conn *grpc.ClientConn, encCfg encoding.Config, address string) (accNum uint64, seqNum uint64, err error) { - qclient := authtypes.NewQueryClient(conn) - resp, err := qclient.Account( - ctx, - &authtypes.QueryAccountRequest{Address: address}, - ) - if err != nil { - return accNum, seqNum, err - } - - var acc authtypes.AccountI - err = encCfg.InterfaceRegistry.UnpackAny(resp.Account, &acc) - if err != nil { - return accNum, seqNum, err - } - - accNum, seqNum = acc.GetAccountNumber(), acc.GetSequence() - return accNum, seqNum, nil -} - -func (s *Signer) getSignatureV2(sequence uint64, signature []byte) signing.SignatureV2 { +func (s *TxSigner) getSignatureV2(sequence uint64, pubKey cryptotypes.PubKey, signature []byte) signing.SignatureV2 { sigV2 := signing.SignatureV2{ Data: &signing.SingleSignatureData{ SignMode: signing.SignMode_SIGN_MODE_DIRECT, @@ -599,19 +290,7 @@ func (s *Signer) getSignatureV2(sequence uint64, signature []byte) signing.Signa Sequence: sequence, } if sequence == 0 { - sigV2.PubKey = s.pk + sigV2.PubKey = pubKey } return sigV2 } - -func getSequenceNumber(tx authsigning.Tx) (uint64, error) { - sigs, err := tx.GetSignaturesV2() - if err != nil { - return 0, err - } - if len(sigs) > 1 { - return 0, fmt.Errorf("only a signle signature is supported, got %d", len(sigs)) - } - - return sigs[0].Sequence, nil -} diff --git a/pkg/user/tx_client.go b/pkg/user/tx_client.go new file mode 100644 index 0000000000..e7d47d4823 --- /dev/null +++ b/pkg/user/tx_client.go @@ -0,0 +1,463 @@ +package user + +import ( + "bytes" + "context" + "errors" + "fmt" + "strings" + "sync" + "time" + + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdktx "github.com/cosmos/cosmos-sdk/types/tx" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + "google.golang.org/grpc" + + "github.com/celestiaorg/celestia-app/app/encoding" + apperrors "github.com/celestiaorg/celestia-app/app/errors" +) + +const ( + DefaultPollTime = 3 * time.Second + DefaultGasMultiplier float64 = 1.1 +) + +type Option func(s *TxClient) + +// WithGasMultiplier is a functional option allows to configure the gas multiplier. +func WithGasMultiplier(multiplier float64) Option { + return func(c *TxClient) { + c.gasMultiplier = multiplier + } +} + +func WithPollTime(time time.Duration) Option { + return func(c *TxClient) { + c.pollTime = time + } +} + +func WithDefaultAddress(address sdktypes.AccAddress) Option { + return func(c *TxClient) { + record, err := c.signer.keys.KeyByAddress(address) + if err != nil { + panic(err) + } + c.defaultAccount = record.Name + } +} + +func WithDefaultAccount(name string) Option { + return func(c *TxClient) { + if _, err := c.signer.keys.Key(name); err != nil { + panic(err) + } + c.defaultAccount = name + } +} + +// TxClient is an abstraction for building, signing, and broadcasting Celestia transactions +// It supports multiple accounts. If none is specified, it will +// try use the default account. +// TxClient is thread-safe. +type TxClient struct { + mtx sync.Mutex + signer *TxSigner + registry codectypes.InterfaceRegistry + grpc *grpc.ClientConn + // how often to poll the network for confirmation of a transaction + pollTime time.Duration + // gasMultiplier is used to increase gas limit as it is sometimes underestimated + gasMultiplier float64 + defaultAccount string + defaultAddress sdktypes.AccAddress +} + +// NewTxClient returns a new signer using the provided keyring +func NewTxClient( + signer *TxSigner, + conn *grpc.ClientConn, + registry codectypes.InterfaceRegistry, + options ...Option, +) (*TxClient, error) { + records, err := signer.keys.List() + if err != nil { + return nil, fmt.Errorf("retrieving keys: %w", err) + } + + if len(records) == 0 { + return nil, errors.New("signer must have at least one key") + } + + addr, err := records[0].GetAddress() + if err != nil { + return nil, err + } + + txClient := &TxClient{ + signer: signer, + registry: registry, + grpc: conn, + pollTime: DefaultPollTime, + gasMultiplier: DefaultGasMultiplier, + defaultAccount: records[0].Name, + defaultAddress: addr, + } + + for _, opt := range options { + opt(txClient) + } + + return txClient, nil +} + +// SetupTxClient uses the underlying grpc connection to populate the chainID, accountNumber and sequence number of all +// the accounts in the keyring. +func SetupTxClient( + ctx context.Context, + keys keyring.Keyring, + conn *grpc.ClientConn, + encCfg encoding.Config, + options ...Option, +) (*TxClient, error) { + resp, err := tmservice.NewServiceClient(conn).GetLatestBlock( + ctx, + &tmservice.GetLatestBlockRequest{}, + ) + if err != nil { + return nil, err + } + + chainID := resp.SdkBlock.Header.ChainID + appVersion := resp.SdkBlock.Header.Version.App + + records, err := keys.List() + if err != nil { + return nil, err + } + + accounts := make([]*Account, 0, len(records)) + for _, record := range records { + addr, err := record.GetAddress() + if err != nil { + return nil, err + } + accNum, seqNum, err := QueryAccountInfo(ctx, conn, encCfg.InterfaceRegistry, addr) + if err != nil { + // skip over the accounts that don't exist in state + continue + } + + accounts = append(accounts, NewAccount(record.Name, accNum, seqNum)) + } + + signer, err := NewTxSigner(keys, encCfg.TxConfig, chainID, appVersion, accounts...) + if err != nil { + return nil, fmt.Errorf("failed to create signer: %w", err) + } + + return NewTxClient(signer, conn, encCfg.InterfaceRegistry, options...) +} + +// SubmitPayForBlob forms a transaction from the provided blobs, signs it, and submits it to the chain. +// TxOptions may be provided to set the fee and gas limit. +func (s *TxClient) SubmitPayForBlob(ctx context.Context, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + resp, err := s.BroadcastPayForBlob(ctx, blobs, opts...) + if err != nil { + return resp, err + } + + return s.ConfirmTx(ctx, resp.TxHash) +} + +func (s *TxClient) SubmitPayForBlobsWithAccount(ctx context.Context, account string, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + resp, err := s.BroadcastPayForBlobWithAccount(ctx, account, blobs, opts...) + if err != nil { + return resp, err + } + + return s.ConfirmTx(ctx, resp.TxHash) +} + +// BroadcastPayForBlob signs and broadcasts a transaction to pay for blobs. +// It does not confirm that the transaction has been committed on chain. +func (s *TxClient) BroadcastPayForBlob(ctx context.Context, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + return s.BroadcastPayForBlobWithAccount(ctx, s.defaultAccount, blobs, opts...) +} + +func (s *TxClient) BroadcastPayForBlobWithAccount(ctx context.Context, account string, blobs []*tmproto.Blob, opts ...TxOption) (*sdktypes.TxResponse, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + if err := s.checkAccountLoaded(ctx, account); err != nil { + return nil, err + } + + txBytes, _, err := s.signer.CreatePayForBlobs(account, blobs, opts...) + if err != nil { + return nil, err + } + + return s.broadcastTx(ctx, txBytes, account) +} + +// SubmitTx forms a transaction from the provided messages, signs it, and submits it to the chain. TxOptions +// may be provided to set the fee and gas limit. +func (s *TxClient) SubmitTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { + resp, err := s.BroadcastTx(ctx, msgs, opts...) + if err != nil { + return resp, err + } + + return s.ConfirmTx(ctx, resp.TxHash) +} + +func (s *TxClient) BroadcastTx(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (*sdktypes.TxResponse, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + account, err := s.getAccountNameFromMsgs(msgs) + if err != nil { + return nil, err + } + + if err := s.checkAccountLoaded(ctx, account); err != nil { + return nil, err + } + + tx, account, _, err := s.signer.SignTx(msgs, opts...) + if err != nil { + return nil, err + } + + txBytes, err := s.signer.EncodeTx(tx) + if err != nil { + return nil, err + } + + return s.broadcastTx(ctx, txBytes, account) +} + +func (s *TxClient) broadcastTx(ctx context.Context, txBytes []byte, signer string) (*sdktypes.TxResponse, error) { + txClient := sdktx.NewServiceClient(s.grpc) + resp, err := txClient.BroadcastTx( + ctx, + &sdktx.BroadcastTxRequest{ + Mode: sdktx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, + ) + if err != nil { + return nil, err + } + if resp.TxResponse.Code != abci.CodeTypeOK { + if apperrors.IsNonceMismatchCode(resp.TxResponse.Code) { + // query the account to update the sequence number on-chain for the account + _, seqNum, err := QueryAccountInfo(ctx, s.grpc, s.registry, s.signer.accounts[signer].address) + if err != nil { + return nil, fmt.Errorf("querying account for new sequence number: %w\noriginal tx response: %s", err, resp.TxResponse.RawLog) + } + if err := s.signer.SetSequence(signer, seqNum); err != nil { + return nil, fmt.Errorf("setting sequence: %w", err) + } + return s.retryBroadcastingTx(ctx, txBytes) + } + return resp.TxResponse, fmt.Errorf("tx failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) + } + + // after the transaction has been submitted, we can increment the + // sequence of the signer + if err := s.signer.IncrementSequence(signer); err != nil { + return nil, fmt.Errorf("increment sequencing: %w", err) + } + return resp.TxResponse, nil +} + +// retryBroadcastingTx creates a new transaction by copying over an existing transaction but creates a new signature with the +// new sequence number. It then calls `broadcastTx` and attempts to submit the transaction +func (s *TxClient) retryBroadcastingTx(ctx context.Context, txBytes []byte) (*sdktypes.TxResponse, error) { + blobTx, isBlobTx := tmtypes.UnmarshalBlobTx(txBytes) + if isBlobTx { + txBytes = blobTx.Tx + } + tx, err := s.signer.DecodeTx(txBytes) + if err != nil { + return nil, err + } + txBuilder := s.signer.txBuilder() + if err := txBuilder.SetMsgs(tx.GetMsgs()...); err != nil { + return nil, err + } + if granter := tx.FeeGranter(); granter != nil { + txBuilder.SetFeeGranter(granter) + } + if payer := tx.FeePayer(); payer != nil { + txBuilder.SetFeePayer(payer) + } + if memo := tx.GetMemo(); memo != "" { + txBuilder.SetMemo(memo) + } + if fee := tx.GetFee(); fee != nil { + txBuilder.SetFeeAmount(fee) + } + if gas := tx.GetGas(); gas > 0 { + txBuilder.SetGasLimit(gas) + } + + signer, _, err := s.signer.signTransaction(txBuilder) + if err != nil { + return nil, fmt.Errorf("resigning transaction: %w", err) + } + + newTxBytes, err := s.signer.EncodeTx(txBuilder.GetTx()) + if err != nil { + return nil, err + } + + // rewrap the blob tx if it was originally a blob tx + if isBlobTx { + newTxBytes, err = tmtypes.MarshalBlobTx(newTxBytes, blobTx.Blobs...) + if err != nil { + return nil, err + } + } + + return s.broadcastTx(ctx, newTxBytes, signer) +} + +// ConfirmTx periodically pings the provided node for the commitment of a transaction by its +// hash. It will continually loop until the context is cancelled, the tx is found or an error +// is encountered. +func (s *TxClient) ConfirmTx(ctx context.Context, txHash string) (*sdktypes.TxResponse, error) { + txClient := sdktx.NewServiceClient(s.grpc) + + pollTicker := time.NewTicker(s.pollTime) + defer pollTicker.Stop() + + for { + resp, err := txClient.GetTx(ctx, &sdktx.GetTxRequest{Hash: txHash}) + if err == nil { + if resp.TxResponse.Code != 0 { + return resp.TxResponse, fmt.Errorf("tx was included but failed with code %d: %s", resp.TxResponse.Code, resp.TxResponse.RawLog) + } + return resp.TxResponse, nil + } + // FIXME: this is a relatively brittle of working out whether to retry or not. The tx might be not found for other + // reasons. It may have been removed from the mempool at a later point. We should build an endpoint that gives the + // signer more information on the status of their transaction and then update the logic here + if !strings.Contains(err.Error(), "not found") { + return &sdktypes.TxResponse{}, err + } + + // Wait for the next round. + select { + case <-ctx.Done(): + return &sdktypes.TxResponse{}, ctx.Err() + case <-pollTicker.C: + } + } +} + +func (s *TxClient) EstimateGas(ctx context.Context, msgs []sdktypes.Msg, opts ...TxOption) (uint64, error) { + s.mtx.Lock() + defer s.mtx.Unlock() + + txBuilder := s.signer.txBuilder(opts...) + if err := txBuilder.SetMsgs(msgs...); err != nil { + return 0, err + } + + _, _, err := s.signer.signTransaction(txBuilder) + if err != nil { + return 0, err + } + + txBytes, err := s.signer.EncodeTx(txBuilder.GetTx()) + if err != nil { + return 0, err + } + + resp, err := sdktx.NewServiceClient(s.grpc).Simulate(ctx, &sdktx.SimulateRequest{ + TxBytes: txBytes, + }) + if err != nil { + return 0, err + } + + gasLimit := uint64(float64(resp.GasInfo.GasUsed) * s.gasMultiplier) + return gasLimit, nil +} + +// Account returns an account of the signer from the key name. Also returns a bool if the +// account exists. +// Thread-safe +func (s *TxClient) Account(name string) (*Account, bool) { + s.mtx.Lock() + defer s.mtx.Unlock() + acc, exists := s.signer.accounts[name] + if !exists { + return nil, false + } + return acc.Copy(), true +} + +func (s *TxClient) AccountByAddress(address sdktypes.AccAddress) *Account { + s.mtx.Lock() + defer s.mtx.Unlock() + return s.signer.AccountByAddress(address) +} + +func (s *TxClient) DefaultAddress() sdktypes.AccAddress { + return s.defaultAddress +} + +func (s *TxClient) DefaultAccountName() string { return s.defaultAccount } + +func (s *TxClient) checkAccountLoaded(ctx context.Context, account string) error { + if _, exists := s.signer.accounts[account]; exists { + return nil + } + record, err := s.signer.keys.Key(account) + if err != nil { + return fmt.Errorf("trying to find account %s on keyring: %w", account, err) + } + addr, err := record.GetAddress() + if err != nil { + return fmt.Errorf("retrieving address from keyring: %w", err) + } + accNum, sequence, err := QueryAccountInfo(ctx, s.grpc, s.registry, addr) + if err != nil { + return fmt.Errorf("querying account %s: %w", account, err) + } + return s.signer.AddAccount(NewAccount(account, accNum, sequence)) +} + +func (s *TxClient) getAccountNameFromMsgs(msgs []sdktypes.Msg) (string, error) { + var addr sdktypes.AccAddress + for _, msg := range msgs { + signers := msg.GetSigners() + if len(signers) != 1 { + return "", fmt.Errorf("only one signer per transaction supported, got %d", len(signers)) + } + if addr == nil { + addr = signers[0] + } + if !bytes.Equal(addr, signers[0]) { + return "", errors.New("not supported: got two different signers across multiple messages") + } + } + record, err := s.signer.keys.KeyByAddress(addr) + if err != nil { + return "", err + } + return record.Name, nil +} + +// Signer exposes the tx clients underlying signer +func (s *TxClient) Signer() *TxSigner { + return s.signer +} diff --git a/pkg/user/tx_client_test.go b/pkg/user/tx_client_test.go new file mode 100644 index 0000000000..07ade404da --- /dev/null +++ b/pkg/user/tx_client_test.go @@ -0,0 +1,168 @@ +package user_test + +import ( + "context" + "testing" + "time" + + "github.com/celestiaorg/celestia-app/test/util/testfactory" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/rand" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/user" + "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/celestiaorg/celestia-app/test/util/testnode" +) + +func TestTxClientTestSuite(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode.") + } + suite.Run(t, new(TxClientTestSuite)) +} + +type TxClientTestSuite struct { + suite.Suite + + ctx testnode.Context + encCfg encoding.Config + txClient *user.TxClient +} + +func (s *TxClientTestSuite) SetupSuite() { + s.encCfg = encoding.MakeConfig(app.ModuleEncodingRegisters...) + s.ctx, _, _ = testnode.NewNetwork(s.T(), testnode.DefaultConfig().WithAccounts([]string{"a"})) + _, err := s.ctx.WaitForHeight(1) + s.Require().NoError(err) + s.txClient, err = user.SetupTxClient(s.ctx.GoContext(), s.ctx.Keyring, s.ctx.GRPCClient, s.encCfg, user.WithGasMultiplier(1.2)) + s.Require().NoError(err) +} + +func (s *TxClientTestSuite) TestSubmitPayForBlob() { + t := s.T() + blobs := blobfactory.ManyRandBlobs(t, rand.NewRand(), 1e3, 1e4) + fee := user.SetFee(1e6) + gas := user.SetGasLimit(1e6) + subCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + resp, err := s.txClient.SubmitPayForBlob(subCtx, blobs, fee, gas) + require.NoError(t, err) + require.EqualValues(t, 0, resp.Code) +} + +func (s *TxClientTestSuite) TestSubmitTx() { + t := s.T() + fee := user.SetFee(1e6) + gas := user.SetGasLimit(1e6) + addr := s.txClient.DefaultAddress() + msg := bank.NewMsgSend(addr, testfactory.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 10))) + resp, err := s.txClient.SubmitTx(s.ctx.GoContext(), []sdk.Msg{msg}, fee, gas) + require.NoError(t, err) + require.EqualValues(t, 0, resp.Code) +} + +func (s *TxClientTestSuite) TestConfirmTx() { + t := s.T() + + fee := user.SetFee(1e6) + gas := user.SetGasLimit(1e6) + + t.Run("deadline exceeded when the context times out", func(t *testing.T) { + ctx, cancel := context.WithTimeout(s.ctx.GoContext(), time.Second) + defer cancel() + _, err := s.txClient.ConfirmTx(ctx, "E32BD15CAF57AF15D17B0D63CF4E63A9835DD1CEBB059C335C79586BC3013728") + require.Error(t, err) + require.Contains(t, err.Error(), context.DeadlineExceeded.Error()) + }) + + t.Run("should error when tx is not found", func(t *testing.T) { + ctx, cancel := context.WithTimeout(s.ctx.GoContext(), 5*time.Second) + defer cancel() + _, err := s.txClient.ConfirmTx(ctx, "not found tx") + require.Error(t, err) + }) + + t.Run("should success when tx is found immediately", func(t *testing.T) { + addr := s.txClient.DefaultAddress() + msg := bank.NewMsgSend(addr, testfactory.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 10))) + resp, err := s.txClient.BroadcastTx(s.ctx.GoContext(), []sdk.Msg{msg}, fee, gas) + require.NoError(t, err) + require.NotNil(t, resp) + ctx, cancel := context.WithTimeout(s.ctx.GoContext(), 30*time.Second) + defer cancel() + resp, err = s.txClient.ConfirmTx(ctx, resp.TxHash) + require.NoError(t, err) + require.Equal(t, abci.CodeTypeOK, resp.Code) + }) + + t.Run("should error when tx is found with a non-zero error code", func(t *testing.T) { + balance := s.queryCurrentBalance(t) + addr := s.txClient.DefaultAddress() + // Create a msg send with out of balance, ensure this tx fails + msg := bank.NewMsgSend(addr, testfactory.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 1+balance))) + resp, err := s.txClient.BroadcastTx(s.ctx.GoContext(), []sdk.Msg{msg}, fee, gas) + require.NoError(t, err) + require.NotNil(t, resp) + resp, err = s.txClient.ConfirmTx(s.ctx.GoContext(), resp.TxHash) + require.Error(t, err) + require.NotEqual(t, abci.CodeTypeOK, resp.Code) + }) +} + +func (s *TxClientTestSuite) TestGasEstimation() { + addr := s.txClient.DefaultAddress() + msg := bank.NewMsgSend(addr, testfactory.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 10))) + gas, err := s.txClient.EstimateGas(s.ctx.GoContext(), []sdk.Msg{msg}) + require.NoError(s.T(), err) + require.Greater(s.T(), gas, uint64(0)) +} + +// TestGasConsumption verifies that the amount deducted from a user's balance is +// based on the fee provided in the tx instead of the gas used by the tx. This +// behavior leads to poor UX because tx submitters must over-estimate the amount +// of gas that their tx will consume and they are not refunded for the excess. +func (s *TxClientTestSuite) TestGasConsumption() { + t := s.T() + + utiaToSend := int64(1) + addr := s.txClient.DefaultAddress() + msg := bank.NewMsgSend(addr, testfactory.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, utiaToSend))) + + gasPrice := int64(1) + gasLimit := uint64(1e6) + fee := uint64(1e6) // 1 TIA + // Note: gas price * gas limit = fee amount. So by setting gasLimit and fee + // to the same value, these options set a gas price of 1utia. + options := []user.TxOption{user.SetGasLimit(gasLimit), user.SetFee(fee)} + + balanceBefore := s.queryCurrentBalance(t) + resp, err := s.txClient.SubmitTx(s.ctx.GoContext(), []sdk.Msg{msg}, options...) + require.NoError(t, err) + + require.EqualValues(t, abci.CodeTypeOK, resp.Code) + balanceAfter := s.queryCurrentBalance(t) + + // verify that the amount deducted depends on the fee set in the tx. + amountDeducted := balanceBefore - balanceAfter - utiaToSend + require.Equal(t, int64(fee), amountDeducted) + + // verify that the amount deducted does not depend on the actual gas used. + gasUsedBasedDeduction := resp.GasUsed * gasPrice + require.NotEqual(t, gasUsedBasedDeduction, amountDeducted) + // The gas used based deduction should be less than the fee because the fee is 1 TIA. + require.Less(t, gasUsedBasedDeduction, int64(fee)) +} + +func (s *TxClientTestSuite) queryCurrentBalance(t *testing.T) int64 { + balanceQuery := bank.NewQueryClient(s.ctx.GRPCClient) + addr := s.txClient.DefaultAddress() + balanceResp, err := balanceQuery.AllBalances(s.ctx.GoContext(), &bank.QueryAllBalancesRequest{Address: addr.String()}) + require.NoError(t, err) + return balanceResp.Balances.AmountOf(app.BondDenom).Int64() +} From d366698c5f591f8c58874632f01cb8789295a3e7 Mon Sep 17 00:00:00 2001 From: Rootul P Date: Fri, 31 May 2024 11:25:55 -0600 Subject: [PATCH 03/13] chore(deps): upgrade to cosmos-sdk v1.23.0-sdk-v0.46.16 (#3517) Closes https://github.com/celestiaorg/celestia-app/issues/3448 Unblocks celestia-node because this cosmos-sdk release contains https://github.com/celestiaorg/cosmos-sdk/pull/401 which should let celestia-node upgrade their `prometheus/common` dependency. --- README.md | 2 +- app/default_overrides.go | 12 ++++++++ app/prepare_proposal.go | 2 +- app/process_proposal.go | 2 +- app/square_size.go | 4 +-- app/test/max_total_blob_size_test.go | 2 +- go.mod | 18 ++++++------ go.sum | 30 +++++++++---------- test/cmd/txsim/cli_test.go | 2 +- test/util/app_options.go | 12 ++++++++ test/util/malicious/out_of_order_prepare.go | 2 +- test/util/test_app.go | 32 ++++++++------------- test/util/testnode/config.go | 26 +++++++++++++---- x/mint/test/mint_test.go | 10 +++---- x/upgrade/test/removal_test.go | 7 ++--- 15 files changed, 93 insertions(+), 70 deletions(-) create mode 100644 test/util/app_options.go diff --git a/README.md b/README.md index 8de78e07da..bef49e0bd7 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ See for more information ### Tools -1. Install [golangci-lint](https://golangci-lint.run/usage/install/) +1. Install [golangci-lint](https://golangci-lint.run/welcome/install/) 1. Install [markdownlint](https://github.com/DavidAnson/markdownlint) 1. Install [hadolint](https://github.com/hadolint/hadolint) 1. Install [yamllint](https://yamllint.readthedocs.io/en/stable/quickstart.html) diff --git a/app/default_overrides.go b/app/default_overrides.go index 6079c3fcfc..31320a52e0 100644 --- a/app/default_overrides.go +++ b/app/default_overrides.go @@ -6,6 +6,7 @@ import ( "time" "github.com/celestiaorg/celestia-app/pkg/appconsts" + v1 "github.com/celestiaorg/celestia-app/pkg/appconsts/v1" "github.com/celestiaorg/celestia-app/x/mint" minttypes "github.com/celestiaorg/celestia-app/x/mint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -208,6 +209,17 @@ func DefaultConsensusParams() *tmproto.ConsensusParams { } } +func DefaultInitialConsensusParams() *tmproto.ConsensusParams { + return &tmproto.ConsensusParams{ + Block: DefaultBlockParams(), + Evidence: DefaultEvidenceParams(), + Validator: coretypes.DefaultValidatorParams(), + Version: tmproto.VersionParams{ + AppVersion: v1.Version, + }, + } +} + // DefaultBlockParams returns a default BlockParams with a MaxBytes determined // using a goal square size. func DefaultBlockParams() tmproto.BlockParams { diff --git a/app/prepare_proposal.go b/app/prepare_proposal.go index 1e19b65543..c8d06b664e 100644 --- a/app/prepare_proposal.go +++ b/app/prepare_proposal.go @@ -71,7 +71,7 @@ func (app *App) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePr // build the square from the set of valid and prioritised transactions. // The txs returned are the ones used in the square and block - dataSquare, txs, err := square.Build(txs, app.GetBaseApp().AppVersion(sdkCtx), app.GovSquareSizeUpperBound(sdkCtx)) + dataSquare, txs, err := square.Build(txs, app.GetBaseApp().AppVersion(), app.GovSquareSizeUpperBound(sdkCtx)) if err != nil { panic(err) } diff --git a/app/process_proposal.go b/app/process_proposal.go index 6c7c472223..0aaf791840 100644 --- a/app/process_proposal.go +++ b/app/process_proposal.go @@ -108,7 +108,7 @@ func (app *App) ProcessProposal(req abci.RequestProcessProposal) (resp abci.Resp } // Construct the data square from the block's transactions - dataSquare, err := square.Construct(req.BlockData.Txs, app.GetBaseApp().AppVersion(sdkCtx), app.GovSquareSizeUpperBound(sdkCtx)) + dataSquare, err := square.Construct(req.BlockData.Txs, app.GetBaseApp().AppVersion(), app.GovSquareSizeUpperBound(sdkCtx)) if err != nil { logInvalidPropBlockError(app.Logger(), req.Header, "failure to compute data square from transactions:", err) return reject() diff --git a/app/square_size.go b/app/square_size.go index 9af83aef55..f0c3825580 100644 --- a/app/square_size.go +++ b/app/square_size.go @@ -20,8 +20,8 @@ func (app *App) GovSquareSizeUpperBound(ctx sdk.Context) int { gmax := int(app.BlobKeeper.GovMaxSquareSize(ctx)) // perform a secondary check on the max square size. - if gmax > appconsts.SquareSizeUpperBound(app.AppVersion(ctx)) { - gmax = appconsts.SquareSizeUpperBound(app.AppVersion(ctx)) + if gmax > appconsts.SquareSizeUpperBound(app.AppVersion()) { + gmax = appconsts.SquareSizeUpperBound(app.AppVersion()) } return gmax diff --git a/app/test/max_total_blob_size_test.go b/app/test/max_total_blob_size_test.go index 2197259b6f..f497f364ae 100644 --- a/app/test/max_total_blob_size_test.go +++ b/app/test/max_total_blob_size_test.go @@ -49,7 +49,7 @@ func (s *MaxTotalBlobSizeSuite) SetupSuite() { tmConfig := testnode.DefaultTendermintConfig() tmConfig.Mempool.MaxTxBytes = 10 * mebibyte - cParams := testnode.DefaultParams() + cParams := testnode.DefaultConsensusParams() cParams.Block.MaxBytes = 10 * mebibyte cfg := testnode.DefaultConfig(). diff --git a/go.mod b/go.mod index d513fe7eb1..cfaa479e7b 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/celestiaorg/nmt v0.20.0 github.com/ethereum/go-ethereum v1.13.2 github.com/gogo/protobuf v1.3.3 - github.com/golang/protobuf v1.5.3 + github.com/golang/protobuf v1.5.4 github.com/google/uuid v1.4.0 // indirect github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -30,7 +30,7 @@ require ( github.com/celestiaorg/quantum-gravity-bridge/v2 v2.1.2 github.com/celestiaorg/rsmt2d v0.11.0 github.com/cosmos/cosmos-proto v1.0.0-alpha8 - github.com/cosmos/cosmos-sdk v0.46.16 + github.com/cosmos/cosmos-sdk v0.46.12 github.com/cosmos/gogoproto v1.4.11 github.com/cosmos/ibc-go/v6 v6.2.1 github.com/rs/zerolog v1.31.0 @@ -63,7 +63,6 @@ require ( github.com/onsi/gomega v1.27.4 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect - github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/supranational/blst v0.3.11 // indirect go.opentelemetry.io/otel v1.24.0 // indirect @@ -169,7 +168,6 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -179,9 +177,9 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rakyll/statik v0.1.7 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect @@ -205,13 +203,13 @@ require ( github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.152.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.33.0 gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.6 // indirect @@ -219,7 +217,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.20.1-sdk-v0.46.16 + github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16 // Pin to ledger-cosmos-go v0.12.4 to avoid a breaking change introduced in v0.13.0 // The following replace statement can be removed when we upgrade to cosmos-sdk >= v0.50.0 github.com/cosmos/ledger-cosmos-go => github.com/cosmos/ledger-cosmos-go v0.12.4 diff --git a/go.sum b/go.sum index 9a9fb4808f..3534b9afc3 100644 --- a/go.sum +++ b/go.sum @@ -316,8 +316,8 @@ github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOC github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/celestiaorg/celestia-core v1.36.0-tm-v0.34.29 h1:qLsTxyS0CHMuMO4S0ug1Zntv+gFB6OVdDNmG3pz5mHI= github.com/celestiaorg/celestia-core v1.36.0-tm-v0.34.29/go.mod h1:AL7kotb6ucKF4bpKKhGIzGhGL7dwYj2nFstiFC6PGxM= -github.com/celestiaorg/cosmos-sdk v1.20.1-sdk-v0.46.16 h1:9U9UthIJSOyVjabD5PkD6aczvqlWOyAFTOXw0duPT5k= -github.com/celestiaorg/cosmos-sdk v1.20.1-sdk-v0.46.16/go.mod h1:Tvsc3YnqvflXTYC8xIy/Q07Es95xZ1pZC/imoKogtbg= +github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16 h1:N2uETI13szEKnGAdKhtTR0EsrpcW0AwRKYER74WLnuw= +github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16/go.mod h1:Bpl1LSWiDpQumgOhhMTZBMopqa0j7fRasIhvTZB44P0= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA= github.com/celestiaorg/nmt v0.20.0 h1:9i7ultZ8Wv5ytt8ZRaxKQ5KOOMo4A2K2T/aPGjIlSas= @@ -636,8 +636,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -943,8 +943,6 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= @@ -1073,16 +1071,16 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1091,8 +1089,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1479,8 +1477,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1952,8 +1950,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/test/cmd/txsim/cli_test.go b/test/cmd/txsim/cli_test.go index 40091df841..2654c5f84a 100644 --- a/test/cmd/txsim/cli_test.go +++ b/test/cmd/txsim/cli_test.go @@ -57,7 +57,7 @@ func setup(t testing.TB) (keyring.Keyring, string, string) { t.Helper() // set the consensus params to allow for the max square size - cparams := testnode.DefaultParams() + cparams := testnode.DefaultConsensusParams() cparams.Block.MaxBytes = int64(appconsts.DefaultSquareSizeUpperBound*appconsts.DefaultSquareSizeUpperBound) * appconsts.ContinuationSparseShareContentSize cfg := testnode.DefaultConfig(). diff --git a/test/util/app_options.go b/test/util/app_options.go new file mode 100644 index 0000000000..c0e3374f42 --- /dev/null +++ b/test/util/app_options.go @@ -0,0 +1,12 @@ +package util + +import "github.com/cosmos/cosmos-sdk/server/types" + +// emptyAppOptions implements the AppOptions interface. +var _ types.AppOptions = emptyAppOptions{} + +type emptyAppOptions struct{} + +func (e emptyAppOptions) Get(_ string) interface{} { + return nil +} diff --git a/test/util/malicious/out_of_order_prepare.go b/test/util/malicious/out_of_order_prepare.go index 24a6bff664..ebc23bc31b 100644 --- a/test/util/malicious/out_of_order_prepare.go +++ b/test/util/malicious/out_of_order_prepare.go @@ -36,7 +36,7 @@ func (a *App) OutOfOrderPrepareProposal(req abci.RequestPrepareProposal) abci.Re // build the square from the set of valid and prioritised transactions. // The txs returned are the ones used in the square and block - dataSquare, txs, err := Build(txs, a.GetBaseApp().AppVersion(sdkCtx), a.GovSquareSizeUpperBound(sdkCtx), OutOfOrderExport) + dataSquare, txs, err := Build(txs, a.GetBaseApp().AppVersion(), a.GovSquareSizeUpperBound(sdkCtx), OutOfOrderExport) if err != nil { panic(err) } diff --git a/test/util/test_app.go b/test/util/test_app.go index ac0b0c0d5c..efa8e478fe 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -40,35 +40,26 @@ func init() { simapp.GetSimulatorFlags() } -type emptyAppOptions struct{} - -// Get implements AppOptions -func (ao emptyAppOptions) Get(_ string) interface{} { - return nil -} - // SetupTestAppWithGenesisValSet initializes a new app with a validator set and // genesis accounts that also act as delegators. For simplicity, each validator // is bonded with a delegation of one consensus engine unit in the default token // of the app from first genesis account. A no-op logger is set in app. func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts ...string) (*app.App, keyring.Keyring) { - // var cache sdk.MultiStorePersistentCache - // EmptyAppOptions is a stub implementing AppOptions - emptyOpts := emptyAppOptions{} - // var anteOpt = func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(nil) } - db := dbm.NewMemDB() + emptyOptions := emptyAppOptions{} skipUpgradeHeights := make(map[int64]bool) - - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + encodingConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...) testApp := app.New( - log.NewNopLogger(), db, nil, true, skipUpgradeHeights, - cast.ToString(emptyOpts.Get(flags.FlagHome)), - cast.ToUint(emptyOpts.Get(server.FlagInvCheckPeriod)), - encCfg, - emptyOpts, + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + skipUpgradeHeights, + cast.ToString(emptyOptions.Get(flags.FlagHome)), + cast.ToUint(emptyOptions.Get(server.FlagInvCheckPeriod)), + encodingConfig, + emptyOptions, ) - testApp.GetBaseApp().SetAppVersion(sdk.Context{}, appconsts.LatestVersion) genesisState, valSet, kr := GenesisStateWithSingleValidator(testApp, genAccounts...) @@ -86,6 +77,7 @@ func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts }, Evidence: &cparams.Evidence, Validator: &cparams.Validator, + Version: &cparams.Version, } genesisTime := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC() diff --git a/test/util/testnode/config.go b/test/util/testnode/config.go index 7cec330835..38d591a009 100644 --- a/test/util/testnode/config.go +++ b/test/util/testnode/config.go @@ -6,6 +6,7 @@ import ( "github.com/celestiaorg/celestia-app/cmd/celestia-appd/cmd" "github.com/celestiaorg/celestia-app/pkg/appconsts" + v1 "github.com/celestiaorg/celestia-app/pkg/appconsts/v1" "github.com/cosmos/cosmos-sdk/server" srvconfig "github.com/cosmos/cosmos-sdk/server/config" srvtypes "github.com/cosmos/cosmos-sdk/server/types" @@ -115,7 +116,7 @@ func DefaultConfig() *Config { WithAccounts([]string{}). WithChainID(tmrand.Str(6)). WithTendermintConfig(DefaultTendermintConfig()). - WithConsensusParams(DefaultParams()). + WithConsensusParams(DefaultConsensusParams()). WithAppOptions(DefaultAppOptions()). WithAppConfig(DefaultAppConfig()). WithGenesisOptions(). @@ -173,12 +174,25 @@ func DefaultAppOptions() *KVAppOptions { return opts } +// Deprecated: use DefaultConsensusParams instead. func DefaultParams() *tmproto.ConsensusParams { - cparams := types.DefaultConsensusParams() - cparams.Block.TimeIotaMs = 1 - cparams.Block.MaxBytes = appconsts.DefaultMaxBytes - cparams.Version.AppVersion = appconsts.LatestVersion - return cparams + return DefaultConsensusParams() +} + +func DefaultConsensusParams() *tmproto.ConsensusParams { + consensusParams := types.DefaultConsensusParams() + consensusParams.Block.TimeIotaMs = 1 + consensusParams.Block.MaxBytes = appconsts.DefaultMaxBytes + consensusParams.Version.AppVersion = appconsts.LatestVersion + return consensusParams +} + +func DefaultInitialConsensusParams() *tmproto.ConsensusParams { + consensusParams := types.DefaultConsensusParams() + consensusParams.Block.TimeIotaMs = 1 + consensusParams.Block.MaxBytes = appconsts.DefaultMaxBytes + consensusParams.Version.AppVersion = v1.Version + return consensusParams } func DefaultTendermintConfig() *tmconfig.Config { diff --git a/x/mint/test/mint_test.go b/x/mint/test/mint_test.go index f38d5514c6..fcd0eb6450 100644 --- a/x/mint/test/mint_test.go +++ b/x/mint/test/mint_test.go @@ -26,7 +26,7 @@ func (s *IntegrationTestSuite) SetupSuite() { t := s.T() t.Log("setting up mint integration test suite") - cparams := testnode.DefaultParams() + consensusParams := testnode.DefaultInitialConsensusParams() oneDay := time.Hour * 24 oneMonth := oneDay * 30 sixMonths := oneMonth * 6 @@ -37,12 +37,10 @@ func (s *IntegrationTestSuite) SetupSuite() { // // Note: if TimeIotaMs is removed from CometBFT, this technique will no // longer work. - cparams.Block.TimeIotaMs = int64(sixMonths.Milliseconds()) + consensusParams.Block.TimeIotaMs = int64(sixMonths.Milliseconds()) - cfg := testnode.DefaultConfig(). - WithConsensusParams(cparams) - - cctx, _, _ := testnode.NewNetwork(t, cfg) + config := testnode.DefaultConfig().WithConsensusParams(consensusParams) + cctx, _, _ := testnode.NewNetwork(t, config) s.cctx = cctx } diff --git a/x/upgrade/test/removal_test.go b/x/upgrade/test/removal_test.go index 8bfd0ca710..3e6dd7dfe3 100644 --- a/x/upgrade/test/removal_test.go +++ b/x/upgrade/test/removal_test.go @@ -10,9 +10,8 @@ import ( ) func TestRemoval(t *testing.T) { - app, _ := testutil.SetupTestAppWithGenesisValSet(app.DefaultConsensusParams()) - sftwrUpgrd := sdkupgradetypes.MsgSoftwareUpgrade{} - router := app.MsgServiceRouter() - handler := router.Handler(&sftwrUpgrd) + app, _ := testutil.SetupTestAppWithGenesisValSet(app.DefaultInitialConsensusParams()) + softwareUpgrade := sdkupgradetypes.MsgSoftwareUpgrade{} + handler := app.MsgServiceRouter().Handler(&softwareUpgrade) require.Nil(t, handler) } From 21b5bc747c8500e4888474df7d828e66c33f332d Mon Sep 17 00:00:00 2001 From: Rootul P Date: Tue, 4 Jun 2024 01:46:13 -0600 Subject: [PATCH 04/13] chore(deps): upgrade to nmt v0.21.0 (#3527) Manual backport of https://github.com/celestiaorg/celestia-app/pull/3526 --- go.mod | 4 ++-- go.sum | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index cfaa479e7b..25827b04c6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/celestiaorg/celestia-app go 1.22.3 require ( - github.com/celestiaorg/nmt v0.20.0 + github.com/celestiaorg/nmt v0.21.0 github.com/ethereum/go-ethereum v1.13.2 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.4 @@ -13,7 +13,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/spf13/cast v1.6.0 github.com/spf13/cobra v1.8.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/tendermint/tm-db v0.6.7 golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect diff --git a/go.sum b/go.sum index 3534b9afc3..0e9880815e 100644 --- a/go.sum +++ b/go.sum @@ -320,8 +320,8 @@ github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16 h1:N2uETI13szEKnGAdKhtTR0 github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16/go.mod h1:Bpl1LSWiDpQumgOhhMTZBMopqa0j7fRasIhvTZB44P0= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA= -github.com/celestiaorg/nmt v0.20.0 h1:9i7ultZ8Wv5ytt8ZRaxKQ5KOOMo4A2K2T/aPGjIlSas= -github.com/celestiaorg/nmt v0.20.0/go.mod h1:Oz15Ub6YPez9uJV0heoU4WpFctxazuIhKyUtaYNio7E= +github.com/celestiaorg/nmt v0.21.0 h1:81MBqxNn3orByoiCtdNVjwi5WsLgMkzHwP02ZMhTBHM= +github.com/celestiaorg/nmt v0.21.0/go.mod h1:ia/EpCk0enD5yO5frcxoNoFToz2Ghtk2i+blmCRjIY8= github.com/celestiaorg/quantum-gravity-bridge/v2 v2.1.2 h1:Q8nr5SAtDW5gocrBwqwDJcSS/JedqU58WwQA2SP+nXw= github.com/celestiaorg/quantum-gravity-bridge/v2 v2.1.2/go.mod h1:s/LzLUw0WeYPJ6qdk4q46jKLOq7rc9Z5Mdrxtfpcigw= github.com/celestiaorg/rsmt2d v0.11.0 h1:lcto/637WyTEZR3dLRoNvyuExfnUbxvdvKi3qz/2V4k= @@ -1185,8 +1185,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1197,8 +1198,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= @@ -1215,8 +1217,8 @@ github.com/tidwall/btree v1.5.0 h1:iV0yVY/frd7r6qGBXfEYs7DH0gTDgrKTrDjS7xt/IyQ= github.com/tidwall/btree v1.5.0/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= From 9c6322c28d3d3c7de7fe53e9e7a8ab95eb48be4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nina=20/=20=E1=83=9C=E1=83=98=E1=83=9C=E1=83=90?= Date: Mon, 10 Jun 2024 14:04:08 +0200 Subject: [PATCH 05/13] chore: add test/util/genesis on v1.x (#3520) ## Overview adding genesis to utils as part of #2414 --- test/util/genesis/accounts.go | 139 ++++++++++++++++++++++ test/util/genesis/document.go | 115 ++++++++++++++++++ test/util/genesis/files.go | 69 +++++++++++ test/util/genesis/genesis.go | 212 ++++++++++++++++++++++++++++++++++ test/util/genesis/modifier.go | 91 +++++++++++++++ test/util/genesis/util.go | 23 ++++ 6 files changed, 649 insertions(+) create mode 100644 test/util/genesis/accounts.go create mode 100644 test/util/genesis/document.go create mode 100644 test/util/genesis/files.go create mode 100644 test/util/genesis/genesis.go create mode 100644 test/util/genesis/modifier.go create mode 100644 test/util/genesis/util.go diff --git a/test/util/genesis/accounts.go b/test/util/genesis/accounts.go new file mode 100644 index 0000000000..6605949443 --- /dev/null +++ b/test/util/genesis/accounts.go @@ -0,0 +1,139 @@ +package genesis + +import ( + "fmt" + mrand "math/rand" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/crypto" +) + +type Account struct { + Name string + InitialTokens int64 +} + +func NewAccounts(initBal int64, names ...string) []Account { + accounts := make([]Account, len(names)) + for i, name := range names { + accounts[i] = Account{ + Name: name, + InitialTokens: initBal, + } + } + return accounts +} + +func (ga *Account) ValidateBasic() error { + if ga.Name == "" { + return fmt.Errorf("name cannot be empty") + } + if ga.InitialTokens <= 0 { + return fmt.Errorf("initial tokens must be positive") + } + return nil +} + +type Validator struct { + Account + Stake int64 + + // ConsensusKey is the key used by the validator to sign votes. + ConsensusKey crypto.PrivKey + NetworkKey crypto.PrivKey +} + +func NewDefaultValidator(name string) Validator { + r := mrand.New(mrand.NewSource(time.Now().UnixNano())) + return Validator{ + Account: Account{ + Name: name, + InitialTokens: 999_999_999_999_999_999, + }, + Stake: 99_999_999_999_999_999, // save some tokens for fees + ConsensusKey: GenerateEd25519(NewSeed(r)), + NetworkKey: GenerateEd25519(NewSeed(r)), + } +} + +// ValidateBasic performs stateless validation on the validitor +func (v *Validator) ValidateBasic() error { + if err := v.Account.ValidateBasic(); err != nil { + return err + } + if v.Stake <= 0 { + return fmt.Errorf("stake must be positive") + } + if v.ConsensusKey == nil { + return fmt.Errorf("consensus key cannot be empty") + } + if v.Stake > v.InitialTokens { + return fmt.Errorf("stake cannot be greater than initial tokens") + } + return nil +} + +// GenTx generates a genesis transaction to create a validator as configured by +// the validator struct. It assumes the validator's genesis account has already +// been added to the keyring and that the sequence for that account is 0. +func (v *Validator) GenTx(ecfg encoding.Config, kr keyring.Keyring, chainID string) (sdk.Tx, error) { + rec, err := kr.Key(v.Name) + if err != nil { + return nil, err + } + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + commission, err := sdk.NewDecFromStr("0.5") + if err != nil { + return nil, err + } + + pk, err := cryptocodec.FromTmPubKeyInterface(v.ConsensusKey.PubKey()) + if err != nil { + return nil, fmt.Errorf("converting public key for node %s: %w", v.Name, err) + } + + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + pk, + sdk.NewCoin(app.BondDenom, sdk.NewInt(v.Stake)), + stakingtypes.NewDescription(v.Name, "", "", "", ""), + stakingtypes.NewCommissionRates(commission, sdk.OneDec(), sdk.OneDec()), + sdk.NewInt(v.Stake/2), + ) + if err != nil { + return nil, err + } + + fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) + txBuilder := ecfg.TxConfig.NewTxBuilder() + err = txBuilder.SetMsgs(createValMsg) + if err != nil { + return nil, err + } + txBuilder.SetFeeAmount(fee) // Arbitrary fee + txBuilder.SetGasLimit(1000000) // Need at least 100386 + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(chainID). + WithKeybase(kr). + WithTxConfig(ecfg.TxConfig) + + err = tx.Sign(txFactory, v.Name, txBuilder, true) + if err != nil { + return nil, err + } + + return txBuilder.GetTx(), nil +} diff --git a/test/util/genesis/document.go b/test/util/genesis/document.go new file mode 100644 index 0000000000..4c18840dc1 --- /dev/null +++ b/test/util/genesis/document.go @@ -0,0 +1,115 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Document will create a valid genesis doc with funded addresses. +func Document( + ecfg encoding.Config, + params *tmproto.ConsensusParams, + chainID string, + gentxs []json.RawMessage, + addrs []string, + pubkeys []cryptotypes.PubKey, + mods ...Modifier, +) (*coretypes.GenesisDoc, error) { + genutilGenState := genutiltypes.DefaultGenesisState() + genutilGenState.GenTxs = gentxs + + genBals, genAccs, err := accountsToSDKTypes(addrs, pubkeys) + if err != nil { + return nil, err + } + + accounts, err := authtypes.PackAccounts(genAccs) + if err != nil { + return nil, err + } + + authGenState := authtypes.DefaultGenesisState() + bankGenState := banktypes.DefaultGenesisState() + authGenState.Accounts = append(authGenState.Accounts, accounts...) + bankGenState.Balances = append(bankGenState.Balances, genBals...) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + // perform some basic validation of the genesis state + if err := authtypes.ValidateGenesis(*authGenState); err != nil { + return nil, err + } + if err := bankGenState.Validate(); err != nil { + return nil, err + } + if err := genutiltypes.ValidateGenesis(genutilGenState, ecfg.TxConfig.TxJSONDecoder()); err != nil { + return nil, err + } + + state := app.ModuleBasics.DefaultGenesis(ecfg.Codec) + state[authtypes.ModuleName] = ecfg.Codec.MustMarshalJSON(authGenState) + state[banktypes.ModuleName] = ecfg.Codec.MustMarshalJSON(bankGenState) + state[genutiltypes.ModuleName] = ecfg.Codec.MustMarshalJSON(genutilGenState) + + for _, modifer := range mods { + state = modifer(state) + } + + stateBz, err := json.MarshalIndent(state, "", " ") + if err != nil { + return nil, err + } + + // Create the genesis doc + genesisDoc := &coretypes.GenesisDoc{ + ChainID: chainID, + GenesisTime: time.Now(), + ConsensusParams: params, + AppState: stateBz, + } + + return genesisDoc, nil +} + +// accountsToSDKTypes converts the genesis accounts to native SDK types. +func accountsToSDKTypes(addrs []string, pubkeys []cryptotypes.PubKey) ([]banktypes.Balance, []authtypes.GenesisAccount, error) { + if len(addrs) != len(pubkeys) { + return nil, nil, fmt.Errorf("length of addresses and public keys are not equal") + } + genBals := make([]banktypes.Balance, len(addrs)) + genAccs := make([]authtypes.GenesisAccount, len(addrs)) + hasMap := make(map[string]bool) + for i, addr := range addrs { + if hasMap[addr] { + return nil, nil, fmt.Errorf("duplicate account address %s", addr) + } + hasMap[addr] = true + + pubKey := pubkeys[i] + + balances := sdk.NewCoins( + sdk.NewCoin(appconsts.BondDenom, sdk.NewInt(999_999_999_999_999_999)), + ) + + genBals[i] = banktypes.Balance{Address: addr, Coins: balances.Sort()} + + parsedAddress, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return nil, nil, err + } + + genAccs[i] = authtypes.NewBaseAccount(parsedAddress, pubKey, uint64(i), 0) + } + return genBals, genAccs, nil +} diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go new file mode 100644 index 0000000000..3272b46537 --- /dev/null +++ b/test/util/genesis/files.go @@ -0,0 +1,69 @@ +package genesis + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/tendermint/tendermint/config" + tmos "github.com/tendermint/tendermint/libs/os" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/privval" +) + +// InitFiles initializes the files for a new tendermint node with the provided +// genesis. It will use the validatorIndex to save the validator's consensus +// key. +func InitFiles( + dir string, + tmCfg *config.Config, + g *Genesis, + validatorIndex int, +) (string, error) { + val, has := g.Validator(validatorIndex) + if !has { + return "", fmt.Errorf("validator %d not found", validatorIndex) + } + + basePath := filepath.Join(dir, ".celestia-app") + tmCfg.SetRoot(basePath) + + // save the genesis file + configPath := filepath.Join(basePath, "config") + err := os.MkdirAll(configPath, os.ModePerm) + if err != nil { + return "", err + } + gDoc, err := g.Export() + if err != nil { + return "", err + } + err = gDoc.SaveAs(tmCfg.GenesisFile()) + if err != nil { + return "", err + } + + pvStateFile := tmCfg.PrivValidatorStateFile() + if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + return "", err + } + pvKeyFile := tmCfg.PrivValidatorKeyFile() + if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { + return "", err + } + filePV := privval.NewFilePV(val.ConsensusKey, pvKeyFile, pvStateFile) + filePV.Save() + + nodeKeyFile := tmCfg.NodeKeyFile() + if err := tmos.EnsureDir(filepath.Dir(nodeKeyFile), 0o777); err != nil { + return "", err + } + nodeKey := &p2p.NodeKey{ + PrivKey: val.NetworkKey, + } + if err := nodeKey.SaveAs(nodeKeyFile); err != nil { + return "", err + } + + return basePath, nil +} diff --git a/test/util/genesis/genesis.go b/test/util/genesis/genesis.go new file mode 100644 index 0000000000..560d20291d --- /dev/null +++ b/test/util/genesis/genesis.go @@ -0,0 +1,212 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + coretypes "github.com/tendermint/tendermint/types" +) + +// Genesis manages the creation of the genesis state of a network. It is meant +// to be used as the first step to any test that requires a network. +type Genesis struct { + ecfg encoding.Config + // ConsensusParams are the consensus parameters of the network. + ConsensusParams *tmproto.ConsensusParams + // ChainID is the chain ID of the network. + ChainID string + // GenesisTime is the genesis time of the network. + GenesisTime time.Time + + // kr is the keyring used to generate the genesis accounts and validators. + // Transaction keys for all genesis accounts are stored in this keyring and + // are indexed by account name. Public keys and addresses can be derived + // from those keys using the existing keyring API. + kr keyring.Keyring + + // accounts are the genesis accounts that will be included in the genesis. + accounts []Account + // validators are the validators of the network. Note that each validator + // also has a genesis account. + validators []Validator + // genTxs are the genesis transactions that will be included in the genesis. + // Transactions are generated upon adding a validator to the genesis. + genTxs []sdk.Tx + genOps []Modifier +} + +// NewDefaultGenesis creates a new default genesis with no accounts or validators. +func NewDefaultGenesis() *Genesis { + ecfg := encoding.MakeConfig(app.ModuleBasics) + g := &Genesis{ + ecfg: ecfg, + ConsensusParams: DefaultConsensusParams(), + ChainID: tmrand.Str(6), + GenesisTime: time.Now(), + kr: keyring.NewInMemory(ecfg.Codec), + genOps: []Modifier{}, + } + return g +} + +func (g *Genesis) WithModifiers(ops ...Modifier) *Genesis { + g.genOps = append(g.genOps, ops...) + return g +} + +func (g *Genesis) WithConsensusParams(params *tmproto.ConsensusParams) *Genesis { + g.ConsensusParams = params + return g +} + +func (g *Genesis) WithChainID(chainID string) *Genesis { + g.ChainID = chainID + return g +} + +func (g *Genesis) WithGenesisTime(genesisTime time.Time) *Genesis { + g.GenesisTime = genesisTime + return g +} + +func (g *Genesis) WithValidators(vals ...Validator) *Genesis { + for _, val := range vals { + err := g.AddValidator(val) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) WithAccounts(accs ...Account) *Genesis { + for _, acc := range accs { + err := g.AddAccount(acc) + if err != nil { + panic(err) + } + } + return g +} + +func (g *Genesis) AddAccount(acc Account) error { + _, err := g.kr.Key(acc.Name) + if err == nil { + return fmt.Errorf("account with name %s already exists", acc.Name) + } + if err := acc.ValidateBasic(); err != nil { + return err + } + _, _, err = g.kr.NewMnemonic(acc.Name, keyring.English, "", "", hd.Secp256k1) + if err != nil { + return err + } + g.accounts = append(g.accounts, acc) + return nil +} + +func (g *Genesis) AddValidator(val Validator) error { + if err := val.ValidateBasic(); err != nil { + return err + } + + // Add the validator's genesis account + if err := g.AddAccount(val.Account); err != nil { + return err + } + + // Add the validator's genesis transaction + gentx, err := val.GenTx(g.ecfg, g.kr, g.ChainID) + if err != nil { + return err + } + + // install the validator + g.genTxs = append(g.genTxs, gentx) + g.validators = append(g.validators, val) + return nil +} + +func (g *Genesis) Accounts() []Account { + return g.accounts +} + +func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { + addrs := make([]string, 0, len(g.accounts)) + pubKeys := make([]cryptotypes.PubKey, 0, len(g.accounts)) + gentxs := make([]json.RawMessage, 0, len(g.genTxs)) + + for _, acc := range g.Accounts() { + rec, err := g.kr.Key(acc.Name) + if err != nil { + return nil, err + } + + addr, err := rec.GetAddress() + if err != nil { + return nil, err + } + + addrs = append(addrs, addr.String()) + + pubK, err := rec.GetPubKey() + if err != nil { + return nil, err + } + + pubKeys = append(pubKeys, pubK) + } + + for _, genTx := range g.genTxs { + bz, err := g.ecfg.TxConfig.TxJSONEncoder()(genTx) + if err != nil { + return nil, err + } + + gentxs = append(gentxs, json.RawMessage(bz)) + } + + return Document( + g.ecfg, + g.ConsensusParams, + g.ChainID, + gentxs, + addrs, + pubKeys, + g.genOps..., + ) +} + +func (g *Genesis) Keyring() keyring.Keyring { + return g.kr +} + +func (g *Genesis) Validators() []Validator { + return g.validators +} + +// Validator returns the validator at the given index. False is returned if the +// index is out of bounds. +func (g *Genesis) Validator(i int) (Validator, bool) { + if i < len(g.validators) { + return g.validators[i], true + } + return Validator{}, false +} + +func DefaultConsensusParams() *tmproto.ConsensusParams { + cparams := coretypes.DefaultConsensusParams() + cparams.Block.TimeIotaMs = 1 + cparams.Block.MaxBytes = appconsts.DefaultMaxBytes + return cparams +} diff --git a/test/util/genesis/modifier.go b/test/util/genesis/modifier.go new file mode 100644 index 0000000000..9ffe274d11 --- /dev/null +++ b/test/util/genesis/modifier.go @@ -0,0 +1,91 @@ +package genesis + +import ( + "encoding/json" + "time" + + "github.com/celestiaorg/celestia-app/app" + blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" + qgbtypes "github.com/celestiaorg/celestia-app/x/qgb/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" +) + +// Modifier allows for arbitrary changes to be made on the genesis state +// after initial accounts have been added. It accepts the genesis state as input +// and is expected to return the modified genesis as output. +type Modifier func(state map[string]json.RawMessage) map[string]json.RawMessage + +// SetBlobParams will set the provided blob params as genesis state. +func SetBlobParams(codec codec.Codec, params blobtypes.Params) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + blobGenState := blobtypes.DefaultGenesis() + blobGenState.Params = params + state[blobtypes.ModuleName] = codec.MustMarshalJSON(blobGenState) + return state + } +} + +// ImmediateProposals sets the thresholds for getting a gov proposal to very low +// levels. +func ImmediateProposals(codec codec.Codec) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + gs := v1.DefaultGenesisState() + gs.DepositParams.MinDeposit = sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) + gs.TallyParams.Quorum = "0.000001" + gs.TallyParams.Threshold = "0.000001" + vp := time.Second * 5 + gs.VotingParams.VotingPeriod = &vp + state[govtypes.ModuleName] = codec.MustMarshalJSON(gs) + return state + } +} + +// SetDataCommitmentWindow will set the provided data commitment window in the +// qgb module's genesis state. +func SetDataCommitmentWindow(codec codec.Codec, window uint64) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + qgbGenState := qgbtypes.DefaultGenesis() + qgbGenState.Params.DataCommitmentWindow = window + state[qgbtypes.ModuleName] = codec.MustMarshalJSON(qgbGenState) + return state + } +} + +// FundAccounts adds a set of accounts to the genesis and then sets their balance as provided. +// This is good in the case where you have a separate keyring you want to test against and not +// use the one generated by the testnet infra. +func FundAccounts(codec codec.Codec, addresses []sdk.AccAddress, balance sdk.Coin) Modifier { + return func(state map[string]json.RawMessage) map[string]json.RawMessage { + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + codec.MustUnmarshalJSON(state[authtypes.ModuleName], &authGenState) + + genAccounts := make([]authtypes.GenesisAccount, len(addresses)) + genBalances := make([]banktypes.Balance, len(addresses)) + for idx, addr := range addresses { + genAccounts[idx] = authtypes.NewBaseAccount(addr, nil, uint64(idx+len(authGenState.Accounts)), 0) + genBalances[idx] = banktypes.Balance{Address: addr.String(), Coins: sdk.NewCoins(balance)} + } + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + panic(err) + } + + authGenState.Accounts = append(authGenState.Accounts, accounts...) + state[authtypes.ModuleName] = codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + codec.MustUnmarshalJSON(state[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = append(bankGenState.Balances, genBalances...) + state[banktypes.ModuleName] = codec.MustMarshalJSON(&bankGenState) + return state + } +} diff --git a/test/util/genesis/util.go b/test/util/genesis/util.go new file mode 100644 index 0000000000..42d11288d9 --- /dev/null +++ b/test/util/genesis/util.go @@ -0,0 +1,23 @@ +package genesis + +import ( + "io" + mrand "math/rand" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func NewSeed(r *mrand.Rand) []byte { + seed := make([]byte, ed25519.SeedSize) + + _, err := io.ReadFull(r, seed) + if err != nil { + panic(err) // this shouldn't happen + } + return seed +} + +func GenerateEd25519(seed []byte) crypto.PrivKey { + return ed25519.GenPrivKeyFromSecret(seed) +} From 6acb97f8f942a11d60d899ffaabca03b8e66fe79 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 19 Jun 2024 18:35:27 +0200 Subject: [PATCH 06/13] fix: usage of default address and account in tx client (#3597) This ports a bug that was caught in `main` whereby setting either the default address or default account name in the tx client does not automatically set the other leading in a potential incoherence between the two --- pkg/user/tx_client.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/user/tx_client.go b/pkg/user/tx_client.go index e7d47d4823..e5599664bd 100644 --- a/pkg/user/tx_client.go +++ b/pkg/user/tx_client.go @@ -50,15 +50,22 @@ func WithDefaultAddress(address sdktypes.AccAddress) Option { panic(err) } c.defaultAccount = record.Name + c.defaultAddress = address } } func WithDefaultAccount(name string) Option { return func(c *TxClient) { - if _, err := c.signer.keys.Key(name); err != nil { + rec, err := c.signer.keys.Key(name) + if err != nil { + panic(err) + } + addr, err := rec.GetAddress() + if err != nil { panic(err) } c.defaultAccount = name + c.defaultAddress = addr } } From ab32bfa6555ff1eb7951cfa2ec2f76d34c3b7597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nina=20/=20=E1=83=9C=E1=83=98=E1=83=9C=E1=83=90?= Date: Fri, 21 Jun 2024 11:14:14 +0200 Subject: [PATCH 07/13] test(v1.x): consistent appHash between commits (#3522) ## Overview Adds consistent appHash test on v1.x --- app/consistent_apphash_test.go | 208 +++++++++++++++++++++++++++++ test/util/blobfactory/test_util.go | 13 ++ test/util/genesis/accounts.go | 40 +++--- test/util/genesis/document.go | 69 +++++----- test/util/genesis/files.go | 2 +- test/util/genesis/genesis.go | 129 +++++++++--------- test/util/test_app.go | 149 ++++++++++++++++++++- 7 files changed, 502 insertions(+), 108 deletions(-) create mode 100644 app/consistent_apphash_test.go diff --git a/app/consistent_apphash_test.go b/app/consistent_apphash_test.go new file mode 100644 index 0000000000..44c4272964 --- /dev/null +++ b/app/consistent_apphash_test.go @@ -0,0 +1,208 @@ +package app_test + +import ( + "fmt" + "testing" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/pkg/appconsts" + appns "github.com/celestiaorg/celestia-app/pkg/namespace" + "github.com/celestiaorg/celestia-app/pkg/user" + testutil "github.com/celestiaorg/celestia-app/test/util" + "github.com/celestiaorg/celestia-app/test/util/blobfactory" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/proto/tendermint/version" + coretypes "github.com/tendermint/tendermint/types" +) + +type SdkTx struct { + sdkMsgs []sdk.Msg + txOptions []user.TxOption +} + +type BlobTx struct { + author string + blobs []*tmproto.Blob + txOptions []user.TxOption +} + +// TestConsistentAppHash executes a set of txs, generates an app hash, +// and compares it against a previously generated hash from the same set of transactions. +// App hashes across different commits should be consistent. +func TestConsistentAppHash(t *testing.T) { + // App hash produced from executing txs on this branch + expectedAppHash := []byte{9, 208, 117, 101, 108, 61, 146, 58, 26, 190, 199, 124, 76, 178, 84, 74, 54, 159, 76, 187, 2, 169, 128, 87, 70, 78, 8, 192, 28, 144, 116, 117} + + // Initialize testApp + testApp := testutil.NewTestApp() + + enc := encoding.MakeConfig(app.ModuleEncodingRegisters...) + // Create deterministic keys + kr, pubKeys := deterministicKeyRing(enc.Codec) + + recs, err := kr.List() + require.NoError(t, err) + accountNames := make([]string, 0, len(recs)) + + // Get the name of the records + for _, rec := range recs { + accountNames = append(accountNames, rec.Name) + } + + // Apply genesis state to the app. + _, _, err = testutil.SetupDeterministicGenesisState(testApp, pubKeys, 1_000_000_000, app.DefaultInitialConsensusParams()) + require.NoError(t, err) + + // Query keyring account infos + accountInfos := queryAccountInfo(testApp, accountNames, kr) + + // Create accounts for the signer + accounts := make([]*user.Account, 0, len(accountInfos)) + for i, accountInfo := range accountInfos { + account := user.NewAccount(accountNames[i], accountInfo.AccountNum, accountInfo.Sequence) + accounts = append(accounts, account) + } + + // Create a signer with keyring accounts + signer, err := user.NewTxSigner(kr, enc.TxConfig, testutil.ChainID, appconsts.LatestVersion, accounts...) + require.NoError(t, err) + + amount := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewIntFromUint64(1000))) + + // Create an SDK Tx + sdkTx := SdkTx{ + sdkMsgs: []sdk.Msg{ + banktypes.NewMsgSend(signer.Account(accountNames[0]).Address(), + signer.Account(accountNames[1]).Address(), + amount), + }, + txOptions: blobfactory.DefaultTxOpts(), + } + + // Create a Blob Tx + blobTx := BlobTx{ + author: accountNames[2], + blobs: []*tmproto.Blob{New(fixedNamespace(), []byte{1}, appconsts.DefaultShareVersion)}, + txOptions: blobfactory.DefaultTxOpts(), + } + + // Create SDK Tx + rawSdkTx, err := signer.CreateTx(sdkTx.sdkMsgs, sdkTx.txOptions...) + require.NoError(t, err) + + // Create Blob Tx + rawBlobTx, _, err := signer.CreatePayForBlobs(blobTx.author, blobTx.blobs, blobTx.txOptions...) + require.NoError(t, err) + + // BeginBlock + header := tmproto.Header{ + Version: version.Consensus{App: 1}, + Height: testApp.LastBlockHeight() + 1, + } + testApp.BeginBlock(abci.RequestBeginBlock{Header: header}) + + // Deliver SDK Tx + resp := testApp.DeliverTx(abci.RequestDeliverTx{Tx: rawSdkTx}) + require.EqualValues(t, 0, resp.Code, resp.Log) + + // Deliver Blob Tx + blob, isBlobTx := coretypes.UnmarshalBlobTx(rawBlobTx) + require.True(t, isBlobTx) + resp = testApp.DeliverTx(abci.RequestDeliverTx{Tx: blob.Tx}) + require.EqualValues(t, 0, resp.Code, resp.Log) + + // EndBlock + testApp.EndBlock(abci.RequestEndBlock{Height: header.Height}) + + // Commit the state + testApp.Commit() + + // Get the app hash + appHash := testApp.LastCommitID().Hash + + // Require that the app hash is equal to the app hash produced on a different commit + require.Equal(t, expectedAppHash, appHash) +} + +// fixedNamespace returns a hardcoded namespace +func fixedNamespace() appns.Namespace { + return appns.Namespace{ + Version: 0, + ID: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 67, 154, 200, 228, 130, 74, 147, 162, 11}, + } +} + +// deterministicKeyRing returns a deterministic keyring and a list of deterministic public keys +func deterministicKeyRing(cdc codec.Codec) (keyring.Keyring, []types.PubKey) { + mnemonics := []string{ + "great myself congress genuine scale muscle view uncover pipe miracle sausage broccoli lonely swap table foam brand turtle comic gorilla firm mad grunt hazard", + "cheap job month trigger flush cactus chest juice dolphin people limit crunch curious secret object beach shield snake hunt group sketch cousin puppy fox", + "oil suffer bamboo one better attack exist dolphin relief enforce cat asset raccoon lava regret found love certain plunge grocery accuse goat together kiss", + "giraffe busy subject doll jump drama sea daring again club spend toe mind organ real liar permit refuse change opinion donkey job cricket speed", + "fee vapor thing fish fan memory negative raven cram win quantum ozone job mirror shoot sting quiz black apart funny sort cancel friend curtain", + "skin beef review pilot tooth act any alarm there only kick uniform ticket material cereal radar ethics list unlock method coral smooth street frequent", + "ecology scout core guard load oil school effort near alcohol fancy save cereal owner enforce impact sand husband trophy solve amount fish festival sell", + "used describe angle twin amateur pyramid bitter pool fluid wing erode rival wife federal curious drink battle put elbow mandate another token reveal tone", + "reason fork target chimney lift typical fine divorce mixture web robot kiwi traffic stove miss crane welcome camp bless fuel october riot pluck ordinary", + "undo logic mobile modify master force donor rose crumble forget plate job canal waste turn damp sure point deposit hazard quantum car annual churn", + "charge subway treat loop donate place loan want grief leg message siren joy road exclude match empty enforce vote meadow enlist vintage wool involve", + } + kb := keyring.NewInMemory(cdc) + pubKeys := make([]types.PubKey, len(mnemonics)) + for idx, mnemonic := range mnemonics { + rec, err := kb.NewAccount(fmt.Sprintf("account-%d", idx), mnemonic, "", "", hd.Secp256k1) + if err != nil { + panic(err) + } + pubKey, err := rec.GetPubKey() + if err != nil { + panic(err) + } + pubKeys[idx] = pubKey + } + return kb, pubKeys +} + +func getAddress(account string, kr keyring.Keyring) sdk.AccAddress { + rec, err := kr.Key(account) + if err != nil { + panic(err) + } + addr, err := rec.GetAddress() + if err != nil { + panic(err) + } + return addr +} + +func queryAccountInfo(capp *app.App, accs []string, kr keyring.Keyring) []blobfactory.AccountInfo { + infos := make([]blobfactory.AccountInfo, len(accs)) + for i, acc := range accs { + addr := getAddress(acc, kr) + accI := testutil.DirectQueryAccount(capp, addr) + infos[i] = blobfactory.AccountInfo{ + AccountNum: accI.GetAccountNumber(), + Sequence: accI.GetSequence(), + } + } + return infos +} + +// New creates a new tmproto.Blob from the provided data +func New(ns appns.Namespace, blob []byte, shareVersion uint8) *tmproto.Blob { + return &tmproto.Blob{ + NamespaceId: ns.ID, + Data: blob, + ShareVersion: uint32(shareVersion), + NamespaceVersion: uint32(ns.Version), + } +} diff --git a/test/util/blobfactory/test_util.go b/test/util/blobfactory/test_util.go index 8406e51907..99f4f79fed 100644 --- a/test/util/blobfactory/test_util.go +++ b/test/util/blobfactory/test_util.go @@ -2,6 +2,7 @@ package blobfactory import ( "github.com/celestiaorg/celestia-app/pkg/appconsts" + "github.com/celestiaorg/celestia-app/pkg/user" "github.com/celestiaorg/celestia-app/test/util/testfactory" blobtypes "github.com/celestiaorg/celestia-app/x/blob/types" "github.com/cosmos/cosmos-sdk/client" @@ -11,6 +12,18 @@ import ( coretypes "github.com/tendermint/tendermint/types" ) +func DefaultTxOpts() []user.TxOption { + return FeeTxOpts(10_000_000) +} + +func FeeTxOpts(gas uint64) []user.TxOption { + fee := uint64(float64(gas)*appconsts.DefaultMinGasPrice) + 1 + return []user.TxOption{ + user.SetFee(fee), + user.SetGasLimit(gas), + } +} + func GenerateManyRawSendTxs(txConfig client.TxConfig, count int) []coretypes.Tx { const acc = "signer" kr := testfactory.GenerateKeyring(acc) diff --git a/test/util/genesis/accounts.go b/test/util/genesis/accounts.go index 6605949443..a62b009f43 100644 --- a/test/util/genesis/accounts.go +++ b/test/util/genesis/accounts.go @@ -1,6 +1,7 @@ package genesis import ( + "errors" "fmt" mrand "math/rand" "time" @@ -15,15 +16,22 @@ import ( "github.com/tendermint/tendermint/crypto" ) -type Account struct { +const ( + DefaultInitialBalance = 1e15 // 1 billion TIA +) + +// KeyringAccount represents a user account on the Celestia network. +// Either the name, if using the genesis keyring, or an address +// needs to be provided +type KeyringAccount struct { Name string InitialTokens int64 } -func NewAccounts(initBal int64, names ...string) []Account { - accounts := make([]Account, len(names)) +func NewKeyringAccounts(initBal int64, names ...string) []KeyringAccount { + accounts := make([]KeyringAccount, len(names)) for i, name := range names { - accounts[i] = Account{ + accounts[i] = KeyringAccount{ Name: name, InitialTokens: initBal, } @@ -31,18 +39,18 @@ func NewAccounts(initBal int64, names ...string) []Account { return accounts } -func (ga *Account) ValidateBasic() error { +func (ga *KeyringAccount) ValidateBasic() error { if ga.Name == "" { - return fmt.Errorf("name cannot be empty") + return errors.New("name cannot be empty") } if ga.InitialTokens <= 0 { - return fmt.Errorf("initial tokens must be positive") + return errors.New("initial tokens must be positive") } return nil } type Validator struct { - Account + KeyringAccount Stake int64 // ConsensusKey is the key used by the validator to sign votes. @@ -53,11 +61,11 @@ type Validator struct { func NewDefaultValidator(name string) Validator { r := mrand.New(mrand.NewSource(time.Now().UnixNano())) return Validator{ - Account: Account{ + KeyringAccount: KeyringAccount{ Name: name, - InitialTokens: 999_999_999_999_999_999, + InitialTokens: DefaultInitialBalance, }, - Stake: 99_999_999_999_999_999, // save some tokens for fees + Stake: DefaultInitialBalance / 2, // save some tokens for fees ConsensusKey: GenerateEd25519(NewSeed(r)), NetworkKey: GenerateEd25519(NewSeed(r)), } @@ -65,17 +73,17 @@ func NewDefaultValidator(name string) Validator { // ValidateBasic performs stateless validation on the validitor func (v *Validator) ValidateBasic() error { - if err := v.Account.ValidateBasic(); err != nil { + if err := v.KeyringAccount.ValidateBasic(); err != nil { return err } if v.Stake <= 0 { - return fmt.Errorf("stake must be positive") + return errors.New("stake must be positive") } if v.ConsensusKey == nil { - return fmt.Errorf("consensus key cannot be empty") + return errors.New("consensus key cannot be empty") } if v.Stake > v.InitialTokens { - return fmt.Errorf("stake cannot be greater than initial tokens") + return errors.New("stake cannot be greater than initial tokens") } return nil } @@ -115,7 +123,7 @@ func (v *Validator) GenTx(ecfg encoding.Config, kr keyring.Keyring, chainID stri return nil, err } - fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))) + fee := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(20000))) txBuilder := ecfg.TxConfig.NewTxBuilder() err = txBuilder.SetMsgs(createValMsg) if err != nil { diff --git a/test/util/genesis/document.go b/test/util/genesis/document.go index 4c18840dc1..6973025ea5 100644 --- a/test/util/genesis/document.go +++ b/test/util/genesis/document.go @@ -23,26 +23,25 @@ func Document( params *tmproto.ConsensusParams, chainID string, gentxs []json.RawMessage, - addrs []string, - pubkeys []cryptotypes.PubKey, + accounts []Account, mods ...Modifier, ) (*coretypes.GenesisDoc, error) { genutilGenState := genutiltypes.DefaultGenesisState() genutilGenState.GenTxs = gentxs - genBals, genAccs, err := accountsToSDKTypes(addrs, pubkeys) + genBals, genAccs, err := accountsToSDKTypes(accounts) if err != nil { - return nil, err + return nil, fmt.Errorf("converting accounts into sdk types: %w", err) } - accounts, err := authtypes.PackAccounts(genAccs) + sdkAccounts, err := authtypes.PackAccounts(genAccs) if err != nil { - return nil, err + return nil, fmt.Errorf("packing accounts: %w", err) } authGenState := authtypes.DefaultGenesisState() bankGenState := banktypes.DefaultGenesisState() - authGenState.Accounts = append(authGenState.Accounts, accounts...) + authGenState.Accounts = append(authGenState.Accounts, sdkAccounts...) bankGenState.Balances = append(bankGenState.Balances, genBals...) bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) @@ -58,17 +57,18 @@ func Document( } state := app.ModuleBasics.DefaultGenesis(ecfg.Codec) + state[authtypes.ModuleName] = ecfg.Codec.MustMarshalJSON(authGenState) state[banktypes.ModuleName] = ecfg.Codec.MustMarshalJSON(bankGenState) state[genutiltypes.ModuleName] = ecfg.Codec.MustMarshalJSON(genutilGenState) - for _, modifer := range mods { - state = modifer(state) + for _, modifier := range mods { + state = modifier(state) } stateBz, err := json.MarshalIndent(state, "", " ") if err != nil { - return nil, err + return nil, fmt.Errorf("marshalling genesis state: %w", err) } // Create the genesis doc @@ -83,33 +83,42 @@ func Document( } // accountsToSDKTypes converts the genesis accounts to native SDK types. -func accountsToSDKTypes(addrs []string, pubkeys []cryptotypes.PubKey) ([]banktypes.Balance, []authtypes.GenesisAccount, error) { - if len(addrs) != len(pubkeys) { - return nil, nil, fmt.Errorf("length of addresses and public keys are not equal") - } - genBals := make([]banktypes.Balance, len(addrs)) - genAccs := make([]authtypes.GenesisAccount, len(addrs)) - hasMap := make(map[string]bool) - for i, addr := range addrs { - if hasMap[addr] { +func accountsToSDKTypes(accounts []Account) ([]banktypes.Balance, []authtypes.GenesisAccount, error) { + genBals := make([]banktypes.Balance, len(accounts)) + genAccs := make([]authtypes.GenesisAccount, len(accounts)) + hasMap := make(map[string]struct{}) + for i, account := range accounts { + if err := account.ValidateBasic(); err != nil { + return nil, nil, fmt.Errorf("invalid account %d: %v", i, err) + } + addr := sdk.AccAddress(account.PubKey.Address()) + if _, ok := hasMap[addr.String()]; ok { return nil, nil, fmt.Errorf("duplicate account address %s", addr) } - hasMap[addr] = true - - pubKey := pubkeys[i] + hasMap[addr.String()] = struct{}{} balances := sdk.NewCoins( - sdk.NewCoin(appconsts.BondDenom, sdk.NewInt(999_999_999_999_999_999)), + sdk.NewCoin(appconsts.BondDenom, sdk.NewInt(account.Balance)), ) - genBals[i] = banktypes.Balance{Address: addr, Coins: balances.Sort()} + genBals[i] = banktypes.Balance{Address: addr.String(), Coins: balances.Sort()} - parsedAddress, err := sdk.AccAddressFromBech32(addr) - if err != nil { - return nil, nil, err - } - - genAccs[i] = authtypes.NewBaseAccount(parsedAddress, pubKey, uint64(i), 0) + genAccs[i] = authtypes.NewBaseAccount(addr, account.PubKey, uint64(i), 0) } return genBals, genAccs, nil } + +type Account struct { + PubKey cryptotypes.PubKey + Balance int64 +} + +func (ga Account) ValidateBasic() error { + if ga.PubKey == nil { + return fmt.Errorf("pubkey cannot be empty") + } + if ga.Balance <= 0 { + return fmt.Errorf("balance must be greater than 0") + } + return nil +} diff --git a/test/util/genesis/files.go b/test/util/genesis/files.go index 3272b46537..c8de7eec03 100644 --- a/test/util/genesis/files.go +++ b/test/util/genesis/files.go @@ -36,7 +36,7 @@ func InitFiles( } gDoc, err := g.Export() if err != nil { - return "", err + return "", fmt.Errorf("exporting genesis: %w", err) } err = gDoc.SaveAs(tmCfg.GenesisFile()) if err != nil { diff --git a/test/util/genesis/genesis.go b/test/util/genesis/genesis.go index 560d20291d..a886e58195 100644 --- a/test/util/genesis/genesis.go +++ b/test/util/genesis/genesis.go @@ -1,16 +1,15 @@ package genesis import ( + "bytes" "encoding/json" "fmt" "time" "github.com/celestiaorg/celestia-app/app" "github.com/celestiaorg/celestia-app/app/encoding" - "github.com/celestiaorg/celestia-app/pkg/appconsts" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -45,12 +44,27 @@ type Genesis struct { genOps []Modifier } +// Accounts getter +func (g *Genesis) Accounts() []Account { + return g.accounts +} + +// Keyring getter +func (g *Genesis) Keyring() keyring.Keyring { + return g.kr +} + +// Validators getter +func (g *Genesis) Validators() []Validator { + return g.validators +} + // NewDefaultGenesis creates a new default genesis with no accounts or validators. func NewDefaultGenesis() *Genesis { ecfg := encoding.MakeConfig(app.ModuleBasics) g := &Genesis{ ecfg: ecfg, - ConsensusParams: DefaultConsensusParams(), + ConsensusParams: app.DefaultConsensusParams(), ChainID: tmrand.Str(6), GenesisTime: time.Now(), kr: keyring.NewInMemory(ecfg.Codec), @@ -59,29 +73,34 @@ func NewDefaultGenesis() *Genesis { return g } +// WithModifier adds a genesis modifier to the genesis. func (g *Genesis) WithModifiers(ops ...Modifier) *Genesis { g.genOps = append(g.genOps, ops...) return g } +// WithConsensusParams sets the consensus parameters of the genesis. func (g *Genesis) WithConsensusParams(params *tmproto.ConsensusParams) *Genesis { g.ConsensusParams = params return g } +// WithChainID sets the chain ID of the genesis. func (g *Genesis) WithChainID(chainID string) *Genesis { g.ChainID = chainID return g } +// WithGenesisTime sets the genesis time of the genesis. func (g *Genesis) WithGenesisTime(genesisTime time.Time) *Genesis { g.GenesisTime = genesisTime return g } +// WithAccounts adds the given validators to the genesis. func (g *Genesis) WithValidators(vals ...Validator) *Genesis { for _, val := range vals { - err := g.AddValidator(val) + err := g.NewValidator(val) if err != nil { panic(err) } @@ -89,9 +108,11 @@ func (g *Genesis) WithValidators(vals ...Validator) *Genesis { return g } -func (g *Genesis) WithAccounts(accs ...Account) *Genesis { +// WithKeyringAccounts adds the given keyring accounts to the genesis. If an +// account with the same name already exists, it panics. +func (g *Genesis) WithKeyringAccounts(accs ...KeyringAccount) *Genesis { for _, acc := range accs { - err := g.AddAccount(acc) + err := g.NewAccount(acc) if err != nil { panic(err) } @@ -99,32 +120,53 @@ func (g *Genesis) WithAccounts(accs ...Account) *Genesis { return g } -func (g *Genesis) AddAccount(acc Account) error { - _, err := g.kr.Key(acc.Name) - if err == nil { - return fmt.Errorf("account with name %s already exists", acc.Name) +// AddAccount adds an existing account to the genesis. +func (g *Genesis) AddAccount(account Account) error { + for _, acc := range g.accounts { + if bytes.Equal(acc.PubKey.Bytes(), account.PubKey.Bytes()) { + return fmt.Errorf("account with pubkey %s already exists", account.PubKey.String()) + } } + g.accounts = append(g.accounts, account) + return nil +} + +// NewAccount creates a new account and adds it to the genesis. +func (g *Genesis) NewAccount(acc KeyringAccount) error { if err := acc.ValidateBasic(); err != nil { return err } - _, _, err = g.kr.NewMnemonic(acc.Name, keyring.English, "", "", hd.Secp256k1) + // check that the account does not already exist + if _, err := g.kr.Key(acc.Name); err == nil { + return fmt.Errorf("account with name %s already exists", acc.Name) + } + + // generate the keys and add it to the genesis keyring + record, _, err := g.kr.NewMnemonic(acc.Name, keyring.English, "", "", hd.Secp256k1) + if err != nil { + return err + } + + pubKey, err := record.GetPubKey() if err != nil { return err } - g.accounts = append(g.accounts, acc) + + account := Account{ + PubKey: pubKey, + Balance: acc.InitialTokens, + } + + g.accounts = append(g.accounts, account) return nil } +// AddValidator verifies and adds a given validator to the genesis. func (g *Genesis) AddValidator(val Validator) error { if err := val.ValidateBasic(); err != nil { return err } - // Add the validator's genesis account - if err := g.AddAccount(val.Account); err != nil { - return err - } - // Add the validator's genesis transaction gentx, err := val.GenTx(g.ecfg, g.kr, g.ChainID) if err != nil { @@ -137,36 +179,19 @@ func (g *Genesis) AddValidator(val Validator) error { return nil } -func (g *Genesis) Accounts() []Account { - return g.accounts +// Creates a new validator account and adds it to the genesis. +func (g *Genesis) NewValidator(val Validator) error { + // Add the validator's genesis account + if err := g.NewAccount(val.KeyringAccount); err != nil { + return err + } + + return g.AddValidator(val) } +// Export returns the genesis document of the network. func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { - addrs := make([]string, 0, len(g.accounts)) - pubKeys := make([]cryptotypes.PubKey, 0, len(g.accounts)) gentxs := make([]json.RawMessage, 0, len(g.genTxs)) - - for _, acc := range g.Accounts() { - rec, err := g.kr.Key(acc.Name) - if err != nil { - return nil, err - } - - addr, err := rec.GetAddress() - if err != nil { - return nil, err - } - - addrs = append(addrs, addr.String()) - - pubK, err := rec.GetPubKey() - if err != nil { - return nil, err - } - - pubKeys = append(pubKeys, pubK) - } - for _, genTx := range g.genTxs { bz, err := g.ecfg.TxConfig.TxJSONEncoder()(genTx) if err != nil { @@ -181,20 +206,11 @@ func (g *Genesis) Export() (*coretypes.GenesisDoc, error) { g.ConsensusParams, g.ChainID, gentxs, - addrs, - pubKeys, + g.accounts, g.genOps..., ) } -func (g *Genesis) Keyring() keyring.Keyring { - return g.kr -} - -func (g *Genesis) Validators() []Validator { - return g.validators -} - // Validator returns the validator at the given index. False is returned if the // index is out of bounds. func (g *Genesis) Validator(i int) (Validator, bool) { @@ -203,10 +219,3 @@ func (g *Genesis) Validator(i int) (Validator, bool) { } return Validator{}, false } - -func DefaultConsensusParams() *tmproto.ConsensusParams { - cparams := coretypes.DefaultConsensusParams() - cparams.Block.TimeIotaMs = 1 - cparams.Block.MaxBytes = appconsts.DefaultMaxBytes - return cparams -} diff --git a/test/util/test_app.go b/test/util/test_app.go index efa8e478fe..7371d43e5e 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -28,11 +28,16 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" + "github.com/celestiaorg/celestia-app/test/util/genesis" + "github.com/cosmos/cosmos-sdk/crypto/hd" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/tendermint/tendermint/crypto/ed25519" + tmversion "github.com/tendermint/tendermint/proto/tendermint/version" ) const ( - ChainID = "testapp" + ChainID = "test-app" ) // Get flags every time the simulator is run @@ -40,6 +45,106 @@ func init() { simapp.GetSimulatorFlags() } +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(_ string) interface{} { + return nil +} + +// NewTestApp initializes a new app with a no-op logger and in-memory database. +func NewTestApp() *app.App { + // EmptyAppOptions is a stub implementing AppOptions + emptyOpts := EmptyAppOptions{} + db := dbm.NewMemDB() + + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + + return app.New( + log.NewNopLogger(), + db, + nil, + true, + nil, + "", + cast.ToUint(emptyOpts.Get(server.FlagInvCheckPeriod)), + encCfg, + emptyOpts, + ) +} + +// SetupDeterministicGenesisState sets genesis on initialized testApp with the provided arguments. +func SetupDeterministicGenesisState(testApp *app.App, pubKeys []cryptotypes.PubKey, balance int64, cparams *tmproto.ConsensusParams) (keyring.Keyring, []genesis.Account, error) { + // Create genesis + gen := genesis.NewDefaultGenesis(). + WithChainID(ChainID). + WithConsensusParams(cparams). + WithGenesisTime(time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC()) + + // Add accounts to genesis + for _, pk := range pubKeys { + err := gen.AddAccount(genesis.Account{ + PubKey: pk, + Balance: balance, + }) + if err != nil { + return nil, nil, err + } + } + + // Add validator to genesis + err := AddDeterministicValidatorToGenesis(gen) + if err != nil { + return nil, nil, fmt.Errorf("failed to add validator: %w", err) + } + + genDoc, err := gen.Export() + if err != nil { + return nil, nil, fmt.Errorf("failed to export genesis doc: %w", err) + } + + // Initialise test app against genesis + testApp.Info(abci.RequestInfo{}) + + abciParams := &abci.ConsensusParams{ + Block: &abci.BlockParams{ + // choose some value large enough to not bottleneck the max square + // size + MaxBytes: int64(appconsts.DefaultSquareSizeUpperBound*appconsts.DefaultSquareSizeUpperBound) * appconsts.ContinuationSparseShareContentSize, + MaxGas: cparams.Block.MaxGas, + }, + Evidence: &cparams.Evidence, + Validator: &cparams.Validator, + Version: &cparams.Version, + } + + // Init chain will set the validator set and initialize the genesis accounts + testApp.InitChain( + abci.RequestInitChain{ + Time: gen.GenesisTime, + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: abciParams, + AppStateBytes: genDoc.AppState, + ChainId: genDoc.ChainID, + }, + ) + + // Commit genesis changes + testApp.Commit() + testApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ + ChainID: ChainID, + Height: testApp.LastBlockHeight() + 1, + AppHash: testApp.LastCommitID().Hash, + ValidatorsHash: genDoc.ValidatorHash(), + NextValidatorsHash: genDoc.ValidatorHash(), + Version: tmversion.Consensus{ + App: cparams.Version.AppVersion, + }, + }}) + + return gen.Keyring(), gen.Accounts(), nil +} + // SetupTestAppWithGenesisValSet initializes a new app with a validator set and // genesis accounts that also act as delegators. For simplicity, each validator // is bonded with a delegation of one consensus engine unit in the default token @@ -105,6 +210,48 @@ func SetupTestAppWithGenesisValSet(cparams *tmproto.ConsensusParams, genAccounts return testApp, kr } +// AddDeterministicValidatorToGenesis adds a single deterministic validator to the genesis. +func AddDeterministicValidatorToGenesis(g *genesis.Genesis) error { + // Hardcoded keys for deterministic account creation + mnemo := "body world north giggle crop reduce height copper damp next verify orphan lens loan adjust inform utility theory now ranch motion opinion crowd fun" + consensusKey := ed25519.GenPrivKeyFromSecret([]byte("12345678901234567890123456389012")) + networkKey := ed25519.GenPrivKeyFromSecret([]byte("12345678901234567890123456786012")) + + val := genesis.Validator{ + KeyringAccount: genesis.KeyringAccount{ + Name: "validator1", + InitialTokens: 1_000_000_000, + }, + Stake: 1_000_000, + ConsensusKey: consensusKey, + NetworkKey: networkKey, + } + + // Initialize the validator's genesis account in the keyring + rec, err := g.Keyring().NewAccount(val.Name, mnemo, "", "", hd.Secp256k1) + if err != nil { + return fmt.Errorf("failed to create account: %w", err) + } + + validatorPubKey, err := rec.GetPubKey() + if err != nil { + return fmt.Errorf("failed to get pubkey: %w", err) + } + + // Make account from keyring account + account := genesis.Account{ + PubKey: validatorPubKey, + Balance: val.KeyringAccount.InitialTokens, + } + + // Add the validator's account to the genesis + if err := g.AddAccount(account); err != nil { + return fmt.Errorf("failed to add account: %w", err) + } + + return g.AddValidator(val) +} + // AddGenesisAccount mimics the cli addGenesisAccount command, providing an // account with an allocation of to "token" and "tia" tokens in the genesis // state From ed392a1ae9d2b6722ce4491a89213c87b87b5b7c Mon Sep 17 00:00:00 2001 From: Rootul P Date: Fri, 21 Jun 2024 13:32:08 -0600 Subject: [PATCH 08/13] chore(deps): upgrade to celestia-core v1.37.0-tm-v0.34.29 (#3608) Manual backport of https://github.com/celestiaorg/celestia-app/pull/3607 --- .github/workflows/ci-release.yml | 2 +- .github/workflows/lint.yml | 9 +++------ .github/workflows/nightly.yml | 7 ++----- .github/workflows/test.yml | 27 ++++++++++++--------------- Dockerfile | 2 +- Makefile | 2 +- README.md | 2 +- docker/Dockerfile.test | 2 +- docker/Dockerfile_ephemeral | 2 +- go.mod | 4 ++-- go.sum | 4 ++-- 11 files changed, 27 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 2af262fd63..f2a925ed04 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -80,7 +80,7 @@ jobs: - run: git fetch --force --tags - uses: actions/setup-go@v4 with: - go-version: 1.22.3 + go-version: 1.22.4 - name: Create .release-env file run: |- echo 'GITHUB_TOKEN=${{secrets.GORELEASER_ACCESS_TOKEN}}' >> .release-env diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3ff50dd8aa..51b4506706 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,19 +2,16 @@ name: lint on: workflow_call: -env: - GO_VERSION: '1.22.3' - jobs: golangci-lint: name: golangci-lint runs-on: ubuntu-latest timeout-minutes: 8 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - uses: technote-space/get-diff-action@v6.1.2 with: # This job will pass without running if go.mod, go.sum, and *.go diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a4bf10a08c..6d9a26c636 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -4,18 +4,15 @@ on: # runs every day at 6am UTC - cron: "0 6 * * *" -env: - GO_VERSION: '1.22.3' - jobs: e2e-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Setup kubeconfig env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b66829229..745c4c2102 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,18 +2,15 @@ name: test on: workflow_call: -env: - GO_VERSION: '1.22.3' - jobs: test-short: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Run tests in short mode run: make test-short @@ -22,11 +19,11 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Run unit tests run: make test @@ -34,11 +31,11 @@ jobs: test-coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Generate coverage.txt run: make test-coverage @@ -51,11 +48,11 @@ jobs: test-race: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: 'go.mod' - name: Run unit tests in race mode run: make test-race diff --git a/Dockerfile b/Dockerfile index 55f466b860..c2e3538f8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # stage 1 Generate celestia-appd Binary -FROM docker.io/golang:1.22.3-alpine3.19 as builder +FROM docker.io/golang:1.22.4-alpine3.19 as builder # hadolint ignore=DL3018 RUN apk update && apk add --no-cache \ gcc \ diff --git a/Makefile b/Makefile index e26233681a..541c1f14c2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ DOCKER_PROTO_BUILDER := docker run -v $(shell pwd):/workspace --workdir /workspa PROJECTNAME=$(shell basename "$(PWD)") HTTPS_GIT := https://github.com/celestiaorg/celestia-app.git PACKAGE_NAME := github.com/celestiaorg/celestia-app -GOLANG_CROSS_VERSION ?= v1.22.3 +GOLANG_CROSS_VERSION ?= v1.22.4 # process linker flags ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=celestia-app \ diff --git a/README.md b/README.md index bef49e0bd7..10209a30c5 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ node | | | | ### Source -1. [Install Go](https://go.dev/doc/install) 1.22.3 +1. [Install Go](https://go.dev/doc/install) 1.22.4 1. Clone this repo 1. Install the celestia-app CLI diff --git a/docker/Dockerfile.test b/docker/Dockerfile.test index bc36be07e8..843069aadb 100644 --- a/docker/Dockerfile.test +++ b/docker/Dockerfile.test @@ -2,7 +2,7 @@ # > docker build -t celestia-app . # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.celestia-appd:/root/.celestia-appd -v ~/.celestia-appcli:/root/.celestia-appcli celestia-app celestia-appd init # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.celestia-appd:/root/.celestia-appd -v ~/.celestia-appcli:/root/.celestia-appcli celestia-app celestia-appd start -FROM docker.io/golang:1.22.3-alpine3.19 AS build-env +FROM docker.io/golang:1.22.4-alpine3.19 AS build-env # Set up dependencies ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 diff --git a/docker/Dockerfile_ephemeral b/docker/Dockerfile_ephemeral index 43fe13e4e9..92bfa872ad 100644 --- a/docker/Dockerfile_ephemeral +++ b/docker/Dockerfile_ephemeral @@ -1,5 +1,5 @@ # stage 1 Generate celestia-appd Binary -FROM docker.io/golang:1.22.3-alpine3.19 as builder +FROM docker.io/golang:1.22.4-alpine3.19 as builder RUN apk update && \ apk upgrade && \ diff --git a/go.mod b/go.mod index 25827b04c6..54e7eedd55 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/celestiaorg/celestia-app -go 1.22.3 +go 1.22.4 require ( github.com/celestiaorg/nmt v0.21.0 @@ -223,7 +223,7 @@ replace ( github.com/cosmos/ledger-cosmos-go => github.com/cosmos/ledger-cosmos-go v0.12.4 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.36.0-tm-v0.34.29 + github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29 ) // v1.8.0 was retracted due to a dependency bump in ibc resulting in a consensus diff --git a/go.sum b/go.sum index 0e9880815e..bd36a6b7f6 100644 --- a/go.sum +++ b/go.sum @@ -314,8 +314,8 @@ github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/celestiaorg/celestia-core v1.36.0-tm-v0.34.29 h1:qLsTxyS0CHMuMO4S0ug1Zntv+gFB6OVdDNmG3pz5mHI= -github.com/celestiaorg/celestia-core v1.36.0-tm-v0.34.29/go.mod h1:AL7kotb6ucKF4bpKKhGIzGhGL7dwYj2nFstiFC6PGxM= +github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29 h1:9nJDE37cTg/Cx+f4FS2g7yYeoLrsaNJg36XsQ47sS1A= +github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29/go.mod h1:IIdMu9gnDtjUmZkFuBN4Bf11z/rBtlL2rtwbQxdbRAU= github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16 h1:N2uETI13szEKnGAdKhtTR0EsrpcW0AwRKYER74WLnuw= github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16/go.mod h1:Bpl1LSWiDpQumgOhhMTZBMopqa0j7fRasIhvTZB44P0= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= From 2cb54a5d0d541fc1d98c1c00b507757817e5a324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nina=20/=20=E1=83=9C=E1=83=98=E1=83=9C=E1=83=90?= Date: Mon, 15 Jul 2024 12:41:05 +0200 Subject: [PATCH 09/13] chore: bump celestia core (#3676) ## Overview celestia-core bersion bump backport --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 54e7eedd55..a79f304f52 100644 --- a/go.mod +++ b/go.mod @@ -223,7 +223,7 @@ replace ( github.com/cosmos/ledger-cosmos-go => github.com/cosmos/ledger-cosmos-go v0.12.4 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29 + github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.38.0-tm-v0.34.29 ) // v1.8.0 was retracted due to a dependency bump in ibc resulting in a consensus diff --git a/go.sum b/go.sum index bd36a6b7f6..7c63df20fc 100644 --- a/go.sum +++ b/go.sum @@ -314,8 +314,8 @@ github.com/bufbuild/protocompile v0.5.1/go.mod h1:G5iLmavmF4NsYtpZFvE3B/zFch2GIY github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29 h1:9nJDE37cTg/Cx+f4FS2g7yYeoLrsaNJg36XsQ47sS1A= -github.com/celestiaorg/celestia-core v1.37.0-tm-v0.34.29/go.mod h1:IIdMu9gnDtjUmZkFuBN4Bf11z/rBtlL2rtwbQxdbRAU= +github.com/celestiaorg/celestia-core v1.38.0-tm-v0.34.29 h1:HwbA4OegRvXX0aNchBA7Cmu+oIxnH7xRcOhISuDP0ak= +github.com/celestiaorg/celestia-core v1.38.0-tm-v0.34.29/go.mod h1:MyElURdWAOJkOp84WZnfEUJ+OLvTwOOHG2lbK9E8XRI= github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16 h1:N2uETI13szEKnGAdKhtTR0EsrpcW0AwRKYER74WLnuw= github.com/celestiaorg/cosmos-sdk v1.23.0-sdk-v0.46.16/go.mod h1:Bpl1LSWiDpQumgOhhMTZBMopqa0j7fRasIhvTZB44P0= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= From 0a6d65eae0ad0b6b68f4b72c36879017142a4374 Mon Sep 17 00:00:00 2001 From: Rootul P Date: Mon, 15 Jul 2024 16:54:46 +0200 Subject: [PATCH 10/13] test(testnode): configure custom min gas price on v1.x (#3674) Unit test to help prevent regressions on v1.x. This is motivated by debugging integration tests on https://github.com/celestiaorg/celestia-node/pull/3453 --- app/test/testnode_test.go | 47 ++++++++++++++++++++++++ go.mod | 31 ++++++++-------- go.sum | 62 +++++++++++++++++--------------- test/txsim/client.go | 2 +- test/util/network/network.go | 2 +- test/util/testnode/rpc_client.go | 2 +- x/blob/types/builder_test.go | 4 +-- x/qgb/client/verify.go | 2 +- 8 files changed, 103 insertions(+), 49 deletions(-) create mode 100644 app/test/testnode_test.go diff --git a/app/test/testnode_test.go b/app/test/testnode_test.go new file mode 100644 index 0000000000..fd7a78aafb --- /dev/null +++ b/app/test/testnode_test.go @@ -0,0 +1,47 @@ +package app_test + +import ( + "context" + "fmt" + "testing" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/test/util/testnode" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +func Test_testnode(t *testing.T) { + t.Run("testnode can start a network with default chain ID", func(t *testing.T) { + testnode.NewNetwork(t, testnode.DefaultConfig()) + }) + t.Run("testnode can start with a custom MinGasPrice", func(t *testing.T) { + wantMinGasPrice := float64(0.003) + appConfig := testnode.DefaultAppConfig() + appConfig.MinGasPrices = fmt.Sprintf("%v%s", wantMinGasPrice, app.BondDenom) + config := testnode.DefaultConfig().WithAppConfig(appConfig) + cctx, _, _ := testnode.NewNetwork(t, config) + + got, err := queryMinimumGasPrice(cctx.GoContext(), cctx.GRPCClient) + require.NoError(t, err) + assert.Equal(t, wantMinGasPrice, got) + }) +} + +func queryMinimumGasPrice(ctx context.Context, grpcConn *grpc.ClientConn) (float64, error) { + resp, err := nodeservice.NewServiceClient(grpcConn).Config(ctx, &nodeservice.ConfigRequest{}) + if err != nil { + return 0, err + } + + minGasCoins, err := sdktypes.ParseDecCoins(resp.MinimumGasPrice) + if err != nil { + return 0, err + } + + minGasPrice := minGasCoins.AmountOf(app.BondDenom).MustFloat64() + return minGasPrice, nil +} diff --git a/go.mod b/go.mod index a79f304f52..00830fe86a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/ethereum/go-ethereum v1.13.2 github.com/gogo/protobuf v1.3.3 github.com/golang/protobuf v1.5.4 - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/pkg/errors v0.9.1 // indirect @@ -19,8 +19,8 @@ require ( golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/grpc v1.60.0 + google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect + google.golang.org/grpc v1.64.0 ) require ( @@ -37,7 +37,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/tendermint/tendermint v0.34.29 golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 gopkg.in/yaml.v2 v2.4.0 ) @@ -65,6 +65,8 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/supranational/blst v0.3.11 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.18.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect @@ -73,16 +75,16 @@ require ( golang.org/x/mod v0.12.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) require ( - cloud.google.com/go v0.110.10 // indirect - cloud.google.com/go/compute v1.23.3 // indirect + cloud.google.com/go v0.112.1 // indirect + cloud.google.com/go/compute v1.25.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/storage v1.35.1 // indirect + cloud.google.com/go/iam v1.1.6 // indirect + cloud.google.com/go/storage v1.38.0 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect @@ -120,7 +122,7 @@ require ( github.com/docker/docker v24.0.7+incompatible github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.6.0 // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -130,14 +132,14 @@ require ( github.com/go-stack/stack v1.8.1 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/gateway v1.1.0 // indirect - github.com/golang/glog v1.1.2 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/googleapis/gax-go/v2 v2.12.2 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -204,10 +206,9 @@ require ( go.etcd.io/bbolt v1.3.6 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sync v0.5.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.152.0 // indirect + google.golang.org/api v0.169.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 7c63df20fc..0752b182b0 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -71,8 +71,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -112,8 +112,8 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= +cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= @@ -174,8 +174,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= -cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.38.0 h1:Az68ZRGlnNTpIBbLjSMIV2BDcwwXYlRlQzis0llkpJg= +cloud.google.com/go/storage v1.38.0/go.mod h1:tlUADB0mAb9BgYls9lq+8MGkfzOXuLrnHXlpHmvFJoY= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -511,8 +511,9 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpm github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -600,8 +601,8 @@ github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -703,8 +704,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -719,8 +720,8 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= +github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -1295,6 +1296,10 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= @@ -1495,8 +1500,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1709,8 +1714,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= @@ -1766,8 +1772,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= -google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= +google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1886,12 +1892,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= +google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1934,8 +1940,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k= -google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/test/txsim/client.go b/test/txsim/client.go index ab37535f2a..3303b72304 100644 --- a/test/txsim/client.go +++ b/test/txsim/client.go @@ -266,7 +266,7 @@ type QueryClient struct { func NewQueryClient(grpcEndpoints []string) (*QueryClient, error) { connections := make([]*grpc.ClientConn, len(grpcEndpoints)) for idx, endpoint := range grpcEndpoints { - conn, err := grpc.Dial(grpcEndpoints[0], grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.NewClient(grpcEndpoints[0], grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, fmt.Errorf("dialing %s: %w", endpoint, err) } diff --git a/test/util/network/network.go b/test/util/network/network.go index 5abda35891..29ec4a5f6c 100644 --- a/test/util/network/network.go +++ b/test/util/network/network.go @@ -58,7 +58,7 @@ func New(t *testing.T, config network.Config, genAccNames ...string) *network.Ne // network. The resulting grpc client connection is stored in the client context func GRPCConn(net *network.Network) error { nodeGRPCAddr := strings.Replace(net.Validators[0].AppConfig.GRPC.Address, "0.0.0.0", "localhost", 1) - conn, err := grpc.Dial(nodeGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.NewClient(nodeGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) net.Validators[0].ClientCtx.GRPCClient = conn return err } diff --git a/test/util/testnode/rpc_client.go b/test/util/testnode/rpc_client.go index 79c043958a..744a8f6007 100644 --- a/test/util/testnode/rpc_client.go +++ b/test/util/testnode/rpc_client.go @@ -65,7 +65,7 @@ func StartGRPCServer(app srvtypes.Application, appCfg *srvconfig.Config, cctx Co } nodeGRPCAddr := strings.Replace(appCfg.GRPC.Address, "0.0.0.0", "localhost", 1) - conn, err := grpc.Dial(nodeGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.NewClient(nodeGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return Context{}, emptycleanup, err } diff --git a/x/blob/types/builder_test.go b/x/blob/types/builder_test.go index 026c12d228..230dab693f 100644 --- a/x/blob/types/builder_test.go +++ b/x/blob/types/builder_test.go @@ -61,7 +61,7 @@ func TestBroadcastPayForBlob(t *testing.T) { RPCAddress := "127.0.0.1:9090" - rpcClient, err := grpc.Dial(RPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + rpcClient, err := grpc.NewClient(RPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) err = k.QueryAccountNumber(context.TODO(), rpcClient) require.NoError(t, err) @@ -96,7 +96,7 @@ func TestQueryAccountNumber(t *testing.T) { RPCAddress := "127.0.0.1:9090" - rpcClient, err := grpc.Dial(RPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + rpcClient, err := grpc.NewClient(RPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) err = k.QueryAccountNumber(context.TODO(), rpcClient) require.NoError(t, err) diff --git a/x/qgb/client/verify.go b/x/qgb/client/verify.go index ea31793fcf..1a5c52b380 100644 --- a/x/qgb/client/verify.go +++ b/x/qgb/client/verify.go @@ -229,7 +229,7 @@ func VerifyShares(ctx context.Context, logger tmlog.Logger, config VerifyConfig, logger.Info("proofs from shares to data root are valid") - qgbGRPC, err := grpc.Dial(config.CelesGRPC, grpc.WithTransportCredentials(insecure.NewCredentials())) + qgbGRPC, err := grpc.NewClient(config.CelesGRPC, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return false, err } From 61d12bae048693dca9b7435a91c4a2e3665369bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:14:01 +0200 Subject: [PATCH 11/13] chore: skip testnode test in race mode (backport #3696) (#3699) I noticed a CI failure on another PR for test-race and I'm pretty sure testnode tests will fail in race mode b/c of an un-ivestigated issue.
This is an automatic backport of pull request #3696 done by [Mergify](https://mergify.com). --------- Co-authored-by: Rootul P --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 541c1f14c2..6ebd6a7971 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ test-short: ## test-race: Run unit tests in race mode. test-race: @echo "--> Running tests in race mode" - @go test ./... -v -race -skip "TestPrepareProposalConsistency|TestIntegrationTestSuite|TestQGBRPCQueries|TestSquareSizeIntegrationTest|TestStandardSDKIntegrationTestSuite|TestTxsimCommandFlags|TestTxsimCommandEnvVar|TestMintIntegrationTestSuite|TestQGBCLI|TestUpgrade|TestMaliciousTestNode|TestMaxTotalBlobSizeSuite|TestQGBIntegrationSuite|TestSignerTestSuite|TestPriorityTestSuite|TestTimeInPrepareProposalContext|TestConcurrentTxSubmission|TestTxClientTestSuite" + @go test ./... -v -race -skip "TestPrepareProposalConsistency|TestIntegrationTestSuite|TestQGBRPCQueries|TestSquareSizeIntegrationTest|TestStandardSDKIntegrationTestSuite|TestTxsimCommandFlags|TestTxsimCommandEnvVar|TestMintIntegrationTestSuite|TestQGBCLI|TestUpgrade|TestMaliciousTestNode|TestMaxTotalBlobSizeSuite|TestQGBIntegrationSuite|TestSignerTestSuite|TestPriorityTestSuite|TestTimeInPrepareProposalContext|TestConcurrentTxSubmission|TestTxClientTestSuite|Test_testnode" .PHONY: test-race ## test-bench: Run unit tests in bench mode. From f2eb67887d74789f16172a2e79082a1fe8baa3c9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:19:11 -0400 Subject: [PATCH 12/13] fix(goreleaser): do not mark rcs as latest release (backport #3701) (#3702) There is an issue with goreleaser where it will automatically mark release candidates as the latest release. Motivated by https://github.com/celestiaorg/celestia-node/pull/3569 Thanks @ramin!
This is an automatic backport of pull request #3701 done by [Mergify](https://mergify.com). Co-authored-by: Rootul P --- .goreleaser.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 5182fbf1a8..bf1a237b64 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -114,5 +114,7 @@ changelog: exclude: - "^docs:" - "^test:" +release: + prerelease: auto git: - prerelease_suffix: "-" + prerelease_suffix: "-rc*" From 34748120f740003b7ba18d3f87aad81193bf9820 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:26:37 +0200 Subject: [PATCH 13/13] refactor(scripts): improve user prompts for single-node.sh (backport #3685) (#3707) Closes #3652 ## Overview It modifies `single-node.sh` script. This rewrites the first prompt and deletes the second one. I wanted write `Do you want to start a new local testnet? [y/n]` only when `$HOME/.celestia-app` does not exist. But it looks like as soon as `celestia-app` is invoked, it creates the directory. Hence I cannot use this information.
This is an automatic backport of pull request #3685 done by [Mergify](https://mergify.com). --------- Co-authored-by: nathan haim Co-authored-by: Rootul Patel --- scripts/single-node.sh | 163 ++++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 74 deletions(-) diff --git a/scripts/single-node.sh b/scripts/single-node.sh index eb7cfe27e0..16584bc2e6 100755 --- a/scripts/single-node.sh +++ b/scripts/single-node.sh @@ -12,85 +12,100 @@ COINS="1000000000000000utia" DELEGATION_AMOUNT="5000000000utia" CELESTIA_APP_HOME="${HOME}/.celestia-app" CELESTIA_APP_VERSION=$(celestia-appd version 2>&1) +GENESIS_FILE="${CELESTIA_APP_HOME}/config/genesis.json" +FEES="500utia" echo "celestia-app home: ${CELESTIA_APP_HOME}" echo "celestia-app version: ${CELESTIA_APP_VERSION}" echo "" -# Ask the user for confirmation before deleting the existing celestia-app home -# directory. -read -p "Are you sure you want to delete: $CELESTIA_APP_HOME? [y/n] " response +createGenesis() { + echo "Initializing validator and node config files..." + celestia-appd init ${CHAIN_ID} \ + --chain-id ${CHAIN_ID} \ + --home "${CELESTIA_APP_HOME}" \ + > /dev/null 2>&1 # Hide output to reduce terminal noise -# Check the user's response -if [[ $response != "y" ]]; then - # Exit if the user did not respond with "y" - echo "You must delete $CELESTIA_APP_HOME to continue." - exit 1 -fi + echo "Adding a new key to the keyring..." + celestia-appd keys add ${KEY_NAME} \ + --keyring-backend=${KEYRING_BACKEND} \ + --home "${CELESTIA_APP_HOME}" \ + > /dev/null 2>&1 # Hide output to reduce terminal noise + + echo "Adding genesis account..." + celestia-appd add-genesis-account \ + "$(celestia-appd keys show ${KEY_NAME} -a --keyring-backend=${KEYRING_BACKEND} --home "${CELESTIA_APP_HOME}")" \ + $COINS \ + --home "${CELESTIA_APP_HOME}" + + echo "Creating a genesis tx..." + celestia-appd gentx ${KEY_NAME} ${DELEGATION_AMOUNT} \ + --fees ${FEES} \ + --keyring-backend=${KEYRING_BACKEND} \ + --chain-id ${CHAIN_ID} \ + --home "${CELESTIA_APP_HOME}" \ + > /dev/null 2>&1 # Hide output to reduce terminal noise + + echo "Collecting genesis txs..." + celestia-appd collect-gentxs \ + --home "${CELESTIA_APP_HOME}" \ + > /dev/null 2>&1 # Hide output to reduce terminal noise + + # If you encounter: `sed: -I or -i may not be used with stdin` on MacOS you can mitigate by installing gnu-sed + # https://gist.github.com/andre3k1/e3a1a7133fded5de5a9ee99c87c6fa0d?permalink_comment_id=3082272#gistcomment-3082272 + + # Override the default RPC servier listening address + sed -i'.bak' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:26657"#g' "${CELESTIA_APP_HOME}"/config/config.toml + + # Enable transaction indexing + sed -i'.bak' 's#"null"#"kv"#g' "${CELESTIA_APP_HOME}"/config/config.toml + + # Persist ABCI responses + sed -i'.bak' 's#discard_abci_responses = true#discard_abci_responses = false#g' "${CELESTIA_APP_HOME}"/config/config.toml + + # Override the log level to debug + # sed -i'.bak' 's#log_level = "info"#log_level = "debug"#g' "${CELESTIA_APP_HOME}"/config/config.toml -echo "Deleting $CELESTIA_APP_HOME..." -rm -r "$CELESTIA_APP_HOME" - -echo "Initializing validator and node config files..." -celestia-appd init ${CHAIN_ID} \ - --chain-id ${CHAIN_ID} \ - --home ${CELESTIA_APP_HOME} \ - &> /dev/null # Hide output to reduce terminal noise - -echo "Adding a new key to the keyring..." -celestia-appd keys add ${KEY_NAME} \ - --keyring-backend=${KEYRING_BACKEND} \ - --home ${CELESTIA_APP_HOME} \ - &> /dev/null # Hide output to reduce terminal noise - -echo "Adding genesis account..." -celestia-appd add-genesis-account \ - $(celestia-appd keys show ${KEY_NAME} -a --keyring-backend=${KEYRING_BACKEND} --home ${CELESTIA_APP_HOME}) \ - $COINS \ - --home ${CELESTIA_APP_HOME} - -echo "Creating a genesis tx..." -celestia-appd gentx ${KEY_NAME} ${DELEGATION_AMOUNT} \ - --keyring-backend=${KEYRING_BACKEND} \ - --chain-id ${CHAIN_ID} \ - --home ${CELESTIA_APP_HOME} \ - &> /dev/null # Hide output to reduce terminal noise - -echo "Collecting genesis txs..." -celestia-appd collect-gentxs \ - --home ${CELESTIA_APP_HOME} \ - &> /dev/null # Hide output to reduce terminal noise - -# Set proper defaults and change ports -# If you encounter: `sed: -I or -i may not be used with stdin` on MacOS you can mitigate by installing gnu-sed -# https://gist.github.com/andre3k1/e3a1a7133fded5de5a9ee99c87c6fa0d?permalink_comment_id=3082272#gistcomment-3082272 -sed -i'.bak' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:26657"#g' "${CELESTIA_APP_HOME}"/config/config.toml - -# Register the validator EVM address -{ - # Wait for block 1 - sleep 20 - - VALIDATOR_ADDRESS=$(celestia-appd keys show ${KEY_NAME} --home "${CELESTIA_APP_HOME}" --bech val --address) - EVM_ADDRESS=0x966e6f22781EF6a6A82BBB4DB3df8E225DfD9488 # private key: da6ed55cb2894ac2c9c10209c09de8e8b9d109b910338d5bf3d747a7e1fc9eb9 - echo "Registering an EVM address for validator..." - celestia-appd tx qgb register \ - ${VALIDATOR_ADDRESS} \ - ${EVM_ADDRESS} \ - --from ${KEY_NAME} \ + # Override the VotingPeriod from 1 week to 1 minute + sed -i'.bak' 's#"604800s"#"60s"#g' "${CELESTIA_APP_HOME}"/config/genesis.json + + # Override the genesis to use app version 1 and then upgrade to app version 2 later. + sed -i'.bak' 's#"app_version": "2"#"app_version": "1"#g' "${CELESTIA_APP_HOME}"/config/genesis.json + + trace_type="local" + sed -i.bak -e "s/^trace_type *=.*/trace_type = \"$trace_type\"/" ${CELESTIA_APP_HOME}/config/config.toml + + trace_pull_address=":26661" + sed -i.bak -e "s/^trace_pull_address *=.*/trace_pull_address = \"$trace_pull_address\"/" ${CELESTIA_APP_HOME}/config/config.toml + + trace_push_batch_size=1000 + sed -i.bak -e "s/^trace_push_batch_size *=.*/trace_push_batch_size = \"$trace_push_batch_size\"/" ${CELESTIA_APP_HOME}/config/config.toml + + echo "Tracing is set up with the ability to pull traced data from the node on the address http://127.0.0.1${trace_pull_address}" +} + +deleteCelestiaAppHome() { + echo "Deleting $CELESTIA_APP_HOME..." + rm -r "$CELESTIA_APP_HOME" +} + +startCelestiaApp() { + echo "Starting celestia-app..." + celestia-appd start \ --home "${CELESTIA_APP_HOME}" \ - --fees 30000utia \ - --broadcast-mode block \ - --yes \ - &> /dev/null # Hide output to reduce terminal noise - - echo "Registered EVM address." -} & - -# Start celestia-app -echo "Starting celestia-app..." -celestia-appd start \ - --home ${CELESTIA_APP_HOME} \ - --api.enable \ - --grpc.enable \ - --grpc-web.enable + --api.enable \ + --grpc.enable \ + --grpc-web.enable +} + +if [ -f $GENESIS_FILE ]; then + echo "Do you want to delete existing ${CELESTIA_APP_HOME} and start a new local testnet? [y/n]" + read -r response + if [ "$response" = "y" ]; then + deleteCelestiaAppHome + createGenesis + fi +else + createGenesis +fi +startCelestiaApp