Repository: celestiaorg/celestia-app

Issue:
`error reading GenesisDoc at /celestia/config/genesis.json: invalid 64-bit integer encoding "1", expected string`
## Context

https://discord.com/channels/638338779505229824/920642717057421393/1418261928005861466

##

> hey guys, I'm trying to run a mocha validator using docker  v5.0.5-mocha. celestia-appd download-genesis mocha-4

```
validator  | Error: failed to get current app state: error reading GenesisDoc at /celestia/config/genesis.json: invalid 64-bit integer encoding "1", expected string
```

## Proposal

1. Investigate if it's possible to block sync from genesis on Mocha using v5.0.5-mocha
2. Respond to user on Discord

Hints from issue comments:
@rootulp: After finagling with the seeds and peers, I was able to start block syncing on Mocha and Mainnet. Will create a separate PR to update the seeds and peers in the block sync script.@rootulp: I also double-checked the mocha genesis.json and it looks correct to me. I didn't need to make any modifications to it.

```shell
$ head -n 10 ~/.celestia-app/config/genesis.json
{
  "genesis_time": "2023-09-06T03:15:51.510579711Z",
  "chain_id": "mocha-4",
  "initial_height": "1",
  "consensus_params": {
    "block": {
      "max_bytes": "1974272",
      "max_gas": "-1",
      "time_iota_ms": "1000"
    },
```@rootulp: Asked the bug reported for more details on Discord.@rootulp: Block syncing on Mocha even works in Docker. See https://github.com/rootulp/celestia-app/tree/rp/genesis-issue

```
$    docker compose -f docker-compose.reproduce-issue.yml up
...
celestia-mocha-validator  | 2:50PM INF executed block app_hash=E7C8B52AFAA1B01B0B1383A91E6872C00CD8F5F4CE78EB0E403B7D07C2D9DC5D height=930 module=state
```@rootulp: Will re-open if I get more details from bug reporter.

Solution patch:
diff --git a/Makefile b/Makefile
index 82be5180..d33d47c7 100644
--- a/Makefile
+++ b/Makefile
@@ -397,7 +397,7 @@ prebuilt-binary:
 		release --clean --parallelism 2
 .PHONY: prebuilt-binary
 
-## goreleaser-dry-run: ensures that the go releaser tool can build all the artefacts correctly.
+## goreleaser-dry-run: ensures that the go releaser tool can build all the artifacts correctly.
 goreleaser-dry-run:
 # Specifies parallelism as 4 so should be run locally. On the regular github runners 2 should be the max.
 	@echo "Running GoReleaser in dry-run mode..."
diff --git a/go.mod b/go.mod
index d505a7bc..c3fa000d 100644
--- a/go.mod
+++ b/go.mod
@@ -36,7 +36,7 @@ require (
 	github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8 v8.2.0
 	github.com/cosmos/ibc-go/modules/capability v1.0.1
 	github.com/cosmos/ibc-go/v8 v8.7.0
-	github.com/digitalocean/godo v1.164.0
+	github.com/digitalocean/godo v1.165.0
 	github.com/go-kit/log v0.2.1
 	github.com/golang/protobuf v1.5.4
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
diff --git a/go.sum b/go.sum
index 3255c631..0e815142 100644
--- a/go.sum
+++ b/go.sum
@@ -924,8 +924,8 @@ github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4Typ
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
-github.com/digitalocean/godo v1.164.0 h1:H4ApzjQK78zVk4ebofaS7mKq3fFwHSKlQtCLEfDCarM=
-github.com/digitalocean/godo v1.164.0/go.mod h1:NJ1VlXmFMSnG1GEe2rWyDZVrhR69c3nHmL0s1cSSQ6M=
+github.com/digitalocean/godo v1.165.0 h1:OG/E4/pUDCoySjKYeW1WKaON2airpWu7TdKwpZllz8I=
+github.com/digitalocean/godo v1.165.0/go.mod h1:NJ1VlXmFMSnG1GEe2rWyDZVrhR69c3nHmL0s1cSSQ6M=
 github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11 h1:IPrmumsT9t5BS7XcPhgsCTlkWbYg80SEXUzDpReaU6Y=
 github.com/docker/go-connections v0.4.1-0.20210727194412-58542c764a11/go.mod h1:a6bNUGTbQBsY6VRHTr4h/rkOXjl244DyRD0tx3fgq4Q=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
diff --git a/pkg/user/tx_client.go b/pkg/user/tx_client.go
index ccebfcf4..1f644edd 100644
--- a/pkg/user/tx_client.go
+++ b/pkg/user/tx_client.go
@@ -547,7 +547,7 @@ func (client *TxClient) broadcastMulti(ctx context.Context, txBytes []byte, sign
 		go func(conn *grpc.ClientConn) {
 			defer wg.Done()
 
-			resp, err := client.broadcastTxAndIncrementSequence(ctx, conn, txBytes, signer)
+			resp, err := client.broadcastTx(ctx, conn, txBytes, signer)
 			if err != nil {
 				errCh <- err
 				return
@@ -569,6 +569,11 @@ func (client *TxClient) broadcastMulti(ctx context.Context, txBytes []byte, sign
 
 	// Return first successful response, if any
 	if resp, ok := <-respCh; ok {
+		client.trackTransaction(signer, resp.TxHash, txBytes)
+
+		if err := client.signer.IncrementSequence(signer); err != nil {
+			return nil, fmt.Errorf("increment sequencing: %w", err)
+		}
 		return resp, nil
 	}
 
diff --git a/scripts/block-sync.sh b/scripts/block-sync.sh
index ec852438..d4a636c6 100755
--- a/scripts/block-sync.sh
+++ b/scripts/block-sync.sh
@@ -26,14 +26,17 @@ case "$NETWORK" in
   arabica)
     CHAIN_ID="arabica-11"
     SEEDS="827583022cc6ce65cf762115642258f937c954cd@validator-1.celestia-arabica-11.com:26656,74e42b39f512f844492ff09e30af23d54579b7bc@validator-2.celestia-arabica-11.com:26656,00d577159b2eb1f524ef9c37cb389c020a2c38d2@validator-3.celestia-arabica-11.com:26656,b2871b6dc2e18916d07264af0e87c456c2bba04f@validator-4.celestia-arabica-11.com:26656"
+    PEERS=""
     ;;
   mocha)
     CHAIN_ID="mocha-4"
-    SEEDS="ee9f90974f85c59d3861fc7f7edb10894f6ac3c8@seed-mocha.pops.one:26656,258f523c96efde50d5fe0a9faeea8a3e83be22ca@seed.mocha-4.celestia.aviaone.com:20279,5d0bf034d6e6a8b5ee31a2f42f753f1107b3a00e@celestia-testnet-seed.itrocket.net:11656,7da0fb48d6ef0823bc9770c0c8068dd7c89ed4ee@celest-test-seed.theamsolutions.info:443"
+    SEEDS="b402fe40f3474e9e208840702e1b7aa37f2edc4b@celestia-testnet-seed.itrocket.net:14656"
+    PEERS="daf2cecee2bd7f1b3bf94839f993f807c6b15fbf@celestia-testnet-peer.itrocket.net:11656,96b2761729cea90ee7c61206433fc0ba40c245bf@57.128.141.126:11656,f4f75a55bfc5f302ef34435ef096a4551ecb6804@152.53.33.96:12056,31bb1c9c1be7743d1115a8270bd1c83d01a9120a@148.72.141.31:26676,3e30bcfc55e7d351f18144aab4b0973e9e9bf987@65.108.226.183:11656,7a0d5818c0e5b0d4fbd86a9921f413f5e4e4ac1e@65.109.83.40:28656,43e9da043318a4ea0141259c17fcb06ecff816af@164.132.247.253:43656,5a7566aa030f7e5e7114dc9764f944b2b1324bcd@65.109.23.114:11656,c17c0cbf05e98656fee5f60fad469fc528f6d6de@65.109.25.113:11656,fb5e0b9efacc11916c58bbcd3606cbaa7d43c99f@65.108.234.84:28656,45504fb31eb97ea8778c920701fc8076e568a9cd@188.214.133.100:26656,edafdf47c443344fb940a32ab9d2067c482e59df@84.32.71.47:26656,ae7d00d6d70d9b9118c31ac0913e0808f2613a75@177.54.156.69:26656,7c841f59c35d70d9f1472d7d2a76a11eefb7f51f@136.243.69.100:43656"
     ;;
   mainnet)
     CHAIN_ID="celestia"
-    SEEDS="e6116822e1a5e283d8a85d3ec38f4d232274eaf3@consensus-full-seed-1.celestia-bootstrap.net:26656,cf7ac8b19ff56a9d47c75551bd4864883d1e24b5@consensus-full-seed-2.celestia-bootstrap.net:26656"
+    SEEDS="12ad7c73c7e1f2460941326937a039139aa78884@celestia-mainnet-seed.itrocket.net:40656"
+    PEERS="d535cbf8d0efd9100649aa3f53cb5cbab33ef2d6@celestia-mainnet-peer.itrocket.net:26656,4928c12dc8ba4c97bf529296c9321341c6dbcfb1@[2001:bc8:1203:1b2::8]:26656,6e4e6676108e3c54cec921d362a455172e0e3477@5.9.80.214:26101,908f49878376bd9fe78cf0ed91b51b6717418aa6@146.70.243.171:26656,d2bdd758eb6fad53f48ab4c7e612a3150c58e810@65.108.128.201:11656,0f7fea6ba71798a9daccf4c38622d8c97f747067@65.21.84.223:11656,8240e8a13594d40b6839f183795c551503309d3c@51.15.16.101:26656,aa15a9c698773412b13054b992bafc1554cfbbd7@193.35.57.185:11656,202a224f679b63cf08bf3b6f9844ff51c68fdcc9@94.237.25.73:26656,938b0387d09ae08d773cd13357d3b9b25cc540e5@136.243.94.113:26666,84970085409a8433133aedcf31f1e849bcfadd96@136.243.67.47:11656"
     ;;
   *)
     echo "Unknown network: $NETWORK"
@@ -64,6 +67,9 @@ celestia-appd init ${NODE_NAME} --chain-id ${CHAIN_ID} > /dev/null 2>&1
 echo "Setting seeds in config.toml..."
 sed -i.bak -e "s/^seeds *=.*/seeds = \"$SEEDS\"/" $CELESTIA_APP_HOME/config/config.toml
 
+echo "Setting persistent peers in config.toml..."
+sed -i -e "/^\[p2p\]/,/^\[/{s/^[[:space:]]*persistent_peers *=.*/persistent_peers = \"$PEERS\"/;}" $CELESTIA_APP_HOME/config/config.toml
+
 echo "Downloading genesis file..."
 celestia-appd download-genesis ${CHAIN_ID} > /dev/null 2>&1 # Hide output to reduce terminal noise
 
diff --git a/specs/src/recovery.md b/specs/src/recovery.md
index 73d9024e..25485f07 100644
--- a/specs/src/recovery.md
+++ b/specs/src/recovery.md
@@ -24,7 +24,7 @@ The Celestia protocol will likely separate block propagation into two phases.
 "recovery" for distributing data *after* the block has been created. This
 document focuses on recovery.
 
-![vacumm! map](./figures/recovery/vacuum!_map.png)
+![vacuum! map](./figures/recovery/vacuum!_map.png)
 
 In order to use the data propagated during the preparation phase, the recovery
 phase has to use *some form* of pull based gossip.
@@ -456,11 +456,11 @@ purely related to block propagation, and not meant to be part of the proposal.
 This allows for block propagation to be backwards compatible with older
 implementations.
 
-The `BlobMetaData` contains the hash of the PFB for the blob transaction that it
-commits to, alongside the `size`. `size` is the length of the blobTx in bytes.
+The `TxMetaData` contains the hash of the PFB for the blob transaction that it
+commits to. The size of the transaction can be calculated as `end - start`.
 
 The `bp_hash` is generated by taking the merkle root over of each of the blob
-transactions in `BlobMetaData` and `Have` messages.
+transactions in `TxMetaData` and `Have` messages.
 
 Verification:
 



Test patch:
diff --git a/app/benchmarks/gas_estimator_test.go b/app/benchmarks/gas_estimator_test.go
index 8f6404ae..332c76f6 100644
--- a/app/benchmarks/gas_estimator_test.go
+++ b/app/benchmarks/gas_estimator_test.go
@@ -74,7 +74,7 @@ func BenchmarkGasPriceEstimation(b *testing.B) {
 				func(txBytes []byte) (sdk.GasInfo, *sdk.Result, error) { return sdk.GasInfo{}, nil, nil },
 				func() (float64, error) { return appconsts.DefaultNetworkMinGasPrice, nil },
 			)
-			for i := 0; i < b.N; i++ {
+			for b.Loop() {
 				_, err := gasEstimationServer.EstimateGasPrice(context.Background(), &gasestimation.EstimateGasPriceRequest{})
 				require.NoError(b, err)
 			}
diff --git a/pkg/user/tx_client_test.go b/pkg/user/tx_client_test.go
index 5b25655f..cb5b3880 100644
--- a/pkg/user/tx_client_test.go
+++ b/pkg/user/tx_client_test.go
@@ -794,3 +794,75 @@ func (suite *TxClientTestSuite) TestMultiConnBroadcast() {
 		})
 	}
 }
+
+// TestSequenceIncrementOnlyOnceInMultiConnBroadcast specifically tests that the sequence
+// is incremented exactly once even when multiple connections succeed simultaneously.
+// This test would fail if we naively called broadcastTxAndIncrementSequence in each goroutine.
+func (suite *TxClientTestSuite) TestSequenceIncrementOnlyOnceInMultiConnBroadcast() {
+	t := suite.T()
+
+	// Create mock services that all succeed immediately
+	mockSvc1 := &grpctest.MockTxService{
+		BroadcastHandler: func(ctx context.Context, req *sdktx.BroadcastTxRequest) (*sdktx.BroadcastTxResponse, error) {
+			return &sdktx.BroadcastTxResponse{TxResponse: &sdk.TxResponse{Code: abci.CodeTypeOK, TxHash: "SUCCESS1"}}, nil
+		},
+	}
+	mockSvc2 := &grpctest.MockTxService{
+		BroadcastHandler: func(ctx context.Context, req *sdktx.BroadcastTxRequest) (*sdktx.BroadcastTxResponse, error) {
+			return &sdktx.BroadcastTxResponse{TxResponse: &sdk.TxResponse{Code: abci.CodeTypeOK, TxHash: "SUCCESS2"}}, nil
+		},
+	}
+	mockSvc3 := &grpctest.MockTxService{
+		BroadcastHandler: func(ctx context.Context, req *sdktx.BroadcastTxRequest) (*sdktx.BroadcastTxResponse, error) {
+			return &sdktx.BroadcastTxResponse{TxResponse: &sdk.TxResponse{Code: abci.CodeTypeOK, TxHash: "SUCCESS3"}}, nil
+		},
+	}
+
+	conn1 := grpctest.StartMockServer(t, mockSvc1)
+	conn2 := grpctest.StartMockServer(t, mockSvc2)
+	conn3 := grpctest.StartMockServer(t, mockSvc3)
+
+	// Create a TxClient with multiple connections
+	origSigner := suite.txClient.Signer()
+	origAcc := origSigner.Account(suite.txClient.DefaultAccountName()).Copy()
+	signer, err := user.NewSigner(suite.ctx.Keyring, suite.encCfg.TxConfig, origSigner.ChainID(), origAcc)
+	require.NoError(t, err)
+
+	multiConnClient, err := user.NewTxClient(
+		suite.encCfg.Codec,
+		signer,
+		conn1,
+		suite.encCfg.InterfaceRegistry,
+		user.WithAdditionalCoreEndpoints([]*grpc.ClientConn{conn2, conn3}),
+	)
+	require.NoError(t, err)
+
+	// Capture sequence before broadcast
+	seqBefore := multiConnClient.Signer().Account(multiConnClient.DefaultAccountName()).Sequence()
+
+	// Create a simple message
+	msg := bank.NewMsgSend(
+		multiConnClient.DefaultAddress(),
+		multiConnClient.DefaultAddress(),
+		sdk.NewCoins(sdk.NewCoin(appconsts.BondDenom, sdkmath.NewInt(1))),
+	)
+
+	// Broadcast the transaction
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	resp, err := multiConnClient.BroadcastTx(ctx, []sdk.Msg{msg}, user.SetGasLimit(100000), user.SetFee(1000))
+	require.NoError(t, err, "BroadcastTx should succeed")
+	require.NotNil(t, resp, "Response should not be nil")
+	require.Equal(t, abci.CodeTypeOK, resp.Code, "Response code should be OK")
+
+	// Verify sequence was incremented by exactly 1
+	seqAfter := multiConnClient.Signer().Account(multiConnClient.DefaultAccountName()).Sequence()
+	require.Equal(t, seqBefore+1, seqAfter, "Sequence should be incremented by exactly 1, not by number of connections")
+
+	// Verify the transaction is tracked
+	trackedSeq, trackedSigner, exists := multiConnClient.GetTxFromTxTracker(resp.TxHash)
+	require.True(t, exists, "Transaction should be in tracker")
+	require.Equal(t, seqBefore, trackedSeq, "Tracked sequence should be the sequence before increment")
+	require.Equal(t, multiConnClient.DefaultAccountName(), trackedSigner, "Tracked signer should match")
+}