From d451fd26edfa832d54bb84d4324249f2583527b8 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:58:51 +0530 Subject: [PATCH 01/54] add: READ_REQUEST event --- universalClient/store/models.go | 1 + 1 file changed, 1 insertion(+) diff --git a/universalClient/store/models.go b/universalClient/store/models.go index 98ef87d8..b3cc331b 100644 --- a/universalClient/store/models.go +++ b/universalClient/store/models.go @@ -28,6 +28,7 @@ const ( EventTypeSignFundMigrate = "SIGN_FUND_MIGRATE" EventTypeInbound = "INBOUND" EventTypeOutbound = "OUTBOUND" + EventTypeReadRequest = "READ_REQUEST" ) // Confirmation type values. From 7c6f3c53754fe996e000d2daa0bda89345963a58 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:58:51 +0530 Subject: [PATCH 02/54] temp: proto results, will be replaced by core implementation --- universalClient/uread/types.go | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 universalClient/uread/types.go diff --git a/universalClient/uread/types.go b/universalClient/uread/types.go new file mode 100644 index 00000000..a7b2ca34 --- /dev/null +++ b/universalClient/uread/types.go @@ -0,0 +1,49 @@ +// Package uread is a TEMPORARY package: it mirrors the read-request proto types +// x/uexecutor will generate (proto/uexecutor/v1/read_request.proto + tx.proto). +// +// TODO(core): once core lands, replace every uread.* reference with the +// generated uexecutortypes equivalents and delete this package. +package uread + +// ReadRequest mirrors the pending read request tracked by x/uexecutor. +type ReadRequest struct { + RequestID string // uint256 as 0x-prefixed hex (from ReadRequested event) + ChainNamespace string // e.g. "eip155", "solana" + ChainID string // e.g. "1", "42161", "mainnet-beta" + Owner []byte // ReadSpec.account.owner (20-byte addr / 32-byte pubkey) + Query []byte // chain-specific envelope, abi.encode(...) + MinConfirmations uint16 + MaxAgeSeconds uint64 + MaxDelaySeconds uint64 + PinnedBlockHeight uint64 // height all validators must query; 0 = not pinned by core + ExpiryTimestamp int64 // unix seconds; 0 = no expiry known + CreatedAtHeight uint64 // Push chain height at which the request was created +} + +// ReadStatus is the observed outcome a validator votes on. +type ReadStatus int32 + +const ( + ReadStatusSuccess ReadStatus = 1 + ReadStatusError ReadStatus = 2 +) + +// ReadResult is the canonical observation submitted via MsgVoteReadResult. +// All fields must be byte-identical across validators for quorum. +type ReadResult struct { + Status ReadStatus + ResultData []byte + ObservedBlockHeight uint64 // block number (EVM) or slot (SVM) + ObservedBlockHash []byte // 32 bytes; empty when the chain cannot pin one deterministically + ErrorMsg string // local diagnostic only — never part of the ballot +} + +// NewErrorResult builds an ERROR observation. ResultData stays empty so all +// validators voting ERROR converge on the same ballot regardless of local error text. +func NewErrorResult(err error) *ReadResult { + msg := "" + if err != nil { + msg = err.Error() + } + return &ReadResult{Status: ReadStatusError, ErrorMsg: msg} +} From d4bbe6211201b945491c479b02ccbe4dc19eb991 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:58:51 +0530 Subject: [PATCH 03/54] temp: pushCore fetch, to be replaced by core grpc fn --- universalClient/pushcore/pushCore.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/universalClient/pushcore/pushCore.go b/universalClient/pushcore/pushCore.go index 647b548e..3de4dda0 100644 --- a/universalClient/pushcore/pushCore.go +++ b/universalClient/pushcore/pushCore.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" @@ -367,6 +368,21 @@ func (c *Client) GetAllPendingOutbounds(ctx context.Context) ([]*uexecutortypes. return resp.Entries, resp.Outbounds, nil } +// ErrReadQueriesNotAvailable is returned until the core-side pending-read query +// exists. Callers treat it as "feature not live yet", not as a failure. +var ErrReadQueriesNotAvailable = errors.New("pushcore: pending read requests query not available yet (blocked on core)") + +// GetAllPendingReadRequests retrieves pending external read requests from Push Chain. +// +// TODO(core): blocked on x/uexecutor Query/PendingReadRequests +// (proto/uexecutor/v1/query.proto). Once it lands, mirror GetAllPendingOutbounds: +// call c.uexecutorClients[idx].AllPendingReadRequests with retryWithRoundRobin, +// map uexecutortypes.ReadRequest -> uread.ReadRequest (or drop the local type +// entirely), and delete ErrReadQueriesNotAvailable. +func (c *Client) GetAllPendingReadRequests(ctx context.Context) ([]*uread.ReadRequest, error) { + return nil, ErrReadQueriesNotAvailable +} + // createGRPCConnection creates a gRPC connection with appropriate transport security. // It automatically detects whether to use TLS based on the URL scheme // and adds default port 9090 if no port is specified. From 4ebd9462c225653b5d3f9419359124e00d993f95 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:58:51 +0530 Subject: [PATCH 04/54] temp: readResult temp vote, to be replace by core impl --- universalClient/pushsigner/pushsigner.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/universalClient/pushsigner/pushsigner.go b/universalClient/pushsigner/pushsigner.go index 8e8dcbfe..f7f9dbe1 100644 --- a/universalClient/pushsigner/pushsigner.go +++ b/universalClient/pushsigner/pushsigner.go @@ -2,6 +2,7 @@ package pushsigner import ( "context" + "errors" "fmt" "strings" "sync" @@ -24,6 +25,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/pushcore" "github.com/pushchain/push-chain-node/universalClient/pushsigner/keys" + "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -132,6 +134,22 @@ func (s *Signer) VoteFundMigration(ctx context.Context, migrationID uint64, txHa return voteFundMigration(ctx, s, s.log, s.granter, migrationID, txHash, success) } +// ErrVoteReadNotAvailable is returned until the core-side vote msg exists. +var ErrVoteReadNotAvailable = errors.New("pushsigner: MsgVoteReadResult not available yet (blocked on core)") + +// VoteReadResult votes on an external read observation. +// +// TODO(core): blocked on uexecutortypes.MsgVoteReadResult +// (proto/uexecutor/v1/tx.proto). Once it lands: +// - add a voteReadResult builder in vote.go (Signer: granter, RequestId, +// Status, ResultData, ObservedBlockHeight, ObservedBlockHash) and route +// through vote() like voteInbound does; +// - ensure the validator AuthZ grant set includes the new msg type URL +// (grant_verifier.go + core-side grant creation). +func (s *Signer) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { + return "", ErrVoteReadNotAvailable +} + // signAndBroadcastAuthZTx signs and broadcasts an AuthZ transaction func (s *Signer) signAndBroadcastAuthZTx( ctx context.Context, From a11aff840d9bcc790a5d6e06b84772e4ae0af3de Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:59:02 +0530 Subject: [PATCH 05/54] feat(uclient): evm/svm read query execution - decode EvmQueryEnvelope (AccountBalance / ERC20Balance / ContractCall / StorageSlot) and SolanaQueryEnvelope (LamportBalance / SPLTokenAccount / RawAccountData) from ReadSpec.query - ExecuteRead on each chain client: deterministic pinned-height queries, canonical result encoding for identical-bytes ballot voting - new RPC methods: GetBalanceAt / GetStorageAt / GetHeaderByNumber (EVM), GetBalanceWithSlot / GetAccountInfoWithSlot (SVM) --- universalClient/externalchains/common/read.go | 51 +++++++ .../externalchains/common/read_test.go | 42 ++++++ .../externalchains/evm/read_envelope.go | 131 ++++++++++++++++++ .../externalchains/evm/read_envelope_test.go | 80 +++++++++++ .../externalchains/evm/read_executor.go | 125 +++++++++++++++++ .../externalchains/evm/rpc_client.go | 39 ++++++ .../externalchains/svm/read_envelope.go | 66 +++++++++ .../externalchains/svm/read_envelope_test.go | 34 +++++ .../externalchains/svm/read_executor.go | 99 +++++++++++++ .../externalchains/svm/rpc_client.go | 47 +++++++ 10 files changed, 714 insertions(+) create mode 100644 universalClient/externalchains/common/read.go create mode 100644 universalClient/externalchains/common/read_test.go create mode 100644 universalClient/externalchains/evm/read_envelope.go create mode 100644 universalClient/externalchains/evm/read_envelope_test.go create mode 100644 universalClient/externalchains/evm/read_executor.go create mode 100644 universalClient/externalchains/svm/read_envelope.go create mode 100644 universalClient/externalchains/svm/read_envelope_test.go create mode 100644 universalClient/externalchains/svm/read_executor.go diff --git a/universalClient/externalchains/common/read.go b/universalClient/externalchains/common/read.go new file mode 100644 index 00000000..58203b10 --- /dev/null +++ b/universalClient/externalchains/common/read.go @@ -0,0 +1,51 @@ +package common + +import ( + "context" + "fmt" + "math/big" + + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +// ChainReader executes an external read request against one chain. +// Implemented by chains/evm.Client and chains/svm.Client. +type ChainReader interface { + ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) +} + +// ReadStoreResolver resolves a CAIP-2 chain ID to that chain's event store, so +// READ_REQUEST events can be routed into the target chain's own database. +// Implemented by externalchains.Chains. +type ReadStoreResolver interface { + GetStore(chainID string) (*ChainStore, error) +} + +// CAIP2 joins a ReadSpec domain (chainNamespace, chainId) into the CAIP-2 key +// used by the chains registry, e.g. ("eip155", "1") -> "eip155:1". +func CAIP2(chainNamespace, chainID string) (string, error) { + if chainNamespace == "" || chainID == "" { + return "", fmt.Errorf("empty chain namespace or id") + } + return chainNamespace + ":" + chainID, nil +} + +// EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256). +func EncodeUint256Result(v *big.Int) ([]byte, error) { + if v == nil { + v = big.NewInt(0) + } + if v.Sign() < 0 || v.BitLen() > 256 { + return nil, fmt.Errorf("value out of uint256 range") + } + out := make([]byte, 32) + v.FillBytes(out) + return out, nil +} + +// EncodeBytes32Result canonically encodes a storage slot value as abi.encode(bytes32). +func EncodeBytes32Result(v [32]byte) ([]byte, error) { + out := make([]byte, 32) + copy(out, v[:]) + return out, nil +} diff --git a/universalClient/externalchains/common/read_test.go b/universalClient/externalchains/common/read_test.go new file mode 100644 index 00000000..63087580 --- /dev/null +++ b/universalClient/externalchains/common/read_test.go @@ -0,0 +1,42 @@ +package common + +import ( + "bytes" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncodeUint256Result(t *testing.T) { + out, err := EncodeUint256Result(big.NewInt(1_000_000)) + require.NoError(t, err) + require.Len(t, out, 32) + assert.Equal(t, big.NewInt(1_000_000), new(big.Int).SetBytes(out)) + + out, err = EncodeUint256Result(nil) + require.NoError(t, err) + assert.True(t, bytes.Equal(out, make([]byte, 32))) + + _, err = EncodeUint256Result(big.NewInt(-1)) + assert.Error(t, err) +} + +func TestEncodeBytes32Result(t *testing.T) { + var v [32]byte + v[31] = 0xff + out, err := EncodeBytes32Result(v) + require.NoError(t, err) + require.Len(t, out, 32) + assert.Equal(t, v[:], out) +} + +func TestCAIP2(t *testing.T) { + got, err := CAIP2("eip155", "1") + require.NoError(t, err) + assert.Equal(t, "eip155:1", got) + + _, err = CAIP2("", "1") + assert.Error(t, err) +} diff --git a/universalClient/externalchains/evm/read_envelope.go b/universalClient/externalchains/evm/read_envelope.go new file mode 100644 index 00000000..8580ba0e --- /dev/null +++ b/universalClient/externalchains/evm/read_envelope.go @@ -0,0 +1,131 @@ +package evm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + ethcommon "github.com/ethereum/go-ethereum/common" +) + +// evmQueryType mirrors the EvmQueryEnvelope enum from the read spec. +type evmQueryType uint8 + +const ( + evmQueryAccountBalance evmQueryType = 0 + evmQueryERC20Balance evmQueryType = 1 + evmQueryContractCall evmQueryType = 2 + evmQueryStorageSlot evmQueryType = 3 +) + +// evmBlockRefType mirrors the EvmBlockRefType enum. Only AT_NUMBER exists in v1. +type evmBlockRefType uint8 + +const evmBlockRefAtNumber evmBlockRefType = 0 + +// evmQueryEnvelope is the decoded abi.encode(EvmQueryEnvelope) query. +type evmQueryEnvelope struct { + QueryType evmQueryType + RefType evmBlockRefType + BlockNumber uint64 + Payload []byte +} + +var ( + evmEnvelopeArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "queryType", Type: "uint8"}, + {Name: "blockRef", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "refType", Type: "uint8"}, + {Name: "blockNumber", Type: "uint64"}, + }}, + {Name: "payload", Type: "bytes"}, + }}) + + addressArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}) + addressPairArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "address"}) + addressBytesArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "bytes"}) + addressBytes32Args = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "bytes32"}) +) + +func mustReadArgs(marshalings ...abi.ArgumentMarshaling) abi.Arguments { + args := make(abi.Arguments, 0, len(marshalings)) + for i, m := range marshalings { + if m.Name == "" { + m.Name = fmt.Sprintf("arg%d", i) + } + typ, err := abi.NewType(m.Type, "", m.Components) + if err != nil { + panic(fmt.Sprintf("evm: invalid abi type %q: %v", m.Type, err)) + } + args = append(args, abi.Argument{Name: m.Name, Type: typ}) + } + return args +} + +type rawEvmEnvelope struct { + QueryType uint8 + BlockRef struct { + RefType uint8 + BlockNumber uint64 + } + Payload []byte +} + +// decodeEvmQueryEnvelope decodes ReadSpec.query for eip155 chains. +func decodeEvmQueryEnvelope(query []byte) (*evmQueryEnvelope, error) { + vals, err := evmEnvelopeArgs.Unpack(query) + if err != nil { + return nil, fmt.Errorf("failed to unpack EvmQueryEnvelope: %w", err) + } + raw := *abi.ConvertType(vals[0], new(rawEvmEnvelope)).(*rawEvmEnvelope) + + env := &evmQueryEnvelope{ + QueryType: evmQueryType(raw.QueryType), + RefType: evmBlockRefType(raw.BlockRef.RefType), + BlockNumber: raw.BlockRef.BlockNumber, + Payload: raw.Payload, + } + if env.QueryType > evmQueryStorageSlot { + return nil, fmt.Errorf("unknown EvmQueryType %d", env.QueryType) + } + if env.RefType != evmBlockRefAtNumber { + return nil, fmt.Errorf("unsupported EvmBlockRefType %d", env.RefType) + } + return env, nil +} + +// decodeAccountBalancePayload decodes abi.encode(address target). +func decodeAccountBalancePayload(payload []byte) (ethcommon.Address, error) { + vals, err := addressArgs.Unpack(payload) + if err != nil { + return ethcommon.Address{}, fmt.Errorf("failed to unpack AccountBalance payload: %w", err) + } + return vals[0].(ethcommon.Address), nil +} + +// decodeERC20BalancePayload decodes abi.encode(address token, address owner). +func decodeERC20BalancePayload(payload []byte) (token, owner ethcommon.Address, err error) { + vals, err := addressPairArgs.Unpack(payload) + if err != nil { + return ethcommon.Address{}, ethcommon.Address{}, fmt.Errorf("failed to unpack ERC20Balance payload: %w", err) + } + return vals[0].(ethcommon.Address), vals[1].(ethcommon.Address), nil +} + +// decodeContractCallPayload decodes abi.encode(address target, bytes callData). +func decodeContractCallPayload(payload []byte) (ethcommon.Address, []byte, error) { + vals, err := addressBytesArgs.Unpack(payload) + if err != nil { + return ethcommon.Address{}, nil, fmt.Errorf("failed to unpack ContractCall payload: %w", err) + } + return vals[0].(ethcommon.Address), vals[1].([]byte), nil +} + +// decodeStorageSlotPayload decodes abi.encode(address contractAddr, bytes32 slot). +func decodeStorageSlotPayload(payload []byte) (ethcommon.Address, ethcommon.Hash, error) { + vals, err := addressBytes32Args.Unpack(payload) + if err != nil { + return ethcommon.Address{}, ethcommon.Hash{}, fmt.Errorf("failed to unpack StorageSlot payload: %w", err) + } + slot := vals[1].([32]byte) + return vals[0].(ethcommon.Address), ethcommon.Hash(slot), nil +} diff --git a/universalClient/externalchains/evm/read_envelope_test.go b/universalClient/externalchains/evm/read_envelope_test.go new file mode 100644 index 00000000..af04832a --- /dev/null +++ b/universalClient/externalchains/evm/read_envelope_test.go @@ -0,0 +1,80 @@ +package evm + +import ( + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func packEvmEnvelope(t *testing.T, queryType, refType uint8, blockNumber uint64, payload []byte) []byte { + t.Helper() + data, err := evmEnvelopeArgs.Pack(rawEvmEnvelope{ + QueryType: queryType, + BlockRef: struct { + RefType uint8 + BlockNumber uint64 + }{refType, blockNumber}, + Payload: payload, + }) + require.NoError(t, err) + return data +} + +func TestDecodeEvmQueryEnvelope(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + env, err := decodeEvmQueryEnvelope(packEvmEnvelope(t, uint8(evmQueryAccountBalance), 0, 1234, payload)) + require.NoError(t, err) + assert.Equal(t, evmQueryAccountBalance, env.QueryType) + assert.Equal(t, evmBlockRefAtNumber, env.RefType) + assert.Equal(t, uint64(1234), env.BlockNumber) + + decoded, err := decodeAccountBalancePayload(env.Payload) + require.NoError(t, err) + assert.Equal(t, target, decoded) +} + +func TestDecodeEvmQueryEnvelope_Invalid(t *testing.T) { + _, err := decodeEvmQueryEnvelope([]byte{0x01, 0x02}) + assert.Error(t, err) + + // unknown query type + _, err = decodeEvmQueryEnvelope(packEvmEnvelope(t, 9, 0, 0, nil)) + assert.Error(t, err) + + // unknown block ref type + _, err = decodeEvmQueryEnvelope(packEvmEnvelope(t, 0, 7, 0, nil)) + assert.Error(t, err) +} + +func TestDecodeEvmPayloads(t *testing.T) { + token := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") + owner := ethcommon.HexToAddress("0x3333333333333333333333333333333333333333") + + erc20Payload, err := addressPairArgs.Pack(token, owner) + require.NoError(t, err) + gotToken, gotOwner, err := decodeERC20BalancePayload(erc20Payload) + require.NoError(t, err) + assert.Equal(t, token, gotToken) + assert.Equal(t, owner, gotOwner) + + callData := []byte{0xde, 0xad, 0xbe, 0xef} + callPayload, err := addressBytesArgs.Pack(token, callData) + require.NoError(t, err) + gotTarget, gotData, err := decodeContractCallPayload(callPayload) + require.NoError(t, err) + assert.Equal(t, token, gotTarget) + assert.Equal(t, callData, gotData) + + slot := [32]byte{0x0a} + slotPayload, err := addressBytes32Args.Pack(token, slot) + require.NoError(t, err) + gotAddr, gotSlot, err := decodeStorageSlotPayload(slotPayload) + require.NoError(t, err) + assert.Equal(t, token, gotAddr) + assert.Equal(t, ethcommon.Hash(slot), gotSlot) +} diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go new file mode 100644 index 00000000..3f1a950b --- /dev/null +++ b/universalClient/externalchains/evm/read_executor.go @@ -0,0 +1,125 @@ +package evm + +import ( + "context" + "fmt" + "math/big" + + ethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +// balanceOfSelector is the 4-byte selector for balanceOf(address). +var balanceOfSelector = []byte{0x70, 0xa0, 0x82, 0x31} + +// ExecuteRead implements common.ChainReader for EVM chains. +// All validators must produce byte-identical results, so every query runs at a +// deterministic block height. +func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + env, err := decodeEvmQueryEnvelope(req.Query) + if err != nil { + return uread.NewErrorResult(err), nil + } + + height, err := c.resolveReadHeight(ctx, req, env) + if err != nil { + return nil, err + } + blockNum := new(big.Int).SetUint64(height) + + header, err := c.rpcClient.GetHeaderByNumber(ctx, blockNum) + if err != nil { + return nil, fmt.Errorf("failed to fetch header at %d: %w", height, err) + } + + var resultData []byte + switch env.QueryType { + case evmQueryAccountBalance: + target, decErr := decodeAccountBalancePayload(env.Payload) + if decErr != nil { + return uread.NewErrorResult(decErr), nil + } + balance, rpcErr := c.rpcClient.GetBalanceAt(ctx, target, blockNum) + if rpcErr != nil { + return nil, rpcErr + } + resultData, err = common.EncodeUint256Result(balance) + + case evmQueryERC20Balance: + token, owner, decErr := decodeERC20BalancePayload(env.Payload) + if decErr != nil { + return uread.NewErrorResult(decErr), nil + } + callData := append(append([]byte{}, balanceOfSelector...), ethcommon.LeftPadBytes(owner.Bytes(), 32)...) + ret, rpcErr := c.rpcClient.CallContract(ctx, token, callData, blockNum) + if rpcErr != nil { + return nil, rpcErr + } + if len(ret) < 32 { + return uread.NewErrorResult(fmt.Errorf("balanceOf returned %d bytes", len(ret))), nil + } + resultData, err = common.EncodeUint256Result(new(big.Int).SetBytes(ret[:32])) + + case evmQueryContractCall: + target, callData, decErr := decodeContractCallPayload(env.Payload) + if decErr != nil { + return uread.NewErrorResult(decErr), nil + } + ret, rpcErr := c.rpcClient.CallContract(ctx, target, callData, blockNum) + if rpcErr != nil { + // eth_call reverts are deterministic at a pinned height — observable as ERROR. + return uread.NewErrorResult(rpcErr), nil + } + resultData = ret + + case evmQueryStorageSlot: + target, slot, decErr := decodeStorageSlotPayload(env.Payload) + if decErr != nil { + return uread.NewErrorResult(decErr), nil + } + value, rpcErr := c.rpcClient.GetStorageAt(ctx, target, slot, blockNum) + if rpcErr != nil { + return nil, rpcErr + } + var slotValue [32]byte + copy(slotValue[32-min(len(value), 32):], value) + resultData, err = common.EncodeBytes32Result(slotValue) + + default: + return uread.NewErrorResult(fmt.Errorf("unknown EvmQueryType %d", env.QueryType)), nil + } + if err != nil { + return uread.NewErrorResult(err), nil + } + + return &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: resultData, + ObservedBlockHeight: height, + ObservedBlockHash: header.Hash().Bytes(), + }, nil +} + +// resolveReadHeight picks the deterministic block height for a read. +// TODO(core): once x/uexecutor pins the height at request creation, +// PinnedBlockHeight is always set and the fallback below must be removed — +// latest-minConfirmations is NOT identical across validators. +func (c *Client) resolveReadHeight(ctx context.Context, req *uread.ReadRequest, env *evmQueryEnvelope) (uint64, error) { + if req.PinnedBlockHeight > 0 { + return req.PinnedBlockHeight, nil + } + if env.BlockNumber > 0 { + return env.BlockNumber, nil + } + latest, err := c.rpcClient.GetLatestBlock(ctx) + if err != nil { + return 0, fmt.Errorf("failed to get latest block: %w", err) + } + conf := uint64(req.MinConfirmations) + if latest <= conf { + return 0, fmt.Errorf("chain height %d below min confirmations %d", latest, conf) + } + return latest - conf, nil +} diff --git a/universalClient/externalchains/evm/rpc_client.go b/universalClient/externalchains/evm/rpc_client.go index b8c83d04..433a8ef3 100644 --- a/universalClient/externalchains/evm/rpc_client.go +++ b/universalClient/externalchains/evm/rpc_client.go @@ -182,6 +182,45 @@ func (rc *RPCClient) GetBalance(ctx context.Context, address ethcommon.Address) return balance, err } +// GetBalanceAt fetches the native token balance for an address at a specific block. +func (rc *RPCClient) GetBalanceAt(ctx context.Context, address ethcommon.Address, blockNumber *big.Int) (*big.Int, error) { + var balance *big.Int + err := rc.executeWithFailover(ctx, "get_balance_at", func(client *ethclient.Client) error { + callCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + var innerErr error + balance, innerErr = client.BalanceAt(callCtx, address, blockNumber) + return innerErr + }) + return balance, err +} + +// GetStorageAt fetches a storage slot value for a contract at a specific block. +func (rc *RPCClient) GetStorageAt(ctx context.Context, address ethcommon.Address, slot ethcommon.Hash, blockNumber *big.Int) ([]byte, error) { + var value []byte + err := rc.executeWithFailover(ctx, "get_storage_at", func(client *ethclient.Client) error { + callCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + var innerErr error + value, innerErr = client.StorageAt(callCtx, address, slot, blockNumber) + return innerErr + }) + return value, err +} + +// GetHeaderByNumber fetches a block header by number. +func (rc *RPCClient) GetHeaderByNumber(ctx context.Context, blockNumber *big.Int) (*types.Header, error) { + var header *types.Header + err := rc.executeWithFailover(ctx, "get_header_by_number", func(client *ethclient.Client) error { + callCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + var innerErr error + header, innerErr = client.HeaderByNumber(callCtx, blockNumber) + return innerErr + }) + return header, err +} + // FilterLogs fetches logs matching the filter query func (rc *RPCClient) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { var logs []types.Log diff --git a/universalClient/externalchains/svm/read_envelope.go b/universalClient/externalchains/svm/read_envelope.go new file mode 100644 index 00000000..b32d172b --- /dev/null +++ b/universalClient/externalchains/svm/read_envelope.go @@ -0,0 +1,66 @@ +package svm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +// solanaQueryType mirrors the SolanaQueryEnvelope enum from the read spec. +type solanaQueryType uint8 + +const ( + solanaQueryLamportBalance solanaQueryType = 0 + solanaQuerySPLTokenAccount solanaQueryType = 1 + solanaQueryRawAccountData solanaQueryType = 2 +) + +// solanaQueryEnvelope is the decoded abi.encode(SolanaQueryEnvelope) query — +// ABI-encoded because it is built by UniversalCallback.sol on Push EVM. +// The target account pubkey travels in ReadSpec.account.owner (32 bytes), not here. +type solanaQueryEnvelope struct { + QueryType solanaQueryType + MinSlot uint64 + Payload []byte // empty for all v1 query types +} + +var svmEnvelopeArgs = func() abi.Arguments { + tupleTy, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "queryType", Type: "uint8"}, + {Name: "slotRef", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "minSlot", Type: "uint64"}, + }}, + {Name: "payload", Type: "bytes"}, + }) + if err != nil { + panic(fmt.Sprintf("svm: invalid envelope abi type: %v", err)) + } + return abi.Arguments{{Name: "envelope", Type: tupleTy}} +}() + +type rawSvmEnvelope struct { + QueryType uint8 + SlotRef struct { + MinSlot uint64 + } + Payload []byte +} + +// decodeSolanaQueryEnvelope decodes ReadSpec.query for solana chains. +func decodeSolanaQueryEnvelope(query []byte) (*solanaQueryEnvelope, error) { + vals, err := svmEnvelopeArgs.Unpack(query) + if err != nil { + return nil, fmt.Errorf("failed to unpack SolanaQueryEnvelope: %w", err) + } + raw := *abi.ConvertType(vals[0], new(rawSvmEnvelope)).(*rawSvmEnvelope) + + env := &solanaQueryEnvelope{ + QueryType: solanaQueryType(raw.QueryType), + MinSlot: raw.SlotRef.MinSlot, + Payload: raw.Payload, + } + if env.QueryType > solanaQueryRawAccountData { + return nil, fmt.Errorf("unknown SolanaQueryType %d", env.QueryType) + } + return env, nil +} diff --git a/universalClient/externalchains/svm/read_envelope_test.go b/universalClient/externalchains/svm/read_envelope_test.go new file mode 100644 index 00000000..d5103053 --- /dev/null +++ b/universalClient/externalchains/svm/read_envelope_test.go @@ -0,0 +1,34 @@ +package svm + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDecodeSolanaQueryEnvelope(t *testing.T) { + data, err := svmEnvelopeArgs.Pack(rawSvmEnvelope{ + QueryType: uint8(solanaQuerySPLTokenAccount), + SlotRef: struct { + MinSlot uint64 + }{42}, + Payload: nil, + }) + require.NoError(t, err) + + env, err := decodeSolanaQueryEnvelope(data) + require.NoError(t, err) + assert.Equal(t, solanaQuerySPLTokenAccount, env.QueryType) + assert.Equal(t, uint64(42), env.MinSlot) + assert.Empty(t, env.Payload) + + _, err = decodeSolanaQueryEnvelope([]byte{0x00}) + assert.Error(t, err) + + // unknown query type + bad, err := svmEnvelopeArgs.Pack(rawSvmEnvelope{QueryType: 9}) + require.NoError(t, err) + _, err = decodeSolanaQueryEnvelope(bad) + assert.Error(t, err) +} diff --git a/universalClient/externalchains/svm/read_executor.go b/universalClient/externalchains/svm/read_executor.go new file mode 100644 index 00000000..4b305e29 --- /dev/null +++ b/universalClient/externalchains/svm/read_executor.go @@ -0,0 +1,99 @@ +package svm + +import ( + "context" + "encoding/binary" + "fmt" + "math/big" + + "github.com/gagliardetto/solana-go" + + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +// splTokenAmountOffset is the byte offset of the u64 amount in an SPL token account. +const splTokenAmountOffset = 64 + +// ExecuteRead implements common.ChainReader for Solana chains. +// +// Determinism caveat: Solana RPC cannot query state at an exact past slot, only +// ">= minSlot" via minContextSlot, so ObservedBlockHeight may differ across +// validators. TODO(core): ballot key must cover ResultData only (drop +// slot/hash) for solana, or quorum will never converge — flagged in +// docs/read-from-chains-implementation-plan.md. +func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + env, err := decodeSolanaQueryEnvelope(req.Query) + if err != nil { + return uread.NewErrorResult(err), nil + } + + if len(req.Owner) != solana.PublicKeyLength { + return uread.NewErrorResult(fmt.Errorf("owner must be a 32-byte pubkey, got %d bytes", len(req.Owner))), nil + } + account := solana.PublicKeyFromBytes(req.Owner) + + minSlot := max(env.MinSlot, req.PinnedBlockHeight) + + switch env.QueryType { + case solanaQueryLamportBalance: + balance, slot, rpcErr := c.rpcClient.GetBalanceWithSlot(ctx, account) + if rpcErr != nil { + return nil, rpcErr + } + if slot < minSlot { + return nil, fmt.Errorf("observed slot %d below min slot %d", slot, minSlot) + } + resultData, encErr := common.EncodeUint256Result(new(big.Int).SetUint64(balance)) + if encErr != nil { + return uread.NewErrorResult(encErr), nil + } + return &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: resultData, + ObservedBlockHeight: slot, + }, nil + + case solanaQuerySPLTokenAccount: + data, owner, found, slot, rpcErr := c.rpcClient.GetAccountInfoWithSlot(ctx, account, minSlot) + if rpcErr != nil { + return nil, rpcErr + } + if !found { + return uread.NewErrorResult(fmt.Errorf("token account %s not found", account)), nil + } + if !owner.Equals(solana.TokenProgramID) && !owner.Equals(solana.Token2022ProgramID) { + return uread.NewErrorResult(fmt.Errorf("account %s is not owned by a token program", account)), nil + } + if len(data) < splTokenAmountOffset+8 { + return uread.NewErrorResult(fmt.Errorf("token account data too short: %d bytes", len(data))), nil + } + amount := binary.LittleEndian.Uint64(data[splTokenAmountOffset : splTokenAmountOffset+8]) + resultData, encErr := common.EncodeUint256Result(new(big.Int).SetUint64(amount)) + if encErr != nil { + return uread.NewErrorResult(encErr), nil + } + return &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: resultData, + ObservedBlockHeight: slot, + }, nil + + case solanaQueryRawAccountData: + data, _, found, slot, rpcErr := c.rpcClient.GetAccountInfoWithSlot(ctx, account, minSlot) + if rpcErr != nil { + return nil, rpcErr + } + if !found { + return uread.NewErrorResult(fmt.Errorf("account %s not found", account)), nil + } + return &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: data, + ObservedBlockHeight: slot, + }, nil + + default: + return uread.NewErrorResult(fmt.Errorf("unknown SolanaQueryType %d", env.QueryType)), nil + } +} diff --git a/universalClient/externalchains/svm/rpc_client.go b/universalClient/externalchains/svm/rpc_client.go index fb788b7a..ee1ac9a0 100644 --- a/universalClient/externalchains/svm/rpc_client.go +++ b/universalClient/externalchains/svm/rpc_client.go @@ -386,6 +386,53 @@ func (rc *RPCClient) GetAccountData(ctx context.Context, pubkey solana.PublicKey return accountData, err } +// GetBalanceWithSlot fetches the lamport balance for an account at finalized +// commitment, returning the context slot the value was observed at. +func (rc *RPCClient) GetBalanceWithSlot(ctx context.Context, pubkey solana.PublicKey) (uint64, uint64, error) { + var balance, slot uint64 + err := rc.executeWithFailover(ctx, "get_balance", func(client *rpc.Client) error { + resp, innerErr := client.GetBalance(ctx, pubkey, rpc.CommitmentFinalized) + if innerErr != nil { + return innerErr + } + balance = resp.Value + slot = resp.RPCContext.Context.Slot + return nil + }) + return balance, slot, err +} + +// GetAccountInfoWithSlot fetches account data at finalized commitment with an +// optional minimum context slot, returning the context slot it was observed at. +// found=false means the account does not exist (a valid, votable observation). +func (rc *RPCClient) GetAccountInfoWithSlot(ctx context.Context, pubkey solana.PublicKey, minContextSlot uint64) (data []byte, owner solana.PublicKey, found bool, slot uint64, err error) { + err = rc.executeWithFailover(ctx, "get_account_info", func(client *rpc.Client) error { + opts := &rpc.GetAccountInfoOpts{Commitment: rpc.CommitmentFinalized} + if minContextSlot > 0 { + opts.MinContextSlot = &minContextSlot + } + resp, innerErr := client.GetAccountInfoWithOpts(ctx, pubkey, opts) + if innerErr != nil { + if innerErr == rpc.ErrNotFound { + found = false + return nil + } + return innerErr + } + if resp.Value == nil { + found = false + slot = resp.RPCContext.Context.Slot + return nil + } + found = true + data = resp.Value.Data.GetBinary() + owner = resp.Value.Owner + slot = resp.RPCContext.Context.Slot + return nil + }) + return data, owner, found, slot, err +} + // Close closes all RPC connections func (rc *RPCClient) Close() { rc.mu.Lock() From 7eede40556591575f474da409002d3baaf655e6f Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 16:59:11 +0530 Subject: [PATCH 06/54] feat(uclient): route read requests to target chains and vote results - pushwatcher listener polls pending reads via gRPC and routes each READ_REQUEST event into the target chain's DB (Chains.GetStore) - EventProcessor gains a READ_REQUEST branch: execute on the chain's own reader -> vote -> COMPLETED; corrupt/expired -> REVERTED; transient -> retry - signer narrowed to consumer-side VoteSigner interface; evm/svm clients pass themselves as reader - implementation plan in docs/read-from-chains-implementation-plan.md --- docs/read-from-chains-implementation-plan.md | 164 +++++++++++ universalClient/core/client.go | 2 + universalClient/externalchains/chains.go | 23 ++ .../externalchains/common/event_processor.go | 102 +++++-- .../common/event_processor_test.go | 267 +++++++++++++----- universalClient/externalchains/evm/client.go | 3 + universalClient/externalchains/svm/client.go | 3 + universalClient/pushwatcher/client.go | 5 +- universalClient/pushwatcher/client_test.go | 32 +-- universalClient/pushwatcher/event_listener.go | 90 +++++- .../pushwatcher/event_listener_test.go | 14 +- universalClient/pushwatcher/event_parser.go | 22 ++ .../pushwatcher/event_parser_test.go | 1 + 13 files changed, 597 insertions(+), 131 deletions(-) create mode 100644 docs/read-from-chains-implementation-plan.md diff --git a/docs/read-from-chains-implementation-plan.md b/docs/read-from-chains-implementation-plan.md new file mode 100644 index 00000000..14586282 --- /dev/null +++ b/docs/read-from-chains-implementation-plan.md @@ -0,0 +1,164 @@ +# Read from Chains — Core + universalClient Implementation Plan + +## References +- Spec v1: `read_v1.pdf` (UniversalCallback + MetaCallbackSpec model — superseded) +- Spec v2: `read_v2.pdf` (UniversalReadClient model — **adopted**) +- Contracts: [pushchain/push-chain-core-contracts@51e5aeb](https://github.com/pushchain/push-chain-core-contracts/commit/51e5aeb2dd0cc0ecd23134f3b313a455ca52fdde) (`read-state-v1`) + +## What the contracts already define (fixed surface we integrate against) + +- `UniversalCallback.sol` (singleton, upgradeable): + - `requestExternalReadSelf(ReadSpec spec, bytes4 callbackSelector, uint64 callbackGasLimit) payable → requestId` + - validates: non-empty account/query, `minConfirmations >= 1`, `maxAgeSeconds/maxDelaySeconds != 0`, `supportedDomains[ns][id]`, `callbackGasLimit <= 1_000_000`, `fee <= msg.value <= spec.maxFee` + - `requestId = keccak256(block.chainid, block.number, address(this), keccak256(spec), nonce++)` + - emits **`ReadRequested(uint256 indexed requestId, ReadSpec spec, address indexed callbackTarget, address indexed originalFunder, uint256 feesDeposited)`** + - `fulfillExternalCallback(uint256 requestId, bytes resultData, uint64 observedBlockHeight, bytes32 observedBlockHash)` — **`onlyUEModule`** + - calls `callbackTarget.call{gas}(selector, requestId, resultData)`; handles fee split (protocol fee → VaultPC, refund → funder) internally + - emits `ReadFulfilled` / `CallbackFailed` + - `expireExternalRead(uint256 requestId)` — **`onlyUEModule`**, emits `RequestExpired` + - `UNIVERSAL_EXECUTOR_MODULE = 0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7` (hardcoded) +- `ReadTypes.sol`: + - `ReadSpec { UniversalAccountId account; bytes query; uint16 minConfirmations; uint64 maxAgeSeconds; uint64 maxDelaySeconds; uint256 maxFee; }` + - `BALLOT_OBSERVATION_TYPE_READ_REQUEST = 0x3dad9a0d…` — contracts expect a matching ballot type in core +- `UniversalReadClient.sol` — app-side base (`_requestRead` / `onUniversalData` / `_onReadResult`, `_localContext` storage); no core/uClient work needed +- `UniversalCore.sol` — new `readBaseFeeByChainNamespace[ns][id]` + `updateReadBaseFeeByChain` (admin) +- Query envelopes (spec v1, still applies): `EvmQueryEnvelope` (AccountBalance / ERC20Balance / ContractCall / StorageSlot + `EvmBlockRef`), `SolanaQueryEnvelope` (LamportBalance / SPLTokenAccount / RawAccountData + `minSlot`), `Web2QueryEnvelope` (GET/POST) + +## End-to-end flow (target) + +1. App inherits `UniversalReadClient`, calls `_requestRead` mid-execution → `ReadRequested` emitted on Push EVM +2. `x/uexecutor` `PostTxProcessing` hook decodes the event in-block → stores `PendingReadRequest` +3. universalClient (each validator) polls pending reads via gRPC → executes the query envelope against the external chain RPC → canonical-encodes result +4. Validator submits `MsgVoteReadResult` → uvalidator ballot; identical `(resultData, height, hash)` → same ballot key → >2/3 quorum +5. On finalization, uexecutor calls `fulfillExternalCallback` on `UniversalCallback` via module EVM call +6. Expiry: request not finalized within `maxDelaySeconds` → EndBlocker calls `expireExternalRead` + +--- + +## Core (`push-chain-node`) changes + +### 1. Proto (`proto/…` + buf regen) + +- `proto/uvalidator/v1/ballot.proto` + - add `BALLOT_OBSERVATION_TYPE_READ_REQUEST` to `BallotObservationType` +- `proto/uexecutor/v1/` (new `read_request.proto` + `tx.proto` + `query.proto`) + - `ReadRequest` type: `request_id (bytes/hex)`, decoded `ReadSpec` fields (`chain_namespace`, `chain_id`, `owner`, `query`, `min_confirmations`, `max_age_seconds`, `max_delay_seconds`), `callback_target`, `pinned_block_height`, `created_at_height`, `expiry_timestamp`, `status (PENDING | FULFILLED | EXPIRED)` + - `MsgVoteReadResult { signer, request_id, result_data, observed_block_height, observed_block_hash, status (SUCCESS | ERROR) }` + - `Query/PendingReadRequests` (paginated) — mirror `GetAllPendingOutbounds` + +### 2. Event detection (`x/uexecutor`) + +- `types/events.go`: add `ReadRequestedEventSig` (topic0 of the event above) +- `types/gateway_pc_event_decode.go`: add `DecodeReadRequestedFromLog` (ABI-decode `ReadSpec` tuple from log data) +- `keeper/evm_hooks.go` `PostTxProcessing`: match logs from the `UniversalCallback` address → decode → `CreatePendingReadRequest` + - follows the existing outbound-detection precedent (`BuildOutboundsFromReceipt`); no Push-EVM log polling needed in uClient +- `x/uregistry`: register `UNIVERSAL_CALLBACK` in `SYSTEM_CONTRACTS` (address source for hook filtering + callback calls) + +### 3. Pending-read storage (`x/uexecutor/keeper`) + +- new `Keeper.PendingReadRequests` collection + `keeper/pending_read_request.go` (CRUD, mirror `pending_outbound.go`) +- **Pin the query height at creation**: `pinned_block_height = ChainMeta[chain].LastAppliedChainHeight − spec.minConfirmations` (clamped) + - all validators query the same height → identical bytes; satisfies the spec rule "block taken must be below gas-oracle minimum" + - reject/park request if no ChainMeta exists for the chain +- set `expiry_timestamp = block.time + maxDelaySeconds` + +### 4. Voting (`x/uexecutor`) + +- `types/msg_vote_read_result.go` — ValidateBasic (mirror `msg_vote_inbound.go`) +- ballot key: `GetReadBallotKey = hash(request_id ‖ status ‖ result_data ‖ observed_block_height ‖ observed_block_hash)` — identical-bytes quorum +- `keeper/voting.go`: `VoteOnReadBallot` → `uvalidatorKeeper.VoteOnBallot` with the new ballot type, threshold `(2*validators)/3 + 1` (same as inbound) +- `keeper/msg_vote_read_result.go`: + - reject if request unknown / not PENDING / past expiry + - on finalizing vote (SUCCESS): `CallFulfillExternalCallback`, mark FULFILLED + - on finalizing vote (ERROR quorum — e.g. query invalid, target chain reorged): mark EXPIRED + `CallExpireExternalRead` (refund path) +- `keeper/ballot_hooks.go`: handle terminal FAILED/EXPIRED ballots for the new type (cleanup) + +### 5. EVM callback (`x/uexecutor`) + +- `types/abi.go`: add `UniversalCallbackABI` const + `ParseUniversalCallbackABI` (only `fulfillExternalCallback`, `expireExternalRead`) +- `keeper/evm.go`: `CallFulfillExternalCallback(...)` + `CallExpireExternalRead(...)` via `DerivedEVMCall` (template: `CallExecuteUniversalTx` / `CallUniversalCoreSetChainMeta`; uses `ModuleAccountNonce`) +- gas: callback gas is bounded on the contract side (`callbackGasLimit ≤ 1M`); give the module call a fixed generous limit +- **verify** module account EVM address == `0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7` (contract hardcodes it); mismatch = every fulfill reverts + +### 6. Expiry sweep + +- `x/uexecutor` EndBlocker (`abci.go`): iterate PENDING reads with `expiry_timestamp < block.time` → `CallExpireExternalRead` → mark EXPIRED + - deterministic on-chain, no vote needed + - bound per-block work (process N per block) to avoid unbounded EndBlocker gas + +### 7. Queries / CLI + +- `keeper/grpc_query.go` (or new file): `PendingReadRequests`, `ReadRequest(id)` +- autocli entries for inspection + +--- + +## universalClient changes + +> **Status: implemented** (UV side done; items marked `TODO(core)` are stubbed and unblock mechanically once core lands — grep `TODO(core)` in `universalClient/`). +> +> - `universalClient/uread/` — **temporary package**: only proto-mirror types (`ReadRequest`/`ReadStatus`/`ReadResult`); delete it once core proto lands by swapping every `uread.*` reference to `uexecutortypes.*` +> - `externalchains/common/read.go` — shared permanent bits: `ChainReader`/`ChainResolver` interfaces, `CAIP2`, canonical result encoders (`EncodeUint256Result`/`EncodeBytes32Result`) +> - `externalchains/evm/read_envelope.go` + `read_executor.go` + RPC additions (`GetBalanceAt`, `GetStorageAt`, `GetHeaderByNumber`) — all 4 query types at pinned height (**TODO(core): remove latest−minConfirmations fallback once core pins height**) +> - `externalchains/svm/read_envelope.go` + `read_executor.go` + RPC additions (`GetBalanceWithSlot`, `GetAccountInfoWithSlot`) — all 3 query types, minContextSlot semantics +> - `universalClient/pushwatcher/` (moved out of the chains manager — push is core-managed, not a registry chain) — `event_listener.go` + `event_parser.go` fetch pending reads via gRPC and **route each `READ_REQUEST` event into the target chain's DB** (`common.ReadStoreResolver`, implemented by `chains.Chains.GetStore`); requests for unserved chains are skipped and retried next poll (core re-serves pending requests; expiry is the backstop) +> - `externalchains/common/event_processor.go` — original type-switch shape kept; gained a `READ_REQUEST` branch (`processReadRequestEvent`: execute on the chain's own `ChainReader` → vote → COMPLETED; corrupt/expired → REVERTED; transient → retry), a `reader ChainReader` constructor param (evm/svm pass the client itself), and the consumer-side `VoteSigner` interface; push client has no processor at all +> - `pushcore/pushCore.go` `GetAllPendingReadRequests` (**TODO(core): wire to Query/PendingReadRequests**; returns sentinel until then, processor idles silently) +> - `pushsigner/pushsigner.go` `VoteReadResult` (**TODO(core): build MsgVoteReadResult in vote.go + add to AuthZ grant set**) +> - wiring: push client is owned by `core/client.go` (not the chains manager) — core opens the push DB once (shared with TSS), creates `push.NewClient(..., chainsManager)` and manages its lifecycle; `externalchains.Chains` only manages registry-driven external chains and implements `GetStore` for read routing + +### 1. pushcore (`universalClient/pushcore/pushCore.go`) + +- `GetAllPendingReadRequests()` — new gRPC query wrapper (mirror `GetAllPendingOutbounds`) + +### 2. pushsigner (`universalClient/pushsigner/`) + +- `VoteReadResult(ctx, msg)` — build `MsgVoteReadResult`, AuthZ-wrap, sign, broadcast (mirror `VoteInbound`) +- add msg type to AuthZ grant set (hot-key authorization for the new msg URL) + +### 3. Read worker (`universalClient/chains/push/` — new component) + +- new `read_request_processor.go` on the Push client (alongside `event_listener.go`): + - poll `GetAllPendingReadRequests` on the existing polling interval + - local store dedup (per-chain SQLite, reuse `common.ChainStore`) so a request is executed/voted once; retry until vote tx confirmed + - skip requests already past `expiry_timestamp` +- **query executor** (new `universalClient/readexecutor/` or under `chains/common/`): + - resolve target chain client: `chainNamespace + ":" + chainId` → CAIP-2 → existing `Chains` registry RPC client + - decode envelope by namespace: + - `eip155` → `EvmQueryEnvelope`: AccountBalance → `eth_getBalance`, ERC20Balance → `balanceOf` via `eth_call`, ContractCall → `eth_call`, StorageSlot → `eth_getStorageAt` — all at `pinned_block_height`; fetch `observedBlockHash` for that height + - `solana` → `SolanaQueryEnvelope`: LamportBalance / SPLTokenAccount / RawAccountData via `getAccountInfo`/`getBalance` with `minContextSlot = pinned height`; observed slot + blockhash from response context + - `web2` → **out of scope for v1** (non-deterministic responses; needs canonicalization design) — vote ERROR if received + - canonical result encoding (must be byte-identical across validators): + - AccountBalance/LamportBalance → `abi.encode(uint256)` + - ERC20Balance/SPLTokenAccount → `abi.encode(uint256)` + - ContractCall → raw returndata + - StorageSlot → `abi.encode(bytes32)`; RawAccountData → raw account bytes + - on RPC/decode failure after retries → `VoteReadResult(status = ERROR)` +- interaction with per-chain clients: reads target chains uClient already watches (registry-driven); if a supported domain has no chain client, vote ERROR + +### 4. Config (`universalClient/config/`) + +- optional: `read_polling_interval_seconds` per Push chain entry (else reuse `event_polling_interval_seconds`) +- no new per-external-chain config — reuse existing `rpc_urls` + +--- + +## Cross-cutting decisions / open questions + +- **Height pinning source**: plan uses ChainMeta (gas oracle) height at request time; confirm ChainMeta exists for all chains that will be `supportedDomains` on the contract (contract-side whitelist and core-side ChainMeta must stay in sync — no core check enforces this) +- **`maxAgeSeconds`**: with pinned-height reads, "freshness" = pinned height recency; ChainMeta staleness already bounds this — decide whether core must additionally reject requests when ChainMeta is older than `maxAgeSeconds` +- **Solana determinism**: account data can change between slots and `getAccountInfo` can't query an exact past slot; `minContextSlot` gives ≥ semantics, so identical-bytes quorum may need slot-tolerant ballot design (e.g. vote on value only, drop observed slot from ballot key) — flag for design review +- **`expireExternalRead` vs ERROR quorum**: both route to expiry on the contract; keep both (EndBlocker for timeout, ERROR ballot for definitively-failing queries) or simplify to timeout-only +- **Module address**: `UNIVERSAL_EXECUTOR_MODULE` is hardcoded in the contract — verify against `authtypes.NewModuleAddress(uexecutortypes.ModuleName)` EVM mapping before deploy +- **Fee flow**: fully contract-side (protocol fee → VaultPC, refunds); core only triggers callbacks — no bank/fee logic needed in module +- **Nomenclature**: PDFs say `x/UCallback` as a separate module; plan puts everything in `x/uexecutor` (reuses EVM hooks, module nonce, ballot plumbing, existing AuthZ grants) — confirm + +## Suggested implementation order + +1. Proto + ballot type + codegen +2. Event decode + PendingReadRequest storage + evm_hooks detection +3. ABI + `CallFulfillExternalCallback` / `CallExpireExternalRead` +4. `MsgVoteReadResult` handler + ballot wiring + EndBlocker expiry +5. Queries (gRPC) + autocli +6. uClient: pushcore query + pushsigner vote + read processor + query executor (EVM first, then SVM) +7. E2E test: local chain + mock external RPC (extend `scripts/test_universal.sh`) diff --git a/universalClient/core/client.go b/universalClient/core/client.go index 965e7c20..25fb17c6 100644 --- a/universalClient/core/client.go +++ b/universalClient/core/client.go @@ -76,12 +76,14 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie return nil, err } + // chainsManager routes read request events into target chain DBs. pushChain, err := pushwatcher.NewClient( pushDB, cfg.GetChainConfig(cfg.PushChainID), pushCore, cfg.PushChainID, log, + chainsManager, ) if err != nil { return nil, fmt.Errorf("failed to create push chain client: %w", err) diff --git a/universalClient/externalchains/chains.go b/universalClient/externalchains/chains.go index df25f2b8..7b4f4095 100644 --- a/universalClient/externalchains/chains.go +++ b/universalClient/externalchains/chains.go @@ -28,6 +28,7 @@ type Chains struct { // Chain client management chains map[string]common.ChainClient // key: CAIP-2 chain ID chainConfigs map[string]*uregistrytypes.ChainConfig // key: CAIP-2 chain ID + chainDBs map[string]*db.DB // key: CAIP-2 chain ID chainsMu sync.RWMutex pushChainID string // Push chain ID (always present) @@ -57,6 +58,7 @@ func NewChains( logger: logger.With().Str("component", "chains").Logger(), chains: make(map[string]common.ChainClient), chainConfigs: make(map[string]*uregistrytypes.ChainConfig), + chainDBs: make(map[string]*db.DB), pushChainID: cfg.PushChainID, } } @@ -286,6 +288,7 @@ func (c *Chains) addChain(ctx context.Context, cfg *uregistrytypes.ChainConfig) c.chainsMu.Lock() c.chains[cfg.Chain] = client c.chainConfigs[cfg.Chain] = cfg + c.chainDBs[cfg.Chain] = chainDB c.chainsMu.Unlock() c.logger.Info(). @@ -314,6 +317,7 @@ func (c *Chains) removeChain(chainID string) error { delete(c.chains, chainID) delete(c.chainConfigs, chainID) + delete(c.chainDBs, chainID) c.logger.Info(). Str("chain", chainID). @@ -341,6 +345,7 @@ func (c *Chains) StopAll() { // Clear the registry c.chains = make(map[string]common.ChainClient) c.chainConfigs = make(map[string]*uregistrytypes.ChainConfig) + c.chainDBs = make(map[string]*db.DB) } // GetClient returns the chain client for the specified chain ID @@ -356,6 +361,24 @@ func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { return client, nil } +// GetStore implements common.ReadStoreResolver: resolves a CAIP-2 chain ID to +// that chain's event store, so read requests can be routed into the target +// chain's database. +func (c *Chains) GetStore(chainID string) (*common.ChainStore, error) { + if chainID == c.pushChainID { + return nil, fmt.Errorf("read requests cannot target push chain itself") + } + + c.chainsMu.RLock() + defer c.chainsMu.RUnlock() + + chainDB, exists := c.chainDBs[chainID] + if !exists { + return nil, fmt.Errorf("no database for chain %s", chainID) + } + return common.NewChainStore(chainDB), nil +} + // IsEVMChain returns true if the chain uses EVM (e.g. Ethereum, BSC). Used by coordinator for nonce behaviour. func (c *Chains) IsEVMChain(chainID string) bool { c.chainsMu.RLock() diff --git a/universalClient/externalchains/common/event_processor.go b/universalClient/externalchains/common/event_processor.go index 3625d306..bd059727 100644 --- a/universalClient/externalchains/common/event_processor.go +++ b/universalClient/externalchains/common/event_processor.go @@ -12,32 +12,44 @@ import ( "github.com/mr-tron/base58" "github.com/pushchain/push-chain-node/universalClient/db" - "github.com/pushchain/push-chain-node/universalClient/pushsigner" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" "github.com/rs/zerolog" ) +// VoteSigner is the subset of pushsigner.Signer used by EventProcessor. +// Defined here (consumer-side) so tests can provide mock implementations. +type VoteSigner interface { + VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) + VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) + VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) +} + // EventProcessor processes events from the chain's database and votes on them type EventProcessor struct { - signer *pushsigner.Signer + signer VoteSigner chainStore *ChainStore logger zerolog.Logger chainID string inboundEnabled bool outboundEnabled bool - running bool - stopCh chan struct{} - wg sync.WaitGroup + // reader executes READ_REQUEST events against this chain (the push event + // listener routes them into this chain's DB). Nil disables read processing. + reader ChainReader + running bool + stopCh chan struct{} + wg sync.WaitGroup } // NewEventProcessor creates a new event processor func NewEventProcessor( - signer *pushsigner.Signer, + signer VoteSigner, database *db.DB, chainID string, inboundEnabled bool, outboundEnabled bool, + reader ChainReader, logger zerolog.Logger, ) *EventProcessor { return &EventProcessor{ @@ -46,6 +58,7 @@ func NewEventProcessor( chainID: chainID, inboundEnabled: inboundEnabled, outboundEnabled: outboundEnabled, + reader: reader, logger: logger.With().Str("component", "event_processor").Str("chain", chainID).Logger(), stopCh: make(chan struct{}), } @@ -111,7 +124,7 @@ func (ep *EventProcessor) processLoop(ctx context.Context) { } } -// processConfirmedEvents processes confirmed events (both inbound and outbound) +// processConfirmedEvents processes confirmed events (inbound, outbound and read requests) func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { events, err := ep.chainStore.GetConfirmedEvents(1000) if err != nil { @@ -143,6 +156,18 @@ func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { Msg("failed to vote on outbound event") continue } + } else if event.Type == store.EventTypeReadRequest { + if ep.reader == nil { + ep.logger.Warn().Str("event_id", event.EventID).Msg("no reader configured, skipping read request event processing") + continue + } + if err := ep.processReadRequestEvent(ctx, &event); err != nil { + ep.logger.Error(). + Err(err). + Str("event_id", event.EventID). + Msg("failed to vote on read request event") + continue + } } } @@ -176,23 +201,7 @@ func (ep *EventProcessor) processOutboundEvent(ctx context.Context, event *store return fmt.Errorf("failed to vote on outbound: %w", err) } - // Atomically record vote hash and flip status in one DB write - rowsAffected, err := ep.chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) - if err != nil { - return fmt.Errorf("failed to update event status and vote_tx_hash: %w", err) - } - - if rowsAffected == 0 { - return nil // already completed by another validator - } - - ep.logger.Info(). - Str("event_id", event.EventID). - Str("type", event.Type). - Str("vote_tx_hash", voteTxHash). - Msg("event marked as COMPLETED") - - return nil + return ep.markCompleted(event, voteTxHash) } // processInboundEvent processes an inbound event by voting on it and confirming it @@ -217,14 +226,49 @@ func (ep *EventProcessor) processInboundEvent(ctx context.Context, event *store. return err } - // Atomically record vote hash and flip status in one DB write + return ep.markCompleted(event, voteTxHash) +} + +// processReadRequestEvent executes an external read request against this chain +// and votes the observation. Transient failures (RPC errors, vote failure) +// keep the event CONFIRMED for retry; corrupt or expired requests flip to +// REVERTED without voting (core's EndBlocker expires them on-chain). +func (ep *EventProcessor) processReadRequestEvent(ctx context.Context, event *store.Event) error { + var req uread.ReadRequest + if err := json.Unmarshal(event.EventData, &req); err != nil { + ep.markReadReverted(event.EventID) + return fmt.Errorf("corrupt read request event data: %w", err) + } + + if req.ExpiryTimestamp > 0 && time.Now().Unix() >= req.ExpiryTimestamp { + ep.logger.Info().Str("request_id", req.RequestID).Msg("read request expired; skipping (core EndBlocker expires it on-chain)") + ep.markReadReverted(event.EventID) + return nil + } + + result, err := ep.reader.ExecuteRead(ctx, &req) + if err != nil { + return fmt.Errorf("read execution failed: %w", err) + } + + voteTxHash, err := ep.signer.VoteReadResult(ctx, req.RequestID, result) + if err != nil { + // TODO(core): ErrVoteReadNotAvailable falls through here until MsgVoteReadResult lands. + return fmt.Errorf("failed to vote read result: %w", err) + } + + return ep.markCompleted(event, voteTxHash) +} + +// markCompleted atomically records the vote hash and flips CONFIRMED -> COMPLETED. +func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) error { rowsAffected, err := ep.chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) if err != nil { return fmt.Errorf("failed to update event status after successful vote: %w", err) } if rowsAffected == 0 { - return nil // already completed by another validator + return nil // already completed } ep.logger.Info(). @@ -236,6 +280,12 @@ func (ep *EventProcessor) processInboundEvent(ctx context.Context, event *store. return nil } +func (ep *EventProcessor) markReadReverted(eventID string) { + if _, err := ep.chainStore.UpdateEventStatus(eventID, store.StatusConfirmed, store.StatusReverted); err != nil { + ep.logger.Error().Err(err).Str("event_id", eventID).Msg("failed to mark read request reverted") + } +} + // constructInbound creates an Inbound message from event data func (ep *EventProcessor) constructInbound(event *store.Event) (*uexecutortypes.Inbound, error) { var eventData UniversalTx diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index 4a08c305..24fd52d8 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -3,6 +3,7 @@ package common import ( "context" "encoding/json" + "fmt" "testing" "time" @@ -12,15 +13,174 @@ import ( ucdb "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) +type fakeVoteSigner struct { + readVotes map[string]*uread.ReadResult + txHash string + err error +} + +func (f *fakeVoteSigner) VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) { + if f.err != nil { + return "", f.err + } + return "", fmt.Errorf("inbound vote not supported by fake") +} + +func (f *fakeVoteSigner) VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) { + if f.err != nil { + return "", f.err + } + return "", fmt.Errorf("outbound vote not supported by fake") +} + +func (f *fakeVoteSigner) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { + if f.err != nil { + return "", f.err + } + if f.readVotes == nil { + f.readVotes = make(map[string]*uread.ReadResult) + } + f.readVotes[requestID] = result + return f.txHash, nil +} + +type fakeChainReader struct { + result *uread.ReadResult + err error +} + +func (f *fakeChainReader) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + return f.result, f.err +} + +func testReadRequest() *uread.ReadRequest { + return &uread.ReadRequest{ + RequestID: "0xabc123", + ChainNamespace: "eip155", + ChainID: "11155111", + Query: []byte{0x01}, + MinConfirmations: 1, + PinnedBlockHeight: 100, + CreatedAtHeight: 7, + } +} + +func newReadTestProcessor(t *testing.T, signer VoteSigner, reader ChainReader) (*EventProcessor, *ChainStore) { + t.Helper() + database, err := ucdb.OpenInMemoryDB(true) + require.NoError(t, err) + ep := NewEventProcessor(signer, database, "eip155:11155111", false, false, reader, zerolog.Nop()) + return ep, NewChainStore(database) +} + +func seedReadRequest(t *testing.T, cs *ChainStore, req *uread.ReadRequest) string { + t.Helper() + eventData, err := json.Marshal(req) + require.NoError(t, err) + eventID := "read:" + req.RequestID + stored, err := cs.InsertEventIfNotExists(&store.Event{ + EventID: eventID, + BlockHeight: req.CreatedAtHeight, + Type: store.EventTypeReadRequest, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: eventData, + }) + require.NoError(t, err) + require.True(t, stored) + return eventID +} + +func eventStatus(t *testing.T, cs *ChainStore, eventID string) string { + t.Helper() + var event store.Event + require.NoError(t, cs.database.Client().Where("event_id = ?", eventID).First(&event).Error) + return event.Status +} + +func TestProcessReadRequest_SuccessFlow(t *testing.T) { + req := testReadRequest() + result := &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: []byte{0xaa}, + ObservedBlockHeight: 100, + } + signer := &fakeVoteSigner{txHash: "VOTE_TX"} + ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: result}) + eventID := seedReadRequest(t, cs, req) + + require.NoError(t, ep.processConfirmedEvents(context.Background())) + + require.Contains(t, signer.readVotes, req.RequestID) + assert.Equal(t, result, signer.readVotes[req.RequestID]) + assert.Equal(t, store.StatusCompleted, eventStatus(t, cs, eventID)) + + // second tick must not re-vote + signer.readVotes = nil + require.NoError(t, ep.processConfirmedEvents(context.Background())) + assert.Empty(t, signer.readVotes) +} + +func TestProcessReadRequest_VoteFailureKeepsConfirmed(t *testing.T) { + req := testReadRequest() + signer := &fakeVoteSigner{err: fmt.Errorf("MsgVoteReadResult not available")} + ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + eventID := seedReadRequest(t, cs, req) + + require.NoError(t, ep.processConfirmedEvents(context.Background())) + + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + +func TestProcessReadRequest_ExpiredMarkedReverted(t *testing.T) { + req := testReadRequest() + req.ExpiryTimestamp = time.Now().Add(-time.Minute).Unix() + signer := &fakeVoteSigner{txHash: "VOTE_TX"} + ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + eventID := seedReadRequest(t, cs, req) + + require.NoError(t, ep.processConfirmedEvents(context.Background())) + + assert.Empty(t, signer.readVotes) + assert.Equal(t, store.StatusReverted, eventStatus(t, cs, eventID)) +} + +func TestProcessReadRequest_ExecutionFailureRetries(t *testing.T) { + req := testReadRequest() + signer := &fakeVoteSigner{txHash: "VOTE_TX"} + ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{err: fmt.Errorf("rpc down")}) + eventID := seedReadRequest(t, cs, req) + + require.NoError(t, ep.processConfirmedEvents(context.Background())) + + // no vote, still CONFIRMED (transient RPC failure) + assert.Empty(t, signer.readVotes) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + +func TestProcessReadRequest_NoReaderSkips(t *testing.T) { + req := testReadRequest() + signer := &fakeVoteSigner{txHash: "VOTE_TX"} + // nil reader -> read events are skipped, left CONFIRMED + ep, cs := newReadTestProcessor(t, signer, nil) + eventID := seedReadRequest(t, cs, req) + + require.NoError(t, ep.processConfirmedEvents(context.Background())) + + assert.Empty(t, signer.readVotes) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + func TestNewEventProcessor(t *testing.T) { t.Run("creates event processor with valid params", func(t *testing.T) { logger := zerolog.Nop() chainID := "eip155:1" - processor := NewEventProcessor(nil, nil, chainID, true, true, logger) + processor := NewEventProcessor(nil, nil, chainID, true, true, nil, logger) require.NotNil(t, processor) assert.Equal(t, chainID, processor.chainID) @@ -52,7 +212,7 @@ func TestEventProcessorStop(t *testing.T) { func TestEventProcessorBase58ToHex(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "test-chain", true, true, logger) + processor := NewEventProcessor(nil, nil, "test-chain", true, true, nil, logger) t.Run("empty string returns 0x", func(t *testing.T) { result, err := processor.base58ToHex("") @@ -86,7 +246,7 @@ func TestEventProcessorBase58ToHex(t *testing.T) { func TestEventProcessorConstructInbound(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) t.Run("nil event returns error", func(t *testing.T) { inbound, err := processor.constructInbound(nil) @@ -252,7 +412,7 @@ func TestEventProcessorConstructInbound(t *testing.T) { func TestEventProcessorParseOutboundEventData(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) t.Run("nil event returns error", func(t *testing.T) { data, err := processor.parseOutboundEventData(nil) @@ -331,7 +491,7 @@ func TestEventProcessorParseOutboundEventData(t *testing.T) { func TestEventProcessorBuildOutboundObservation(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) t.Run("builds observation with gas fee from parsed data", func(t *testing.T) { outboundData := &OutboundEvent{ @@ -403,7 +563,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("nil event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) event := &store.Event{ EventID: "0xabc:0", @@ -417,7 +577,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("empty event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) event := &store.Event{ EventID: "0xabc:0", @@ -431,7 +591,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("invalid JSON event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) event := &store.Event{ EventID: "0xabc:0", @@ -445,7 +605,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("missing tx_id returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) eventData, _ := json.Marshal(OutboundEvent{ TxID: "", @@ -463,7 +623,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("missing universal_tx_id returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) eventData, _ := json.Marshal(OutboundEvent{ TxID: "0xtxid", @@ -493,7 +653,7 @@ func TestProcessInboundEvent(t *testing.T) { t.Run("nil event data returns construct error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) event := &store.Event{ EventID: "0xabc:0", @@ -507,7 +667,7 @@ func TestProcessInboundEvent(t *testing.T) { t.Run("invalid JSON event data returns construct error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) event := &store.Event{ EventID: "0xabc:0", @@ -537,7 +697,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { t.Run("no confirmed events returns nil", func(t *testing.T) { database := setupDB(t, nil) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -553,7 +713,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -580,7 +740,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) // Should not return error - errors on individual events are logged and skipped err := ep.processConfirmedEvents(ctx) @@ -610,7 +770,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -623,55 +783,23 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { assert.Equal(t, store.StatusConfirmed, evt2.Status) }) - t.Run("mixed inbound and outbound with bad data both fail gracefully", func(t *testing.T) { + t.Run("read request without reader is skipped", func(t *testing.T) { database := setupDB(t, []store.Event{ { - EventID: "0xin:0", + EventID: "0xread:0", Status: store.StatusConfirmed, - Type: store.EventTypeInbound, - EventData: []byte("bad"), - }, - { - EventID: "0xout:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: []byte("bad"), - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - - var inEvt, outEvt store.Event - database.Client().Where("event_id = ?", "0xin:0").First(&inEvt) - assert.Equal(t, store.StatusConfirmed, inEvt.Status) - database.Client().Where("event_id = ?", "0xout:0").First(&outEvt) - assert.Equal(t, store.StatusConfirmed, outEvt.Status) - }) - - t.Run("outbound missing tx_id in valid JSON stays CONFIRMED", func(t *testing.T) { - eventData, _ := json.Marshal(OutboundEvent{ - TxID: "", - UniversalTxID: "0xutxid", - }) - database := setupDB(t, []store.Event{ - { - EventID: "0xno_txid:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: eventData, + Type: store.EventTypeReadRequest, + EventData: []byte("{}"), }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) var evt store.Event - database.Client().Where("event_id = ?", "0xno_txid:0").First(&evt) + database.Client().Where("event_id = ?", "0xread:0").First(&evt) assert.Equal(t, store.StatusConfirmed, evt.Status) }) @@ -685,7 +813,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -703,7 +831,7 @@ func TestProcessLoopContextCancellation(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) t.Run("processLoop exits promptly on context cancel", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -739,7 +867,7 @@ func TestProcessLoopStopChannel(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) t.Run("processLoop exits promptly on stop signal", func(t *testing.T) { ctx := context.Background() @@ -789,6 +917,7 @@ func TestEventProcessorStruct(t *testing.T) { ep := &EventProcessor{} assert.Nil(t, ep.signer) assert.Nil(t, ep.chainStore) + assert.Nil(t, ep.reader) assert.Empty(t, ep.chainID) assert.False(t, ep.running) assert.Nil(t, ep.stopCh) @@ -801,25 +930,25 @@ func TestNewEventProcessorEnabledFlags(t *testing.T) { logger := zerolog.Nop() t.Run("both enabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) assert.True(t, ep.inboundEnabled) assert.True(t, ep.outboundEnabled) }) t.Run("inbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, false, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", true, false, nil, logger) assert.True(t, ep.inboundEnabled) assert.False(t, ep.outboundEnabled) }) t.Run("outbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, true, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", false, true, nil, logger) assert.False(t, ep.inboundEnabled) assert.True(t, ep.outboundEnabled) }) t.Run("both disabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, false, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", false, false, nil, logger) assert.False(t, ep.inboundEnabled) assert.False(t, ep.outboundEnabled) }) @@ -831,7 +960,7 @@ func TestEventProcessorStartDoubleStart(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -858,7 +987,7 @@ func TestEventProcessorStopIdempotent(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -888,7 +1017,7 @@ func TestEventProcessorIsRunningStateTransitions(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) // Initial state: not running assert.False(t, ep.IsRunning()) @@ -923,7 +1052,7 @@ func TestEventProcessorStopViaContextCancel(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) ctx, cancel := context.WithCancel(context.Background()) @@ -988,7 +1117,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { t.Run("inbound disabled skips inbound events, leaves them CONFIRMED", func(t *testing.T) { database := setupDB(t, makeEvents()) // inbound=false, outbound=false (no signer so outbound will also fail to vote, but that's ok) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, false, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1001,7 +1130,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { t.Run("outbound disabled skips outbound events, leaves them CONFIRMED", func(t *testing.T) { database := setupDB(t, makeEvents()) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, false, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1022,7 +1151,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { EventData: outboundEventData, }, }) - ep := NewEventProcessor(nil, database, "eip155:1", true, false, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, false, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1043,7 +1172,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { EventData: inboundEventData, }, }) - ep := NewEventProcessor(nil, database, "eip155:1", false, true, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, true, nil, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) diff --git a/universalClient/externalchains/evm/client.go b/universalClient/externalchains/evm/client.go index a9cf0c67..7df8117b 100644 --- a/universalClient/externalchains/evm/client.go +++ b/universalClient/externalchains/evm/client.go @@ -88,12 +88,15 @@ func NewClient( if pushSigner != nil { inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled + // client is the reader for READ_REQUEST events routed into this chain's + // DB by the push event listener. client.eventProcessor = common.NewEventProcessor( pushSigner, database, chainIDStr, inboundEnabled, outboundEnabled, + client, log, ) } diff --git a/universalClient/externalchains/svm/client.go b/universalClient/externalchains/svm/client.go index bdafd098..8bd0233b 100644 --- a/universalClient/externalchains/svm/client.go +++ b/universalClient/externalchains/svm/client.go @@ -98,12 +98,15 @@ func NewClient( if pushSigner != nil { inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled + // client is the reader for READ_REQUEST events routed into this chain's + // DB by the push event listener. client.eventProcessor = common.NewEventProcessor( pushSigner, database, chainIDStr, inboundEnabled, outboundEnabled, + client, log, ) } diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 351b38b9..beaf5c77 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -24,13 +24,15 @@ type Client struct { cancel context.CancelFunc } -// NewClient creates a new Push chain client +// NewClient creates a new Push chain client. +// readStoreResolver may be nil; the listener then skips read request polling. func NewClient( database *db.DB, chainConfig *config.ChainSpecificConfig, pushCore *pushcore.Client, chainID string, logger zerolog.Logger, + readStoreResolver common.ReadStoreResolver, ) (*Client, error) { // Normalize nil config so downstream uses don't need nil guards. if chainConfig == nil { @@ -43,6 +45,7 @@ func NewClient( database, logger, chainConfig, + readStoreResolver, ) if err != nil { return nil, fmt.Errorf("failed to create event listener: %w", err) diff --git a/universalClient/pushwatcher/client_test.go b/universalClient/pushwatcher/client_test.go index 26877bea..9f3a0b01 100644 --- a/universalClient/pushwatcher/client_test.go +++ b/universalClient/pushwatcher/client_test.go @@ -34,7 +34,7 @@ func TestNewClient(t *testing.T) { pc := newTestPushCoreClient() t.Run("success with nil config", func(t *testing.T) { - client, err := NewClient(database, nil, pc, "push-chain", logger) + client, err := NewClient(database, nil, pc, "push-chain", logger, nil) require.NoError(t, err) require.NotNil(t, client) assert.NotNil(t, client.eventListener) @@ -48,27 +48,27 @@ func TestNewClient(t *testing.T) { CleanupIntervalSeconds: &cleanup, RetentionPeriodSeconds: &retention, } - client, err := NewClient(database, cfg, pc, "push-chain", logger) + client, err := NewClient(database, cfg, pc, "push-chain", logger, nil) require.NoError(t, err) require.NotNil(t, client) assert.NotNil(t, client.eventCleaner) }) t.Run("nil pushcore fails", func(t *testing.T) { - _, err := NewClient(database, nil, nil, "push-chain", logger) + _, err := NewClient(database, nil, nil, "push-chain", logger, nil) require.Error(t, err) assert.Contains(t, err.Error(), "push client is nil") }) t.Run("nil database fails", func(t *testing.T) { - _, err := NewClient(nil, nil, pc, "push-chain", logger) + _, err := NewClient(nil, nil, pc, "push-chain", logger, nil) require.Error(t, err) assert.Contains(t, err.Error(), "database is nil") }) } func TestClient_StartStop(t *testing.T) { - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop()) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) require.NoError(t, err) ctx := context.Background() @@ -96,7 +96,7 @@ func TestClient_StopBeforeStart(t *testing.T) { // Stop on a freshly created client (never started) should not panic. // The cancel func is nil, eventListener.Stop() returns ErrNotRunning but // the client logs and swallows that error, returning nil. - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop()) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) require.NoError(t, err) // Should not panic or return error @@ -104,7 +104,7 @@ func TestClient_StopBeforeStart(t *testing.T) { } func TestClient_DoubleStop(t *testing.T) { - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop()) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) require.NoError(t, err) ctx := context.Background() @@ -122,7 +122,7 @@ func TestClient_StartStopWithEventCleaner(t *testing.T) { CleanupIntervalSeconds: &cleanup, RetentionPeriodSeconds: &retention, } - client, err := NewClient(newTestDB(t), cfg, newTestPushCoreClient(), "push-chain", zerolog.Nop()) + client, err := NewClient(newTestDB(t), cfg, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) require.NoError(t, err) require.NotNil(t, client.eventCleaner) @@ -140,7 +140,7 @@ func TestClient_StartStopWithEventCleaner(t *testing.T) { func TestClient_StartStopLifecycleMultiple(t *testing.T) { // Verify the client can be started and stopped multiple times (restart). - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop()) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) require.NoError(t, err) ctx := context.Background() @@ -183,7 +183,7 @@ func TestNewClient_CleanerAlwaysWired(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - client, err := NewClient(database, tc.cfg, pc, "push-chain", logger) + client, err := NewClient(database, tc.cfg, pc, "push-chain", logger, nil) require.NoError(t, err) require.NotNil(t, client.eventCleaner, "cleaner must always be wired up") }) @@ -199,7 +199,7 @@ func TestNewClient_NegativePollInterval(t *testing.T) { cfg := &config.ChainSpecificConfig{ EventPollingIntervalSeconds: &poll, } - client, err := NewClient(database, cfg, pc, "push-chain", logger) + client, err := NewClient(database, cfg, pc, "push-chain", logger, nil) require.NoError(t, err) // Negative poll interval should fall back to default assert.Equal(t, DefaultPollInterval, client.eventListener.cfg.PollInterval) @@ -215,7 +215,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil) + el, err := NewEventListener(pc, database, logger, nil, nil) require.NoError(t, err) event := &store.Event{ @@ -236,7 +236,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil) + el, err := NewEventListener(pc, database, logger, nil, nil) require.NoError(t, err) event := &store.Event{ @@ -260,7 +260,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil) + el, err := NewEventListener(pc, database, logger, nil, nil) require.NoError(t, err) for i := 0; i < 5; i++ { @@ -282,7 +282,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil) + el, err := NewEventListener(pc, database, logger, nil, nil) require.NoError(t, err) event := &store.Event{ @@ -310,7 +310,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil) + el, err := NewEventListener(pc, database, logger, nil, nil) require.NoError(t, err) event := &store.Event{ diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index 7193f444..ec8f35be 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -28,13 +28,17 @@ type Config struct { PollInterval time.Duration } -// EventListener polls Push chain for active TSS events and pending outbounds -// via gRPC, converts them to store.Events, and inserts them into the local DB. +// EventListener polls Push chain for active TSS events, pending outbounds and +// pending read requests via gRPC, converts them to store.Events, and inserts +// them into the local DB. Read request events are routed into the target +// chain's DB (via readStoreResolver) so that chain's own event processor +// executes and votes them. type EventListener struct { - pushCore *pushcore.Client - chainStore *common.ChainStore - cfg Config - logger zerolog.Logger + pushCore *pushcore.Client + chainStore *common.ChainStore + readStoreResolver common.ReadStoreResolver + cfg Config + logger zerolog.Logger mu sync.Mutex running bool @@ -43,11 +47,13 @@ type EventListener struct { } // NewEventListener creates a new Push event listener. +// readStoreResolver may be nil; read request polling is skipped without it. func NewEventListener( pushCore *pushcore.Client, database *db.DB, logger zerolog.Logger, chainConfig *config.ChainSpecificConfig, + readStoreResolver common.ReadStoreResolver, ) (*EventListener, error) { if pushCore == nil { return nil, ErrNilClient @@ -62,10 +68,11 @@ func NewEventListener( } return &EventListener{ - pushCore: pushCore, - chainStore: common.NewChainStore(database), - cfg: Config{PollInterval: pollInterval}, - logger: logger.With().Str("component", "push_event_listener").Logger(), + pushCore: pushCore, + chainStore: common.NewChainStore(database), + readStoreResolver: readStoreResolver, + cfg: Config{PollInterval: pollInterval}, + logger: logger.With().Str("component", "push_event_listener").Logger(), }, nil } @@ -134,17 +141,19 @@ func (el *EventListener) run(ctx context.Context) { } } -// poll fetches pending TSS, outbound & fund migration events, stores them, and updates latest block height. +// poll fetches pending TSS, outbound, fund migration & read request events, stores them, and updates latest block height. func (el *EventListener) poll(ctx context.Context) { tssCount := el.pollTssEvents(ctx) outboundCount := el.pollOutboundEvents(ctx) migrationCount := el.pollFundMigrationEvents(ctx) + readCount := el.pollReadRequestEvents(ctx) - if total := tssCount + outboundCount + migrationCount; total > 0 { + if total := tssCount + outboundCount + migrationCount + readCount; total > 0 { el.logger.Info(). Int("tss_events", tssCount). Int("outbound_events", outboundCount). Int("migration_events", migrationCount). + Int("read_request_events", readCount). Msg("stored new events") } @@ -235,6 +244,63 @@ func (el *EventListener) pollFundMigrationEvents(ctx context.Context) int { return newCount } +// pollReadRequestEvents fetches pending external read requests and routes each +// into its target chain's DB, where that chain's event processor executes and +// votes it. Requests for chains this validator doesn't serve are skipped and +// retried next poll (core keeps returning them until fulfilled or expired). +// Returns new event count. +func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { + if el.readStoreResolver == nil { + return 0 + } + + requests, err := el.pushCore.GetAllPendingReadRequests(ctx) + if err != nil { + if errors.Is(err, pushcore.ErrReadQueriesNotAvailable) { + // TODO(core): remove once Query/PendingReadRequests lands. + return 0 + } + el.logger.Error().Err(err).Msg("failed to fetch pending read requests") + return 0 + } + + var newCount int + for _, req := range requests { + caip2, err := common.CAIP2(req.ChainNamespace, req.ChainID) + if err != nil { + el.logger.Warn().Err(err).Str("request_id", req.RequestID).Msg("invalid read request domain") + continue + } + + targetStore, err := el.readStoreResolver.GetStore(caip2) + if err != nil { + el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("target_chain", caip2).Msg("target chain not served; skipping read request") + continue + } + + event, err := convertReadRequestEvent(req) + if err != nil { + el.logger.Warn().Err(err).Str("request_id", req.RequestID).Msg("failed to convert read request") + continue + } + + stored, err := targetStore.InsertEventIfNotExists(event) + if err != nil { + el.logger.Error().Err(err).Str("event_id", event.EventID).Str("target_chain", caip2).Msg("failed to store read request") + continue + } + if stored { + el.logger.Debug(). + Str("event_id", event.EventID). + Str("target_chain", caip2). + Msg("routed read request to target chain") + newCount++ + } + } + + return newCount +} + // storeEvent inserts an event into the DB if it doesn't already exist. // Returns 1 if stored, 0 if duplicate or error. func (el *EventListener) storeEvent(event *store.Event) int { diff --git a/universalClient/pushwatcher/event_listener_test.go b/universalClient/pushwatcher/event_listener_test.go index 983b3fb3..4805df99 100644 --- a/universalClient/pushwatcher/event_listener_test.go +++ b/universalClient/pushwatcher/event_listener_test.go @@ -17,7 +17,7 @@ func TestNewEventListener(t *testing.T) { client := newTestPushCoreClient() t.Run("success with defaults", func(t *testing.T) { - el, err := NewEventListener(client, db, logger, nil) + el, err := NewEventListener(client, db, logger, nil, nil) require.NoError(t, err) require.NotNil(t, el) assert.Equal(t, DefaultPollInterval, el.cfg.PollInterval) @@ -25,19 +25,19 @@ func TestNewEventListener(t *testing.T) { }) t.Run("nil client", func(t *testing.T) { - _, err := NewEventListener(nil, db, logger, nil) + _, err := NewEventListener(nil, db, logger, nil, nil) assert.ErrorIs(t, err, ErrNilClient) }) t.Run("nil database", func(t *testing.T) { - _, err := NewEventListener(client, nil, logger, nil) + _, err := NewEventListener(client, nil, logger, nil, nil) assert.ErrorIs(t, err, ErrNilDatabase) }) t.Run("custom poll interval from config", func(t *testing.T) { poll := 10 cfg := config.ChainSpecificConfig{EventPollingIntervalSeconds: &poll} - el, err := NewEventListener(client, db, logger, &cfg) + el, err := NewEventListener(client, db, logger, &cfg, nil) require.NoError(t, err) assert.Equal(t, 10*time.Second, el.cfg.PollInterval) }) @@ -45,14 +45,14 @@ func TestNewEventListener(t *testing.T) { t.Run("zero poll interval uses default", func(t *testing.T) { poll := 0 cfg := config.ChainSpecificConfig{EventPollingIntervalSeconds: &poll} - el, err := NewEventListener(client, db, logger, &cfg) + el, err := NewEventListener(client, db, logger, &cfg, nil) require.NoError(t, err) assert.Equal(t, DefaultPollInterval, el.cfg.PollInterval) }) } func TestEventListener_StartStop(t *testing.T) { - el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil) + el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil, nil) require.NoError(t, err) ctx := context.Background() @@ -73,7 +73,7 @@ func TestEventListener_StartStop(t *testing.T) { } func TestEventListener_RestartAfterStop(t *testing.T) { - el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil) + el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil, nil) require.NoError(t, err) ctx := context.Background() diff --git a/universalClient/pushwatcher/event_parser.go b/universalClient/pushwatcher/event_parser.go index a2644d7d..c6f87960 100644 --- a/universalClient/pushwatcher/event_parser.go +++ b/universalClient/pushwatcher/event_parser.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" ) @@ -94,6 +95,27 @@ func convertFundMigrationEvent(migration *utsstypes.FundMigration) (*store.Event }, nil } +// convertReadRequestEvent converts a pending external read request to a store.Event. +func convertReadRequestEvent(req *uread.ReadRequest) (*store.Event, error) { + if req == nil || req.RequestID == "" { + return nil, fmt.Errorf("read request is nil or missing request id") + } + + eventData, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("failed to marshal read request event data: %w", err) + } + + return &store.Event{ + EventID: hashEventID(store.EventTypeReadRequest, req.RequestID), + BlockHeight: req.CreatedAtHeight, + Type: store.EventTypeReadRequest, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: eventData, + }, nil +} + // convertOutboundToEvent converts a PendingOutboundEntry + OutboundTx to a store.Event. func convertOutboundToEvent(entry *uexecutortypes.PendingOutboundEntry, outbound *uexecutortypes.OutboundTx) (*store.Event, error) { if entry == nil || outbound == nil { diff --git a/universalClient/pushwatcher/event_parser_test.go b/universalClient/pushwatcher/event_parser_test.go index cf8a98dc..acd38a7a 100644 --- a/universalClient/pushwatcher/event_parser_test.go +++ b/universalClient/pushwatcher/event_parser_test.go @@ -421,3 +421,4 @@ func TestHashEventID(t *testing.T) { assert.Len(t, id, 64) // sha256 = 32 bytes = 64 hex chars }) } + From 04ca9d582e908d94afe2a9adb25d42029bd40dbf Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 19:34:02 +0530 Subject: [PATCH 07/54] remove docs --- docs/read-from-chains-implementation-plan.md | 164 ------------------- 1 file changed, 164 deletions(-) delete mode 100644 docs/read-from-chains-implementation-plan.md diff --git a/docs/read-from-chains-implementation-plan.md b/docs/read-from-chains-implementation-plan.md deleted file mode 100644 index 14586282..00000000 --- a/docs/read-from-chains-implementation-plan.md +++ /dev/null @@ -1,164 +0,0 @@ -# Read from Chains — Core + universalClient Implementation Plan - -## References -- Spec v1: `read_v1.pdf` (UniversalCallback + MetaCallbackSpec model — superseded) -- Spec v2: `read_v2.pdf` (UniversalReadClient model — **adopted**) -- Contracts: [pushchain/push-chain-core-contracts@51e5aeb](https://github.com/pushchain/push-chain-core-contracts/commit/51e5aeb2dd0cc0ecd23134f3b313a455ca52fdde) (`read-state-v1`) - -## What the contracts already define (fixed surface we integrate against) - -- `UniversalCallback.sol` (singleton, upgradeable): - - `requestExternalReadSelf(ReadSpec spec, bytes4 callbackSelector, uint64 callbackGasLimit) payable → requestId` - - validates: non-empty account/query, `minConfirmations >= 1`, `maxAgeSeconds/maxDelaySeconds != 0`, `supportedDomains[ns][id]`, `callbackGasLimit <= 1_000_000`, `fee <= msg.value <= spec.maxFee` - - `requestId = keccak256(block.chainid, block.number, address(this), keccak256(spec), nonce++)` - - emits **`ReadRequested(uint256 indexed requestId, ReadSpec spec, address indexed callbackTarget, address indexed originalFunder, uint256 feesDeposited)`** - - `fulfillExternalCallback(uint256 requestId, bytes resultData, uint64 observedBlockHeight, bytes32 observedBlockHash)` — **`onlyUEModule`** - - calls `callbackTarget.call{gas}(selector, requestId, resultData)`; handles fee split (protocol fee → VaultPC, refund → funder) internally - - emits `ReadFulfilled` / `CallbackFailed` - - `expireExternalRead(uint256 requestId)` — **`onlyUEModule`**, emits `RequestExpired` - - `UNIVERSAL_EXECUTOR_MODULE = 0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7` (hardcoded) -- `ReadTypes.sol`: - - `ReadSpec { UniversalAccountId account; bytes query; uint16 minConfirmations; uint64 maxAgeSeconds; uint64 maxDelaySeconds; uint256 maxFee; }` - - `BALLOT_OBSERVATION_TYPE_READ_REQUEST = 0x3dad9a0d…` — contracts expect a matching ballot type in core -- `UniversalReadClient.sol` — app-side base (`_requestRead` / `onUniversalData` / `_onReadResult`, `_localContext` storage); no core/uClient work needed -- `UniversalCore.sol` — new `readBaseFeeByChainNamespace[ns][id]` + `updateReadBaseFeeByChain` (admin) -- Query envelopes (spec v1, still applies): `EvmQueryEnvelope` (AccountBalance / ERC20Balance / ContractCall / StorageSlot + `EvmBlockRef`), `SolanaQueryEnvelope` (LamportBalance / SPLTokenAccount / RawAccountData + `minSlot`), `Web2QueryEnvelope` (GET/POST) - -## End-to-end flow (target) - -1. App inherits `UniversalReadClient`, calls `_requestRead` mid-execution → `ReadRequested` emitted on Push EVM -2. `x/uexecutor` `PostTxProcessing` hook decodes the event in-block → stores `PendingReadRequest` -3. universalClient (each validator) polls pending reads via gRPC → executes the query envelope against the external chain RPC → canonical-encodes result -4. Validator submits `MsgVoteReadResult` → uvalidator ballot; identical `(resultData, height, hash)` → same ballot key → >2/3 quorum -5. On finalization, uexecutor calls `fulfillExternalCallback` on `UniversalCallback` via module EVM call -6. Expiry: request not finalized within `maxDelaySeconds` → EndBlocker calls `expireExternalRead` - ---- - -## Core (`push-chain-node`) changes - -### 1. Proto (`proto/…` + buf regen) - -- `proto/uvalidator/v1/ballot.proto` - - add `BALLOT_OBSERVATION_TYPE_READ_REQUEST` to `BallotObservationType` -- `proto/uexecutor/v1/` (new `read_request.proto` + `tx.proto` + `query.proto`) - - `ReadRequest` type: `request_id (bytes/hex)`, decoded `ReadSpec` fields (`chain_namespace`, `chain_id`, `owner`, `query`, `min_confirmations`, `max_age_seconds`, `max_delay_seconds`), `callback_target`, `pinned_block_height`, `created_at_height`, `expiry_timestamp`, `status (PENDING | FULFILLED | EXPIRED)` - - `MsgVoteReadResult { signer, request_id, result_data, observed_block_height, observed_block_hash, status (SUCCESS | ERROR) }` - - `Query/PendingReadRequests` (paginated) — mirror `GetAllPendingOutbounds` - -### 2. Event detection (`x/uexecutor`) - -- `types/events.go`: add `ReadRequestedEventSig` (topic0 of the event above) -- `types/gateway_pc_event_decode.go`: add `DecodeReadRequestedFromLog` (ABI-decode `ReadSpec` tuple from log data) -- `keeper/evm_hooks.go` `PostTxProcessing`: match logs from the `UniversalCallback` address → decode → `CreatePendingReadRequest` - - follows the existing outbound-detection precedent (`BuildOutboundsFromReceipt`); no Push-EVM log polling needed in uClient -- `x/uregistry`: register `UNIVERSAL_CALLBACK` in `SYSTEM_CONTRACTS` (address source for hook filtering + callback calls) - -### 3. Pending-read storage (`x/uexecutor/keeper`) - -- new `Keeper.PendingReadRequests` collection + `keeper/pending_read_request.go` (CRUD, mirror `pending_outbound.go`) -- **Pin the query height at creation**: `pinned_block_height = ChainMeta[chain].LastAppliedChainHeight − spec.minConfirmations` (clamped) - - all validators query the same height → identical bytes; satisfies the spec rule "block taken must be below gas-oracle minimum" - - reject/park request if no ChainMeta exists for the chain -- set `expiry_timestamp = block.time + maxDelaySeconds` - -### 4. Voting (`x/uexecutor`) - -- `types/msg_vote_read_result.go` — ValidateBasic (mirror `msg_vote_inbound.go`) -- ballot key: `GetReadBallotKey = hash(request_id ‖ status ‖ result_data ‖ observed_block_height ‖ observed_block_hash)` — identical-bytes quorum -- `keeper/voting.go`: `VoteOnReadBallot` → `uvalidatorKeeper.VoteOnBallot` with the new ballot type, threshold `(2*validators)/3 + 1` (same as inbound) -- `keeper/msg_vote_read_result.go`: - - reject if request unknown / not PENDING / past expiry - - on finalizing vote (SUCCESS): `CallFulfillExternalCallback`, mark FULFILLED - - on finalizing vote (ERROR quorum — e.g. query invalid, target chain reorged): mark EXPIRED + `CallExpireExternalRead` (refund path) -- `keeper/ballot_hooks.go`: handle terminal FAILED/EXPIRED ballots for the new type (cleanup) - -### 5. EVM callback (`x/uexecutor`) - -- `types/abi.go`: add `UniversalCallbackABI` const + `ParseUniversalCallbackABI` (only `fulfillExternalCallback`, `expireExternalRead`) -- `keeper/evm.go`: `CallFulfillExternalCallback(...)` + `CallExpireExternalRead(...)` via `DerivedEVMCall` (template: `CallExecuteUniversalTx` / `CallUniversalCoreSetChainMeta`; uses `ModuleAccountNonce`) -- gas: callback gas is bounded on the contract side (`callbackGasLimit ≤ 1M`); give the module call a fixed generous limit -- **verify** module account EVM address == `0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7` (contract hardcodes it); mismatch = every fulfill reverts - -### 6. Expiry sweep - -- `x/uexecutor` EndBlocker (`abci.go`): iterate PENDING reads with `expiry_timestamp < block.time` → `CallExpireExternalRead` → mark EXPIRED - - deterministic on-chain, no vote needed - - bound per-block work (process N per block) to avoid unbounded EndBlocker gas - -### 7. Queries / CLI - -- `keeper/grpc_query.go` (or new file): `PendingReadRequests`, `ReadRequest(id)` -- autocli entries for inspection - ---- - -## universalClient changes - -> **Status: implemented** (UV side done; items marked `TODO(core)` are stubbed and unblock mechanically once core lands — grep `TODO(core)` in `universalClient/`). -> -> - `universalClient/uread/` — **temporary package**: only proto-mirror types (`ReadRequest`/`ReadStatus`/`ReadResult`); delete it once core proto lands by swapping every `uread.*` reference to `uexecutortypes.*` -> - `externalchains/common/read.go` — shared permanent bits: `ChainReader`/`ChainResolver` interfaces, `CAIP2`, canonical result encoders (`EncodeUint256Result`/`EncodeBytes32Result`) -> - `externalchains/evm/read_envelope.go` + `read_executor.go` + RPC additions (`GetBalanceAt`, `GetStorageAt`, `GetHeaderByNumber`) — all 4 query types at pinned height (**TODO(core): remove latest−minConfirmations fallback once core pins height**) -> - `externalchains/svm/read_envelope.go` + `read_executor.go` + RPC additions (`GetBalanceWithSlot`, `GetAccountInfoWithSlot`) — all 3 query types, minContextSlot semantics -> - `universalClient/pushwatcher/` (moved out of the chains manager — push is core-managed, not a registry chain) — `event_listener.go` + `event_parser.go` fetch pending reads via gRPC and **route each `READ_REQUEST` event into the target chain's DB** (`common.ReadStoreResolver`, implemented by `chains.Chains.GetStore`); requests for unserved chains are skipped and retried next poll (core re-serves pending requests; expiry is the backstop) -> - `externalchains/common/event_processor.go` — original type-switch shape kept; gained a `READ_REQUEST` branch (`processReadRequestEvent`: execute on the chain's own `ChainReader` → vote → COMPLETED; corrupt/expired → REVERTED; transient → retry), a `reader ChainReader` constructor param (evm/svm pass the client itself), and the consumer-side `VoteSigner` interface; push client has no processor at all -> - `pushcore/pushCore.go` `GetAllPendingReadRequests` (**TODO(core): wire to Query/PendingReadRequests**; returns sentinel until then, processor idles silently) -> - `pushsigner/pushsigner.go` `VoteReadResult` (**TODO(core): build MsgVoteReadResult in vote.go + add to AuthZ grant set**) -> - wiring: push client is owned by `core/client.go` (not the chains manager) — core opens the push DB once (shared with TSS), creates `push.NewClient(..., chainsManager)` and manages its lifecycle; `externalchains.Chains` only manages registry-driven external chains and implements `GetStore` for read routing - -### 1. pushcore (`universalClient/pushcore/pushCore.go`) - -- `GetAllPendingReadRequests()` — new gRPC query wrapper (mirror `GetAllPendingOutbounds`) - -### 2. pushsigner (`universalClient/pushsigner/`) - -- `VoteReadResult(ctx, msg)` — build `MsgVoteReadResult`, AuthZ-wrap, sign, broadcast (mirror `VoteInbound`) -- add msg type to AuthZ grant set (hot-key authorization for the new msg URL) - -### 3. Read worker (`universalClient/chains/push/` — new component) - -- new `read_request_processor.go` on the Push client (alongside `event_listener.go`): - - poll `GetAllPendingReadRequests` on the existing polling interval - - local store dedup (per-chain SQLite, reuse `common.ChainStore`) so a request is executed/voted once; retry until vote tx confirmed - - skip requests already past `expiry_timestamp` -- **query executor** (new `universalClient/readexecutor/` or under `chains/common/`): - - resolve target chain client: `chainNamespace + ":" + chainId` → CAIP-2 → existing `Chains` registry RPC client - - decode envelope by namespace: - - `eip155` → `EvmQueryEnvelope`: AccountBalance → `eth_getBalance`, ERC20Balance → `balanceOf` via `eth_call`, ContractCall → `eth_call`, StorageSlot → `eth_getStorageAt` — all at `pinned_block_height`; fetch `observedBlockHash` for that height - - `solana` → `SolanaQueryEnvelope`: LamportBalance / SPLTokenAccount / RawAccountData via `getAccountInfo`/`getBalance` with `minContextSlot = pinned height`; observed slot + blockhash from response context - - `web2` → **out of scope for v1** (non-deterministic responses; needs canonicalization design) — vote ERROR if received - - canonical result encoding (must be byte-identical across validators): - - AccountBalance/LamportBalance → `abi.encode(uint256)` - - ERC20Balance/SPLTokenAccount → `abi.encode(uint256)` - - ContractCall → raw returndata - - StorageSlot → `abi.encode(bytes32)`; RawAccountData → raw account bytes - - on RPC/decode failure after retries → `VoteReadResult(status = ERROR)` -- interaction with per-chain clients: reads target chains uClient already watches (registry-driven); if a supported domain has no chain client, vote ERROR - -### 4. Config (`universalClient/config/`) - -- optional: `read_polling_interval_seconds` per Push chain entry (else reuse `event_polling_interval_seconds`) -- no new per-external-chain config — reuse existing `rpc_urls` - ---- - -## Cross-cutting decisions / open questions - -- **Height pinning source**: plan uses ChainMeta (gas oracle) height at request time; confirm ChainMeta exists for all chains that will be `supportedDomains` on the contract (contract-side whitelist and core-side ChainMeta must stay in sync — no core check enforces this) -- **`maxAgeSeconds`**: with pinned-height reads, "freshness" = pinned height recency; ChainMeta staleness already bounds this — decide whether core must additionally reject requests when ChainMeta is older than `maxAgeSeconds` -- **Solana determinism**: account data can change between slots and `getAccountInfo` can't query an exact past slot; `minContextSlot` gives ≥ semantics, so identical-bytes quorum may need slot-tolerant ballot design (e.g. vote on value only, drop observed slot from ballot key) — flag for design review -- **`expireExternalRead` vs ERROR quorum**: both route to expiry on the contract; keep both (EndBlocker for timeout, ERROR ballot for definitively-failing queries) or simplify to timeout-only -- **Module address**: `UNIVERSAL_EXECUTOR_MODULE` is hardcoded in the contract — verify against `authtypes.NewModuleAddress(uexecutortypes.ModuleName)` EVM mapping before deploy -- **Fee flow**: fully contract-side (protocol fee → VaultPC, refunds); core only triggers callbacks — no bank/fee logic needed in module -- **Nomenclature**: PDFs say `x/UCallback` as a separate module; plan puts everything in `x/uexecutor` (reuses EVM hooks, module nonce, ballot plumbing, existing AuthZ grants) — confirm - -## Suggested implementation order - -1. Proto + ballot type + codegen -2. Event decode + PendingReadRequest storage + evm_hooks detection -3. ABI + `CallFulfillExternalCallback` / `CallExpireExternalRead` -4. `MsgVoteReadResult` handler + ballot wiring + EndBlocker expiry -5. Queries (gRPC) + autocli -6. uClient: pushcore query + pushsigner vote + read processor + query executor (EVM first, then SVM) -7. E2E test: local chain + mock external RPC (extend `scripts/test_universal.sh`) From 9f674dcba64963e26afa240663c2997e58caf277 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 19:48:42 +0530 Subject: [PATCH 08/54] change proto to hve targetChain --- universalClient/externalchains/chains.go | 2 +- .../common/{read.go => chain_reader.go} | 16 ------- .../{read_test.go => chain_reader_test.go} | 9 ---- .../externalchains/common/chain_store.go | 8 ++++ .../common/event_processor_test.go | 3 +- universalClient/pushwatcher/client.go | 6 +-- universalClient/pushwatcher/event_listener.go | 42 ++++++++----------- universalClient/uread/types.go | 3 +- 8 files changed, 32 insertions(+), 57 deletions(-) rename universalClient/externalchains/common/{read.go => chain_reader.go} (58%) rename universalClient/externalchains/common/{read_test.go => chain_reader_test.go} (80%) diff --git a/universalClient/externalchains/chains.go b/universalClient/externalchains/chains.go index 7b4f4095..6ed0f60c 100644 --- a/universalClient/externalchains/chains.go +++ b/universalClient/externalchains/chains.go @@ -361,7 +361,7 @@ func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { return client, nil } -// GetStore implements common.ReadStoreResolver: resolves a CAIP-2 chain ID to +// GetStore implements common.ExternalChainStoreResolver: resolves a CAIP-2 chain ID to // that chain's event store, so read requests can be routed into the target // chain's database. func (c *Chains) GetStore(chainID string) (*common.ChainStore, error) { diff --git a/universalClient/externalchains/common/read.go b/universalClient/externalchains/common/chain_reader.go similarity index 58% rename from universalClient/externalchains/common/read.go rename to universalClient/externalchains/common/chain_reader.go index 58203b10..17affadb 100644 --- a/universalClient/externalchains/common/read.go +++ b/universalClient/externalchains/common/chain_reader.go @@ -14,22 +14,6 @@ type ChainReader interface { ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) } -// ReadStoreResolver resolves a CAIP-2 chain ID to that chain's event store, so -// READ_REQUEST events can be routed into the target chain's own database. -// Implemented by externalchains.Chains. -type ReadStoreResolver interface { - GetStore(chainID string) (*ChainStore, error) -} - -// CAIP2 joins a ReadSpec domain (chainNamespace, chainId) into the CAIP-2 key -// used by the chains registry, e.g. ("eip155", "1") -> "eip155:1". -func CAIP2(chainNamespace, chainID string) (string, error) { - if chainNamespace == "" || chainID == "" { - return "", fmt.Errorf("empty chain namespace or id") - } - return chainNamespace + ":" + chainID, nil -} - // EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256). func EncodeUint256Result(v *big.Int) ([]byte, error) { if v == nil { diff --git a/universalClient/externalchains/common/read_test.go b/universalClient/externalchains/common/chain_reader_test.go similarity index 80% rename from universalClient/externalchains/common/read_test.go rename to universalClient/externalchains/common/chain_reader_test.go index 63087580..9195428b 100644 --- a/universalClient/externalchains/common/read_test.go +++ b/universalClient/externalchains/common/chain_reader_test.go @@ -31,12 +31,3 @@ func TestEncodeBytes32Result(t *testing.T) { require.Len(t, out, 32) assert.Equal(t, v[:], out) } - -func TestCAIP2(t *testing.T) { - got, err := CAIP2("eip155", "1") - require.NoError(t, err) - assert.Equal(t, "eip155:1", got) - - _, err = CAIP2("", "1") - assert.Error(t, err) -} diff --git a/universalClient/externalchains/common/chain_store.go b/universalClient/externalchains/common/chain_store.go index b67bd1f8..cad71d9f 100644 --- a/universalClient/externalchains/common/chain_store.go +++ b/universalClient/externalchains/common/chain_store.go @@ -22,6 +22,14 @@ func NewChainStore(database *db.DB) *ChainStore { } } +// ExternalChainStoreResolver resolves a CAIP-2 chain ID to that chain's event +// store, so events destined for an external chain (READ_REQUEST today, e.g. +// SIGN events in the future) can be routed into that chain's own database. +// Implemented by externalchains.Chains. +type ExternalChainStoreResolver interface { + GetStore(chainID string) (*ChainStore, error) +} + // GetChainHeight returns the last processed block height for the chain. // Creates a new entry with height 0 if one doesn't exist (atomic via FirstOrCreate). func (cs *ChainStore) GetChainHeight() (uint64, error) { diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index 24fd52d8..d8d5f602 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -60,8 +60,7 @@ func (f *fakeChainReader) ExecuteRead(ctx context.Context, req *uread.ReadReques func testReadRequest() *uread.ReadRequest { return &uread.ReadRequest{ RequestID: "0xabc123", - ChainNamespace: "eip155", - ChainID: "11155111", + TargetChain: "eip155:11155111", Query: []byte{0x01}, MinConfirmations: 1, PinnedBlockHeight: 100, diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index beaf5c77..19122245 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -25,14 +25,14 @@ type Client struct { } // NewClient creates a new Push chain client. -// readStoreResolver may be nil; the listener then skips read request polling. +// storeResolver may be nil; the listener then skips read request polling. func NewClient( database *db.DB, chainConfig *config.ChainSpecificConfig, pushCore *pushcore.Client, chainID string, logger zerolog.Logger, - readStoreResolver common.ReadStoreResolver, + storeResolver common.ExternalChainStoreResolver, ) (*Client, error) { // Normalize nil config so downstream uses don't need nil guards. if chainConfig == nil { @@ -45,7 +45,7 @@ func NewClient( database, logger, chainConfig, - readStoreResolver, + storeResolver, ) if err != nil { return nil, fmt.Errorf("failed to create event listener: %w", err) diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index ec8f35be..e0ae3b20 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -31,14 +31,14 @@ type Config struct { // EventListener polls Push chain for active TSS events, pending outbounds and // pending read requests via gRPC, converts them to store.Events, and inserts // them into the local DB. Read request events are routed into the target -// chain's DB (via readStoreResolver) so that chain's own event processor +// chain's DB (via storeResolver) so that chain's own event processor // executes and votes them. type EventListener struct { - pushCore *pushcore.Client - chainStore *common.ChainStore - readStoreResolver common.ReadStoreResolver - cfg Config - logger zerolog.Logger + pushCore *pushcore.Client + chainStore *common.ChainStore + storeResolver common.ExternalChainStoreResolver + cfg Config + logger zerolog.Logger mu sync.Mutex running bool @@ -47,13 +47,13 @@ type EventListener struct { } // NewEventListener creates a new Push event listener. -// readStoreResolver may be nil; read request polling is skipped without it. +// storeResolver may be nil; read request polling is skipped without it. func NewEventListener( pushCore *pushcore.Client, database *db.DB, logger zerolog.Logger, chainConfig *config.ChainSpecificConfig, - readStoreResolver common.ReadStoreResolver, + storeResolver common.ExternalChainStoreResolver, ) (*EventListener, error) { if pushCore == nil { return nil, ErrNilClient @@ -68,11 +68,11 @@ func NewEventListener( } return &EventListener{ - pushCore: pushCore, - chainStore: common.NewChainStore(database), - readStoreResolver: readStoreResolver, - cfg: Config{PollInterval: pollInterval}, - logger: logger.With().Str("component", "push_event_listener").Logger(), + pushCore: pushCore, + chainStore: common.NewChainStore(database), + storeResolver: storeResolver, + cfg: Config{PollInterval: pollInterval}, + logger: logger.With().Str("component", "push_event_listener").Logger(), }, nil } @@ -250,7 +250,7 @@ func (el *EventListener) pollFundMigrationEvents(ctx context.Context) int { // retried next poll (core keeps returning them until fulfilled or expired). // Returns new event count. func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { - if el.readStoreResolver == nil { + if el.storeResolver == nil { return 0 } @@ -266,15 +266,9 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { var newCount int for _, req := range requests { - caip2, err := common.CAIP2(req.ChainNamespace, req.ChainID) + targetStore, err := el.storeResolver.GetStore(req.TargetChain) if err != nil { - el.logger.Warn().Err(err).Str("request_id", req.RequestID).Msg("invalid read request domain") - continue - } - - targetStore, err := el.readStoreResolver.GetStore(caip2) - if err != nil { - el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("target_chain", caip2).Msg("target chain not served; skipping read request") + el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("target_chain", req.TargetChain).Msg("target chain not served; skipping read request") continue } @@ -286,13 +280,13 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { stored, err := targetStore.InsertEventIfNotExists(event) if err != nil { - el.logger.Error().Err(err).Str("event_id", event.EventID).Str("target_chain", caip2).Msg("failed to store read request") + el.logger.Error().Err(err).Str("event_id", event.EventID).Str("target_chain", req.TargetChain).Msg("failed to store read request") continue } if stored { el.logger.Debug(). Str("event_id", event.EventID). - Str("target_chain", caip2). + Str("target_chain", req.TargetChain). Msg("routed read request to target chain") newCount++ } diff --git a/universalClient/uread/types.go b/universalClient/uread/types.go index a7b2ca34..e9bcaabc 100644 --- a/universalClient/uread/types.go +++ b/universalClient/uread/types.go @@ -8,8 +8,7 @@ package uread // ReadRequest mirrors the pending read request tracked by x/uexecutor. type ReadRequest struct { RequestID string // uint256 as 0x-prefixed hex (from ReadRequested event) - ChainNamespace string // e.g. "eip155", "solana" - ChainID string // e.g. "1", "42161", "mainnet-beta" + TargetChain string // CAIP-2, e.g. "eip155:1", "solana:mainnet-beta" Owner []byte // ReadSpec.account.owner (20-byte addr / 32-byte pubkey) Query []byte // chain-specific envelope, abi.encode(...) MinConfirmations uint16 From 01830bad22f1148a45cb91770ad76167413bc351 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 20:01:33 +0530 Subject: [PATCH 09/54] refactor(uclient): route external events via ChainClient.AddEvent Chain clients own writes to their DB: pushwatcher resolves the target client via GetClient (same as tss consumers) and hands it the event, instead of writing into the chain's store directly. Drops the ExternalChainStoreResolver interface and the chainDBs bookkeeping in the chains manager. --- universalClient/externalchains/chains.go | 22 ----------------- universalClient/externalchains/chains_test.go | 8 ++++--- .../externalchains/common/chain_store.go | 7 ------ .../externalchains/common/types.go | 6 +++++ universalClient/externalchains/evm/client.go | 6 +++++ universalClient/externalchains/svm/client.go | 6 +++++ universalClient/pushwatcher/client.go | 6 ++--- universalClient/pushwatcher/event_listener.go | 24 ++++++++++++------- .../tss/coordinator/coordinator_test.go | 7 +++--- .../tss/txbroadcaster/broadcaster_test.go | 9 +++---- .../tss/txresolver/resolver_test.go | 9 +++---- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/universalClient/externalchains/chains.go b/universalClient/externalchains/chains.go index 6ed0f60c..12c1a907 100644 --- a/universalClient/externalchains/chains.go +++ b/universalClient/externalchains/chains.go @@ -28,7 +28,6 @@ type Chains struct { // Chain client management chains map[string]common.ChainClient // key: CAIP-2 chain ID chainConfigs map[string]*uregistrytypes.ChainConfig // key: CAIP-2 chain ID - chainDBs map[string]*db.DB // key: CAIP-2 chain ID chainsMu sync.RWMutex pushChainID string // Push chain ID (always present) @@ -58,7 +57,6 @@ func NewChains( logger: logger.With().Str("component", "chains").Logger(), chains: make(map[string]common.ChainClient), chainConfigs: make(map[string]*uregistrytypes.ChainConfig), - chainDBs: make(map[string]*db.DB), pushChainID: cfg.PushChainID, } } @@ -288,7 +286,6 @@ func (c *Chains) addChain(ctx context.Context, cfg *uregistrytypes.ChainConfig) c.chainsMu.Lock() c.chains[cfg.Chain] = client c.chainConfigs[cfg.Chain] = cfg - c.chainDBs[cfg.Chain] = chainDB c.chainsMu.Unlock() c.logger.Info(). @@ -317,7 +314,6 @@ func (c *Chains) removeChain(chainID string) error { delete(c.chains, chainID) delete(c.chainConfigs, chainID) - delete(c.chainDBs, chainID) c.logger.Info(). Str("chain", chainID). @@ -345,7 +341,6 @@ func (c *Chains) StopAll() { // Clear the registry c.chains = make(map[string]common.ChainClient) c.chainConfigs = make(map[string]*uregistrytypes.ChainConfig) - c.chainDBs = make(map[string]*db.DB) } // GetClient returns the chain client for the specified chain ID @@ -361,23 +356,6 @@ func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { return client, nil } -// GetStore implements common.ExternalChainStoreResolver: resolves a CAIP-2 chain ID to -// that chain's event store, so read requests can be routed into the target -// chain's database. -func (c *Chains) GetStore(chainID string) (*common.ChainStore, error) { - if chainID == c.pushChainID { - return nil, fmt.Errorf("read requests cannot target push chain itself") - } - - c.chainsMu.RLock() - defer c.chainsMu.RUnlock() - - chainDB, exists := c.chainDBs[chainID] - if !exists { - return nil, fmt.Errorf("no database for chain %s", chainID) - } - return common.NewChainStore(chainDB), nil -} // IsEVMChain returns true if the chain uses EVM (e.g. Ethereum, BSC). Used by coordinator for nonce behaviour. func (c *Chains) IsEVMChain(chainID string) bool { diff --git a/universalClient/externalchains/chains_test.go b/universalClient/externalchains/chains_test.go index 20ed9d84..4989fa22 100644 --- a/universalClient/externalchains/chains_test.go +++ b/universalClient/externalchains/chains_test.go @@ -12,6 +12,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -410,9 +411,10 @@ type mockChainClient struct { stopErr error } -func (m *mockChainClient) Start(ctx context.Context) error { m.startCalled = true; return nil } -func (m *mockChainClient) Stop() error { m.stopCalled = true; return m.stopErr } -func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) Start(ctx context.Context) error { m.startCalled = true; return nil } +func (m *mockChainClient) Stop() error { m.stopCalled = true; return m.stopErr } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return nil, nil } diff --git a/universalClient/externalchains/common/chain_store.go b/universalClient/externalchains/common/chain_store.go index cad71d9f..a7005741 100644 --- a/universalClient/externalchains/common/chain_store.go +++ b/universalClient/externalchains/common/chain_store.go @@ -22,13 +22,6 @@ func NewChainStore(database *db.DB) *ChainStore { } } -// ExternalChainStoreResolver resolves a CAIP-2 chain ID to that chain's event -// store, so events destined for an external chain (READ_REQUEST today, e.g. -// SIGN events in the future) can be routed into that chain's own database. -// Implemented by externalchains.Chains. -type ExternalChainStoreResolver interface { - GetStore(chainID string) (*ChainStore, error) -} // GetChainHeight returns the last processed block height for the chain. // Creates a new entry with height 0 if one doesn't exist (atomic via FirstOrCreate). diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index f3b2317c..bcd466e3 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/pushchain/push-chain-node/universalClient/store" uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -21,6 +22,11 @@ type ChainClient interface { // GetTxBuilder returns the TxBuilder for this chain // Returns an error if txBuilder is not supported for this chain (e.g., Push chain) GetTxBuilder() (TxBuilder, error) + + // AddEvent stores an externally-produced event (e.g. a READ_REQUEST routed + // by the push watcher) in this chain's database for its event processor. + // Returns false if the event already exists. + AddEvent(event *store.Event) (bool, error) } // FundMigrationData contains the data needed to build a fund migration transaction. diff --git a/universalClient/externalchains/evm/client.go b/universalClient/externalchains/evm/client.go index 7df8117b..b36cd282 100644 --- a/universalClient/externalchains/evm/client.go +++ b/universalClient/externalchains/evm/client.go @@ -14,6 +14,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" + "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -202,6 +203,11 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } +// AddEvent stores an externally-produced event in this chain's database. +func (c *Client) AddEvent(event *store.Event) (bool, error) { + return common.NewChainStore(c.database).InsertEventIfNotExists(event) +} + // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { // Create event listener if gateway is configured diff --git a/universalClient/externalchains/svm/client.go b/universalClient/externalchains/svm/client.go index 8bd0233b..a73140a2 100644 --- a/universalClient/externalchains/svm/client.go +++ b/universalClient/externalchains/svm/client.go @@ -12,6 +12,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" + "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -212,6 +213,11 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } +// AddEvent stores an externally-produced event in this chain's database. +func (c *Client) AddEvent(event *store.Event) (bool, error) { + return common.NewChainStore(c.database).InsertEventIfNotExists(event) +} + // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { // Create event listener if gateway is configured diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 19122245..85e2d74a 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -25,14 +25,14 @@ type Client struct { } // NewClient creates a new Push chain client. -// storeResolver may be nil; the listener then skips read request polling. +// chainResolver may be nil; the listener then skips read request polling. func NewClient( database *db.DB, chainConfig *config.ChainSpecificConfig, pushCore *pushcore.Client, chainID string, logger zerolog.Logger, - storeResolver common.ExternalChainStoreResolver, + chainResolver ExternalChainResolver, ) (*Client, error) { // Normalize nil config so downstream uses don't need nil guards. if chainConfig == nil { @@ -45,7 +45,7 @@ func NewClient( database, logger, chainConfig, - storeResolver, + chainResolver, ) if err != nil { return nil, fmt.Errorf("failed to create event listener: %w", err) diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index e0ae3b20..83683999 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -28,15 +28,21 @@ type Config struct { PollInterval time.Duration } +// ExternalChainResolver resolves a CAIP-2 chain ID to its chain client. +// Satisfied by externalchains.Chains. +type ExternalChainResolver interface { + GetClient(chainID string) (common.ChainClient, error) +} + // EventListener polls Push chain for active TSS events, pending outbounds and // pending read requests via gRPC, converts them to store.Events, and inserts // them into the local DB. Read request events are routed into the target -// chain's DB (via storeResolver) so that chain's own event processor -// executes and votes them. +// chain's DB (via chainResolver) so that chain's own event processor executes +// and votes them. type EventListener struct { pushCore *pushcore.Client chainStore *common.ChainStore - storeResolver common.ExternalChainStoreResolver + chainResolver ExternalChainResolver cfg Config logger zerolog.Logger @@ -47,13 +53,13 @@ type EventListener struct { } // NewEventListener creates a new Push event listener. -// storeResolver may be nil; read request polling is skipped without it. +// chainResolver may be nil; read request polling is skipped without it. func NewEventListener( pushCore *pushcore.Client, database *db.DB, logger zerolog.Logger, chainConfig *config.ChainSpecificConfig, - storeResolver common.ExternalChainStoreResolver, + chainResolver ExternalChainResolver, ) (*EventListener, error) { if pushCore == nil { return nil, ErrNilClient @@ -70,7 +76,7 @@ func NewEventListener( return &EventListener{ pushCore: pushCore, chainStore: common.NewChainStore(database), - storeResolver: storeResolver, + chainResolver: chainResolver, cfg: Config{PollInterval: pollInterval}, logger: logger.With().Str("component", "push_event_listener").Logger(), }, nil @@ -250,7 +256,7 @@ func (el *EventListener) pollFundMigrationEvents(ctx context.Context) int { // retried next poll (core keeps returning them until fulfilled or expired). // Returns new event count. func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { - if el.storeResolver == nil { + if el.chainResolver == nil { return 0 } @@ -266,7 +272,7 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { var newCount int for _, req := range requests { - targetStore, err := el.storeResolver.GetStore(req.TargetChain) + targetClient, err := el.chainResolver.GetClient(req.TargetChain) if err != nil { el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("target_chain", req.TargetChain).Msg("target chain not served; skipping read request") continue @@ -278,7 +284,7 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { continue } - stored, err := targetStore.InsertEventIfNotExists(event) + stored, err := targetClient.AddEvent(event) if err != nil { el.logger.Error().Err(err).Str("event_id", event.EventID).Str("target_chain", req.TargetChain).Msg("failed to store read request") continue diff --git a/universalClient/tss/coordinator/coordinator_test.go b/universalClient/tss/coordinator/coordinator_test.go index 69cf0f26..de65f326 100644 --- a/universalClient/tss/coordinator/coordinator_test.go +++ b/universalClient/tss/coordinator/coordinator_test.go @@ -81,9 +81,10 @@ type coordMockChainClient struct { builderErr error } -func (m *coordMockChainClient) Start(context.Context) error { return nil } -func (m *coordMockChainClient) Stop() error { return nil } -func (m *coordMockChainClient) IsHealthy() bool { return true } +func (m *coordMockChainClient) Start(context.Context) error { return nil } +func (m *coordMockChainClient) Stop() error { return nil } +func (m *coordMockChainClient) IsHealthy() bool { return true } +func (m *coordMockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } func (m *coordMockChainClient) GetTxBuilder() (common.TxBuilder, error) { if m.builderErr != nil { return nil, m.builderErr diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index 653d13d0..bc693f72 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -79,10 +79,11 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } -func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } +func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { t.Helper() diff --git a/universalClient/tss/txresolver/resolver_test.go b/universalClient/tss/txresolver/resolver_test.go index b7df913f..beb79aa1 100644 --- a/universalClient/tss/txresolver/resolver_test.go +++ b/universalClient/tss/txresolver/resolver_test.go @@ -76,10 +76,11 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } -func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } +func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { t.Helper() From e996af6a62601754cb82a82aec51d9d4d8039916 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 20:09:11 +0530 Subject: [PATCH 10/54] refactor(uclient): fold chain_reader.go into event_processor and types ChainReader moves next to its consumer (EventProcessor), the uint256 canonical encoder moves to types.go, and the near-no-op bytes32 encoder is inlined at its single call site. --- .../externalchains/common/chain_reader.go | 35 ------------------- .../common/chain_reader_test.go | 33 ----------------- .../externalchains/common/event_processor.go | 6 ++++ .../common/event_processor_test.go | 15 ++++++++ .../externalchains/common/types.go | 17 +++++++++ .../externalchains/evm/read_executor.go | 2 +- 6 files changed, 39 insertions(+), 69 deletions(-) delete mode 100644 universalClient/externalchains/common/chain_reader.go delete mode 100644 universalClient/externalchains/common/chain_reader_test.go diff --git a/universalClient/externalchains/common/chain_reader.go b/universalClient/externalchains/common/chain_reader.go deleted file mode 100644 index 17affadb..00000000 --- a/universalClient/externalchains/common/chain_reader.go +++ /dev/null @@ -1,35 +0,0 @@ -package common - -import ( - "context" - "fmt" - "math/big" - - "github.com/pushchain/push-chain-node/universalClient/uread" -) - -// ChainReader executes an external read request against one chain. -// Implemented by chains/evm.Client and chains/svm.Client. -type ChainReader interface { - ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) -} - -// EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256). -func EncodeUint256Result(v *big.Int) ([]byte, error) { - if v == nil { - v = big.NewInt(0) - } - if v.Sign() < 0 || v.BitLen() > 256 { - return nil, fmt.Errorf("value out of uint256 range") - } - out := make([]byte, 32) - v.FillBytes(out) - return out, nil -} - -// EncodeBytes32Result canonically encodes a storage slot value as abi.encode(bytes32). -func EncodeBytes32Result(v [32]byte) ([]byte, error) { - out := make([]byte, 32) - copy(out, v[:]) - return out, nil -} diff --git a/universalClient/externalchains/common/chain_reader_test.go b/universalClient/externalchains/common/chain_reader_test.go deleted file mode 100644 index 9195428b..00000000 --- a/universalClient/externalchains/common/chain_reader_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package common - -import ( - "bytes" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestEncodeUint256Result(t *testing.T) { - out, err := EncodeUint256Result(big.NewInt(1_000_000)) - require.NoError(t, err) - require.Len(t, out, 32) - assert.Equal(t, big.NewInt(1_000_000), new(big.Int).SetBytes(out)) - - out, err = EncodeUint256Result(nil) - require.NoError(t, err) - assert.True(t, bytes.Equal(out, make([]byte, 32))) - - _, err = EncodeUint256Result(big.NewInt(-1)) - assert.Error(t, err) -} - -func TestEncodeBytes32Result(t *testing.T) { - var v [32]byte - v[31] = 0xff - out, err := EncodeBytes32Result(v) - require.NoError(t, err) - require.Len(t, out, 32) - assert.Equal(t, v[:], out) -} diff --git a/universalClient/externalchains/common/event_processor.go b/universalClient/externalchains/common/event_processor.go index bd059727..2c082303 100644 --- a/universalClient/externalchains/common/event_processor.go +++ b/universalClient/externalchains/common/event_processor.go @@ -26,6 +26,12 @@ type VoteSigner interface { VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) } +// ChainReader executes an external read request against one chain. +// Implemented by the evm and svm chain clients. +type ChainReader interface { + ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) +} + // EventProcessor processes events from the chain's database and votes on them type EventProcessor struct { signer VoteSigner diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index d8d5f602..fd6c802c 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "math/big" "testing" "time" @@ -1182,3 +1183,17 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { assert.Equal(t, store.StatusConfirmed, inboundEvt.Status) }) } + +func TestEncodeUint256Result(t *testing.T) { + out, err := EncodeUint256Result(big.NewInt(1_000_000)) + require.NoError(t, err) + require.Len(t, out, 32) + assert.Equal(t, big.NewInt(1_000_000), new(big.Int).SetBytes(out)) + + out, err = EncodeUint256Result(nil) + require.NoError(t, err) + assert.Equal(t, make([]byte, 32), out) + + _, err = EncodeUint256Result(big.NewInt(-1)) + assert.Error(t, err) +} diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index bcd466e3..a5bf6026 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -2,12 +2,29 @@ package common import ( "context" + "fmt" "math/big" "github.com/pushchain/push-chain-node/universalClient/store" uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) +// EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256) +// so read results are byte-identical across validators and decodable by the +// requesting contract. The bounds check guards against a malicious RPC value +// that would not fit (FillBytes panics on overflow). +func EncodeUint256Result(v *big.Int) ([]byte, error) { + if v == nil { + v = big.NewInt(0) + } + if v.Sign() < 0 || v.BitLen() > 256 { + return nil, fmt.Errorf("value out of uint256 range") + } + out := make([]byte, 32) + v.FillBytes(out) + return out, nil +} + // ChainClient defines the interface for chain-specific implementations type ChainClient interface { // Start initializes and starts the chain client diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go index 3f1a950b..a2057760 100644 --- a/universalClient/externalchains/evm/read_executor.go +++ b/universalClient/externalchains/evm/read_executor.go @@ -85,7 +85,7 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea } var slotValue [32]byte copy(slotValue[32-min(len(value), 32):], value) - resultData, err = common.EncodeBytes32Result(slotValue) + resultData = slotValue[:] default: return uread.NewErrorResult(fmt.Errorf("unknown EvmQueryType %d", env.QueryType)), nil From 56a32092b6fe6e98e470fc1346569ce6e186070b Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 28 Jul 2026 20:21:13 +0530 Subject: [PATCH 11/54] test(uclient): cover evm/svm read executors via json-rpc fakes All query types plus failure modes: eth_call revert, invalid envelopes, transient RPC failures, slot/height constraints, SPL account validation. --- .../externalchains/evm/read_executor_test.go | 255 ++++++++++++++++++ .../externalchains/svm/read_executor_test.go | 210 +++++++++++++++ 2 files changed, 465 insertions(+) create mode 100644 universalClient/externalchains/evm/read_executor_test.go create mode 100644 universalClient/externalchains/svm/read_executor_test.go diff --git a/universalClient/externalchains/evm/read_executor_test.go b/universalClient/externalchains/evm/read_executor_test.go new file mode 100644 index 00000000..661c083e --- /dev/null +++ b/universalClient/externalchains/evm/read_executor_test.go @@ -0,0 +1,255 @@ +package evm + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "net/http" + "net/http/httptest" + "testing" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +// fakeHeader is a minimal valid block header JSON accepted by types.Header. +func fakeHeader(number uint64) map[string]any { + zeroHash := "0x0000000000000000000000000000000000000000000000000000000000000000" + return map[string]any{ + "parentHash": zeroHash, + "sha3Uncles": zeroHash, + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": zeroHash, + "transactionsRoot": zeroHash, + "receiptsRoot": zeroHash, + "logsBloom": "0x" + fmt.Sprintf("%0512x", 0), + "difficulty": "0x0", + "number": fmt.Sprintf("0x%x", number), + "gasLimit": "0x0", + "gasUsed": "0x0", + "timestamp": "0x0", + "extraData": "0x", + "mixHash": zeroHash, + "nonce": "0x0000000000000000", + } +} + +type rpcFault struct { + code int + message string +} + +// newReadTestClient spins up a JSON-RPC server answering from results/faults +// keyed by method name, and returns a Client wired to it. +func newReadTestClient(t *testing.T, results map[string]any, faults map[string]rpcFault) *Client { + t.Helper() + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req struct { + ID json.RawMessage `json:"id"` + Method string `json:"method"` + } + require.NoError(t, json.NewDecoder(r.Body).Decode(&req)) + + resp := map[string]any{"jsonrpc": "2.0", "id": json.RawMessage(req.ID)} + if fault, ok := faults[req.Method]; ok { + resp["error"] = map[string]any{"code": fault.code, "message": fault.message} + } else if result, ok := results[req.Method]; ok { + resp["result"] = result + } else { + t.Errorf("unexpected RPC method %s", req.Method) + resp["error"] = map[string]any{"code": -32601, "message": "method not found"} + } + require.NoError(t, json.NewEncoder(w).Encode(resp)) + })) + t.Cleanup(srv.Close) + + ethClient, err := ethclient.Dial(srv.URL) + require.NoError(t, err) + t.Cleanup(ethClient.Close) + + return &Client{ + logger: zerolog.Nop(), + rpcClient: &RPCClient{clients: []*ethclient.Client{ethClient}, logger: zerolog.Nop()}, + } +} + +func evmReadRequest(t *testing.T, queryType uint8, blockNumber uint64, payload []byte) *uread.ReadRequest { + t.Helper() + return &uread.ReadRequest{ + RequestID: "0xreq1", + TargetChain: "eip155:11155111", + Query: packEvmEnvelope(t, queryType, 0, blockNumber, payload), + MinConfirmations: 1, + PinnedBlockHeight: 100, + } +} + +func TestExecuteRead_AccountBalance(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + "eth_getBalance": "0xf4240", // 1_000_000 + }, nil) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload)) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, big.NewInt(1_000_000), new(big.Int).SetBytes(result.ResultData)) + assert.Equal(t, uint64(100), result.ObservedBlockHeight) + assert.Len(t, result.ObservedBlockHash, 32) +} + +func TestExecuteRead_ERC20Balance(t *testing.T) { + token := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") + owner := ethcommon.HexToAddress("0x3333333333333333333333333333333333333333") + payload, err := addressPairArgs.Pack(token, owner) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + "eth_call": "0x" + fmt.Sprintf("%064x", 42), + }, nil) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryERC20Balance), 0, payload)) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, big.NewInt(42), new(big.Int).SetBytes(result.ResultData)) +} + +func TestExecuteRead_ContractCall(t *testing.T) { + target := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") + payload, err := addressBytesArgs.Pack(target, []byte{0xde, 0xad}) + require.NoError(t, err) + + t.Run("returns raw returndata", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + "eth_call": "0xcafebabe", + }, nil) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryContractCall), 0, payload)) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, []byte{0xca, 0xfe, 0xba, 0xbe}, result.ResultData) + }) + + t.Run("revert is a votable ERROR observation", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + }, map[string]rpcFault{ + "eth_call": {code: 3, message: "execution reverted"}, + }) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryContractCall), 0, payload)) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Empty(t, result.ResultData) + }) +} + +func TestExecuteRead_StorageSlot(t *testing.T) { + target := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") + payload, err := addressBytes32Args.Pack(target, [32]byte{0x01}) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + "eth_getStorageAt": "0x" + fmt.Sprintf("%064x", 7), + }, nil) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryStorageSlot), 0, payload)) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + require.Len(t, result.ResultData, 32) + assert.Equal(t, big.NewInt(7), new(big.Int).SetBytes(result.ResultData)) +} + +func TestExecuteRead_InvalidEnvelope(t *testing.T) { + client := newReadTestClient(t, nil, nil) + + result, err := client.ExecuteRead(context.Background(), &uread.ReadRequest{ + RequestID: "0xreq1", + Query: []byte{0x01, 0x02}, + PinnedBlockHeight: 100, + }) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) +} + +func TestExecuteRead_RPCFailureIsTransient(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + client := newReadTestClient(t, nil, map[string]rpcFault{ + "eth_getBlockByNumber": {code: -32000, message: "node is syncing"}, + }) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload)) + require.Error(t, err) + assert.Nil(t, result) +} + +func TestExecuteRead_EnvelopeBlockNumberFallback(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(55), + "eth_getBalance": "0x1", + }, nil) + + req := evmReadRequest(t, uint8(evmQueryAccountBalance), 55, payload) + req.PinnedBlockHeight = 0 // TODO(core): fallback removed once core always pins + + result, err := client.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uint64(55), result.ObservedBlockHeight) +} + +func TestExecuteRead_LatestHeightFallback(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + // TODO(core): delete along with the latest-minConfirmations fallback. + t.Run("uses latest minus min confirmations", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "eth_blockNumber": "0x64", // 100 + "eth_getBlockByNumber": fakeHeader(99), + "eth_getBalance": "0x1", + }, nil) + + req := evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload) + req.PinnedBlockHeight = 0 + + result, err := client.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uint64(99), result.ObservedBlockHeight) + }) + + t.Run("chain height below min confirmations is transient", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "eth_blockNumber": "0x1", + }, nil) + + req := evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload) + req.PinnedBlockHeight = 0 + req.MinConfirmations = 5 + + result, err := client.ExecuteRead(context.Background(), req) + require.Error(t, err) + assert.Nil(t, result) + }) +} diff --git a/universalClient/externalchains/svm/read_executor_test.go b/universalClient/externalchains/svm/read_executor_test.go new file mode 100644 index 00000000..b462b190 --- /dev/null +++ b/universalClient/externalchains/svm/read_executor_test.go @@ -0,0 +1,210 @@ +package svm + +import ( + "context" + "encoding/base64" + "encoding/binary" + "encoding/json" + "math/big" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gagliardetto/solana-go" + solrpc "github.com/gagliardetto/solana-go/rpc" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +// accountInfoResult builds a getAccountInfo result with base64 data. +func accountInfoResult(slot uint64, owner solana.PublicKey, data []byte) map[string]any { + return map[string]any{ + "context": map[string]any{"slot": slot}, + "value": map[string]any{ + "data": []any{base64.StdEncoding.EncodeToString(data), "base64"}, + "executable": false, + "lamports": 1, + "owner": owner.String(), + "rentEpoch": 0, + }, + } +} + +// newReadTestClient spins up a JSON-RPC server answering from results keyed by +// method name, and returns a Client wired to it. +func newReadTestClient(t *testing.T, results map[string]any) *Client { + t.Helper() + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var req struct { + ID json.RawMessage `json:"id"` + Method string `json:"method"` + } + require.NoError(t, json.NewDecoder(r.Body).Decode(&req)) + + resp := map[string]any{"jsonrpc": "2.0", "id": json.RawMessage(req.ID)} + if result, ok := results[req.Method]; ok { + resp["result"] = result + } else { + t.Errorf("unexpected RPC method %s", req.Method) + resp["error"] = map[string]any{"code": -32601, "message": "method not found"} + } + require.NoError(t, json.NewEncoder(w).Encode(resp)) + })) + t.Cleanup(srv.Close) + + return &Client{ + logger: zerolog.Nop(), + rpcClient: &RPCClient{clients: []*solrpc.Client{solrpc.New(srv.URL)}, logger: zerolog.Nop()}, + } +} + +func svmReadRequest(t *testing.T, queryType uint8, minSlot uint64, owner []byte) *uread.ReadRequest { + t.Helper() + query, err := svmEnvelopeArgs.Pack(rawSvmEnvelope{ + QueryType: queryType, + SlotRef: struct { + MinSlot uint64 + }{minSlot}, + }) + require.NoError(t, err) + return &uread.ReadRequest{ + RequestID: "0xreq1", + TargetChain: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", + Owner: owner, + Query: query, + } +} + +func testAccount() solana.PublicKey { + return solana.MustPublicKeyFromBase58("So11111111111111111111111111111111111111112") +} + +func TestExecuteRead_LamportBalance(t *testing.T) { + account := testAccount() + + t.Run("success", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getBalance": map[string]any{ + "context": map[string]any{"slot": 900}, + "value": 5_000_000, + }, + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryLamportBalance), 800, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, big.NewInt(5_000_000), new(big.Int).SetBytes(result.ResultData)) + assert.Equal(t, uint64(900), result.ObservedBlockHeight) + }) + + t.Run("observed slot below min slot is transient", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getBalance": map[string]any{ + "context": map[string]any{"slot": 700}, + "value": 5_000_000, + }, + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryLamportBalance), 800, account.Bytes())) + require.Error(t, err) + assert.Nil(t, result) + }) +} + +func TestExecuteRead_SPLTokenAccount(t *testing.T) { + account := testAccount() + + tokenAccountData := func(amount uint64) []byte { + data := make([]byte, 165) + binary.LittleEndian.PutUint64(data[splTokenAmountOffset:], amount) + return data + } + + t.Run("success", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getAccountInfo": accountInfoResult(900, solana.TokenProgramID, tokenAccountData(777)), + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 800, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, big.NewInt(777), new(big.Int).SetBytes(result.ResultData)) + assert.Equal(t, uint64(900), result.ObservedBlockHeight) + }) + + t.Run("non token-program owner is a votable ERROR", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getAccountInfo": accountInfoResult(900, solana.SystemProgramID, tokenAccountData(777)), + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("truncated account data is a votable ERROR", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getAccountInfo": accountInfoResult(900, solana.TokenProgramID, make([]byte, 10)), + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("missing account is a votable ERROR", func(t *testing.T) { + client := newReadTestClient(t, map[string]any{ + "getAccountInfo": map[string]any{ + "context": map[string]any{"slot": 900}, + "value": nil, + }, + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) +} + +func TestExecuteRead_RawAccountData(t *testing.T) { + account := testAccount() + raw := []byte{0x01, 0x02, 0x03} + + client := newReadTestClient(t, map[string]any{ + "getAccountInfo": accountInfoResult(900, solana.SystemProgramID, raw), + }) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryRawAccountData), 0, account.Bytes())) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, raw, result.ResultData) + assert.Equal(t, uint64(900), result.ObservedBlockHeight) +} + +func TestExecuteRead_InvalidInputs(t *testing.T) { + account := testAccount() + + t.Run("invalid envelope is a votable ERROR", func(t *testing.T) { + client := newReadTestClient(t, nil) + + result, err := client.ExecuteRead(context.Background(), &uread.ReadRequest{ + RequestID: "0xreq1", + Owner: account.Bytes(), + Query: []byte{0x01}, + }) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("owner not 32 bytes is a votable ERROR", func(t *testing.T) { + client := newReadTestClient(t, nil) + + result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryLamportBalance), 0, []byte{0x01, 0x02})) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) +} From d4691e76ef63097ae2bf60e62c226bd42987324b Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 14:29:01 +0530 Subject: [PATCH 12/54] fix: temp types --- universalClient/uread/types.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/universalClient/uread/types.go b/universalClient/uread/types.go index e9bcaabc..bc706cb3 100644 --- a/universalClient/uread/types.go +++ b/universalClient/uread/types.go @@ -7,16 +7,14 @@ package uread // ReadRequest mirrors the pending read request tracked by x/uexecutor. type ReadRequest struct { - RequestID string // uint256 as 0x-prefixed hex (from ReadRequested event) - TargetChain string // CAIP-2, e.g. "eip155:1", "solana:mainnet-beta" - Owner []byte // ReadSpec.account.owner (20-byte addr / 32-byte pubkey) - Query []byte // chain-specific envelope, abi.encode(...) - MinConfirmations uint16 - MaxAgeSeconds uint64 - MaxDelaySeconds uint64 - PinnedBlockHeight uint64 // height all validators must query; 0 = not pinned by core - ExpiryTimestamp int64 // unix seconds; 0 = no expiry known - CreatedAtHeight uint64 // Push chain height at which the request was created + RequestID string // uint256 as 0x-prefixed hex (from ReadRequested event) + DestinationChain string // CAIP-2, e.g. "eip155:1", "solana:mainnet-beta"; web2 uses "web2:https" + Owner []byte // ReadSpec.account.owner (20-byte addr / 32-byte pubkey) + Query []byte // chain-specific envelope, abi.encode(...) + MinConfirmations uint16 + DestinationBlockHeight uint64 // destination chain height the read is made at; not applicable for web2 + ExpiryBlockHeight uint64 // Push chain height at which the request expires + CreatedAtHeight uint64 // Push chain height at which the request was created } // ReadStatus is the observed outcome a validator votes on. From 8c7d1717386b307bf9e5d8943629e3fde5250d6c Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 14:29:21 +0530 Subject: [PATCH 13/54] fix: pushWatcher acc to types --- universalClient/pushwatcher/event_listener.go | 8 ++++---- universalClient/pushwatcher/event_parser.go | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index 83683999..7b46d3fa 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -272,9 +272,9 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { var newCount int for _, req := range requests { - targetClient, err := el.chainResolver.GetClient(req.TargetChain) + targetClient, err := el.chainResolver.GetClient(req.DestinationChain) if err != nil { - el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("target_chain", req.TargetChain).Msg("target chain not served; skipping read request") + el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("destination_chain", req.DestinationChain).Msg("target chain not served; skipping read request") continue } @@ -286,13 +286,13 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { stored, err := targetClient.AddEvent(event) if err != nil { - el.logger.Error().Err(err).Str("event_id", event.EventID).Str("target_chain", req.TargetChain).Msg("failed to store read request") + el.logger.Error().Err(err).Str("event_id", event.EventID).Str("destination_chain", req.DestinationChain).Msg("failed to store read request") continue } if stored { el.logger.Debug(). Str("event_id", event.EventID). - Str("target_chain", req.TargetChain). + Str("destination_chain", req.DestinationChain). Msg("routed read request to target chain") newCount++ } diff --git a/universalClient/pushwatcher/event_parser.go b/universalClient/pushwatcher/event_parser.go index c6f87960..1abb4a4f 100644 --- a/universalClient/pushwatcher/event_parser.go +++ b/universalClient/pushwatcher/event_parser.go @@ -107,12 +107,13 @@ func convertReadRequestEvent(req *uread.ReadRequest) (*store.Event, error) { } return &store.Event{ - EventID: hashEventID(store.EventTypeReadRequest, req.RequestID), - BlockHeight: req.CreatedAtHeight, - Type: store.EventTypeReadRequest, - ConfirmationType: store.ConfirmationInstant, - Status: store.StatusConfirmed, - EventData: eventData, + EventID: hashEventID(store.EventTypeReadRequest, req.RequestID), + BlockHeight: req.CreatedAtHeight, + ExpiryBlockHeight: req.ExpiryBlockHeight, + Type: store.EventTypeReadRequest, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: eventData, }, nil } From c503e8a8192ae1e142789467c2693cf11f164b93 Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 16:23:56 +0530 Subject: [PATCH 14/54] fix: add read event to pushchain db --- universalClient/core/client.go | 3 +- universalClient/externalchains/chains.go | 15 +++- universalClient/externalchains/chains_test.go | 8 +- .../externalchains/common/event_processor.go | 44 ++++++----- .../common/event_processor_test.go | 54 ++++++++----- .../externalchains/common/types.go | 6 -- universalClient/externalchains/evm/client.go | 9 +-- .../externalchains/evm/read_executor.go | 40 +++++----- .../externalchains/evm/read_executor_test.go | 75 ++++++++++++------- universalClient/externalchains/svm/client.go | 9 +-- .../externalchains/svm/read_executor.go | 11 ++- .../externalchains/svm/read_executor_test.go | 8 +- universalClient/pushwatcher/client.go | 51 ++++++++++--- universalClient/pushwatcher/client_test.go | 32 ++++---- universalClient/pushwatcher/event_listener.go | 60 +++------------ .../pushwatcher/event_listener_test.go | 14 ++-- .../tss/coordinator/coordinator_test.go | 7 +- .../tss/txbroadcaster/broadcaster_test.go | 9 +-- .../tss/txresolver/resolver_test.go | 9 +-- 19 files changed, 249 insertions(+), 215 deletions(-) diff --git a/universalClient/core/client.go b/universalClient/core/client.go index 25fb17c6..f65808f9 100644 --- a/universalClient/core/client.go +++ b/universalClient/core/client.go @@ -76,13 +76,14 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie return nil, err } - // chainsManager routes read request events into target chain DBs. + // chainsManager resolves destination chains for read request execution. pushChain, err := pushwatcher.NewClient( pushDB, cfg.GetChainConfig(cfg.PushChainID), pushCore, cfg.PushChainID, log, + pushSigner, chainsManager, ) if err != nil { diff --git a/universalClient/externalchains/chains.go b/universalClient/externalchains/chains.go index 12c1a907..9df7ee62 100644 --- a/universalClient/externalchains/chains.go +++ b/universalClient/externalchains/chains.go @@ -343,6 +343,20 @@ func (c *Chains) StopAll() { c.chainConfigs = make(map[string]*uregistrytypes.ChainConfig) } +// GetReader implements common.ChainResolver: resolves a CAIP-2 chain ID to a +// chain client that can execute external read requests. +func (c *Chains) GetReader(chainID string) (common.ChainReader, error) { + client, err := c.GetClient(chainID) + if err != nil { + return nil, err + } + reader, ok := client.(common.ChainReader) + if !ok { + return nil, fmt.Errorf("chain client for %s does not support reads", chainID) + } + return reader, nil +} + // GetClient returns the chain client for the specified chain ID func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { c.chainsMu.RLock() @@ -356,7 +370,6 @@ func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { return client, nil } - // IsEVMChain returns true if the chain uses EVM (e.g. Ethereum, BSC). Used by coordinator for nonce behaviour. func (c *Chains) IsEVMChain(chainID string) bool { c.chainsMu.RLock() diff --git a/universalClient/externalchains/chains_test.go b/universalClient/externalchains/chains_test.go index 4989fa22..20ed9d84 100644 --- a/universalClient/externalchains/chains_test.go +++ b/universalClient/externalchains/chains_test.go @@ -12,7 +12,6 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" - "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -411,10 +410,9 @@ type mockChainClient struct { stopErr error } -func (m *mockChainClient) Start(ctx context.Context) error { m.startCalled = true; return nil } -func (m *mockChainClient) Stop() error { m.stopCalled = true; return m.stopErr } -func (m *mockChainClient) IsHealthy() bool { return true } -func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } +func (m *mockChainClient) Start(ctx context.Context) error { m.startCalled = true; return nil } +func (m *mockChainClient) Stop() error { m.stopCalled = true; return m.stopErr } +func (m *mockChainClient) IsHealthy() bool { return true } func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return nil, nil } diff --git a/universalClient/externalchains/common/event_processor.go b/universalClient/externalchains/common/event_processor.go index 2c082303..8a52df7c 100644 --- a/universalClient/externalchains/common/event_processor.go +++ b/universalClient/externalchains/common/event_processor.go @@ -32,6 +32,12 @@ type ChainReader interface { ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) } +// ChainResolver resolves a CAIP-2 chain ID to its ChainReader. +// Implemented by externalchains.Chains. +type ChainResolver interface { + GetReader(chainID string) (ChainReader, error) +} + // EventProcessor processes events from the chain's database and votes on them type EventProcessor struct { signer VoteSigner @@ -40,12 +46,12 @@ type EventProcessor struct { chainID string inboundEnabled bool outboundEnabled bool - // reader executes READ_REQUEST events against this chain (the push event - // listener routes them into this chain's DB). Nil disables read processing. - reader ChainReader - running bool - stopCh chan struct{} - wg sync.WaitGroup + // readResolver resolves the destination chain of READ_REQUEST events (kept + // in the push chain DB). Nil disables read processing. + readResolver ChainResolver + running bool + stopCh chan struct{} + wg sync.WaitGroup } // NewEventProcessor creates a new event processor @@ -55,7 +61,7 @@ func NewEventProcessor( chainID string, inboundEnabled bool, outboundEnabled bool, - reader ChainReader, + readResolver ChainResolver, logger zerolog.Logger, ) *EventProcessor { return &EventProcessor{ @@ -64,7 +70,7 @@ func NewEventProcessor( chainID: chainID, inboundEnabled: inboundEnabled, outboundEnabled: outboundEnabled, - reader: reader, + readResolver: readResolver, logger: logger.With().Str("component", "event_processor").Str("chain", chainID).Logger(), stopCh: make(chan struct{}), } @@ -163,8 +169,8 @@ func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { continue } } else if event.Type == store.EventTypeReadRequest { - if ep.reader == nil { - ep.logger.Warn().Str("event_id", event.EventID).Msg("no reader configured, skipping read request event processing") + if ep.readResolver == nil { + ep.logger.Warn().Str("event_id", event.EventID).Msg("no read resolver configured, skipping read request event processing") continue } if err := ep.processReadRequestEvent(ctx, &event); err != nil { @@ -235,10 +241,11 @@ func (ep *EventProcessor) processInboundEvent(ctx context.Context, event *store. return ep.markCompleted(event, voteTxHash) } -// processReadRequestEvent executes an external read request against this chain -// and votes the observation. Transient failures (RPC errors, vote failure) -// keep the event CONFIRMED for retry; corrupt or expired requests flip to -// REVERTED without voting (core's EndBlocker expires them on-chain). +// processReadRequestEvent executes an external read request against its +// destination chain and votes the observation. Transient failures (chain not +// served, RPC errors, vote failure) keep the event CONFIRMED for retry; +// corrupt requests flip to REVERTED without voting. Expiry is core's job: +// expired requests leave the pending query, so they stop being stored here. func (ep *EventProcessor) processReadRequestEvent(ctx context.Context, event *store.Event) error { var req uread.ReadRequest if err := json.Unmarshal(event.EventData, &req); err != nil { @@ -246,13 +253,14 @@ func (ep *EventProcessor) processReadRequestEvent(ctx context.Context, event *st return fmt.Errorf("corrupt read request event data: %w", err) } - if req.ExpiryTimestamp > 0 && time.Now().Unix() >= req.ExpiryTimestamp { - ep.logger.Info().Str("request_id", req.RequestID).Msg("read request expired; skipping (core EndBlocker expires it on-chain)") - ep.markReadReverted(event.EventID) + reader, err := ep.readResolver.GetReader(req.DestinationChain) + if err != nil { + // destination not served by this validator yet; retry next tick + ep.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("destination_chain", req.DestinationChain).Msg("no reader for destination chain") return nil } - result, err := ep.reader.ExecuteRead(ctx, &req) + result, err := reader.ExecuteRead(ctx, &req) if err != nil { return fmt.Errorf("read execution failed: %w", err) } diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index fd6c802c..ca3de799 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -58,14 +58,25 @@ func (f *fakeChainReader) ExecuteRead(ctx context.Context, req *uread.ReadReques return f.result, f.err } +type fakeChainResolver struct { + reader ChainReader +} + +func (f *fakeChainResolver) GetReader(chainID string) (ChainReader, error) { + if f.reader == nil { + return nil, fmt.Errorf("no reader for %s", chainID) + } + return f.reader, nil +} + func testReadRequest() *uread.ReadRequest { return &uread.ReadRequest{ - RequestID: "0xabc123", - TargetChain: "eip155:11155111", - Query: []byte{0x01}, - MinConfirmations: 1, - PinnedBlockHeight: 100, - CreatedAtHeight: 7, + RequestID: "0xabc123", + DestinationChain: "eip155:11155111", + Query: []byte{0x01}, + MinConfirmations: 1, + DestinationBlockHeight: 100, + CreatedAtHeight: 7, } } @@ -73,7 +84,11 @@ func newReadTestProcessor(t *testing.T, signer VoteSigner, reader ChainReader) ( t.Helper() database, err := ucdb.OpenInMemoryDB(true) require.NoError(t, err) - ep := NewEventProcessor(signer, database, "eip155:11155111", false, false, reader, zerolog.Nop()) + var resolver ChainResolver + if reader != nil { + resolver = &fakeChainResolver{reader: reader} + } + ep := NewEventProcessor(signer, database, "push_42101-1", false, false, resolver, zerolog.Nop()) return ep, NewChainStore(database) } @@ -136,37 +151,40 @@ func TestProcessReadRequest_VoteFailureKeepsConfirmed(t *testing.T) { assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) } -func TestProcessReadRequest_ExpiredMarkedReverted(t *testing.T) { +func TestProcessReadRequest_ExecutionFailureRetries(t *testing.T) { req := testReadRequest() - req.ExpiryTimestamp = time.Now().Add(-time.Minute).Unix() signer := &fakeVoteSigner{txHash: "VOTE_TX"} - ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{err: fmt.Errorf("rpc down")}) eventID := seedReadRequest(t, cs, req) require.NoError(t, ep.processConfirmedEvents(context.Background())) + // no vote, still CONFIRMED (transient RPC failure) assert.Empty(t, signer.readVotes) - assert.Equal(t, store.StatusReverted, eventStatus(t, cs, eventID)) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) } -func TestProcessReadRequest_ExecutionFailureRetries(t *testing.T) { +func TestProcessReadRequest_NoResolverSkips(t *testing.T) { req := testReadRequest() signer := &fakeVoteSigner{txHash: "VOTE_TX"} - ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{err: fmt.Errorf("rpc down")}) + // nil resolver -> read events are skipped, left CONFIRMED + ep, cs := newReadTestProcessor(t, signer, nil) eventID := seedReadRequest(t, cs, req) require.NoError(t, ep.processConfirmedEvents(context.Background())) - // no vote, still CONFIRMED (transient RPC failure) assert.Empty(t, signer.readVotes) assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) } -func TestProcessReadRequest_NoReaderSkips(t *testing.T) { +func TestProcessReadRequest_UnservedChainRetries(t *testing.T) { req := testReadRequest() signer := &fakeVoteSigner{txHash: "VOTE_TX"} - // nil reader -> read events are skipped, left CONFIRMED - ep, cs := newReadTestProcessor(t, signer, nil) + database, err := ucdb.OpenInMemoryDB(true) + require.NoError(t, err) + // resolver present but has no reader for the destination chain + ep := NewEventProcessor(signer, database, "push_42101-1", false, false, &fakeChainResolver{}, zerolog.Nop()) + cs := NewChainStore(database) eventID := seedReadRequest(t, cs, req) require.NoError(t, ep.processConfirmedEvents(context.Background())) @@ -917,7 +935,7 @@ func TestEventProcessorStruct(t *testing.T) { ep := &EventProcessor{} assert.Nil(t, ep.signer) assert.Nil(t, ep.chainStore) - assert.Nil(t, ep.reader) + assert.Nil(t, ep.readResolver) assert.Empty(t, ep.chainID) assert.False(t, ep.running) assert.Nil(t, ep.stopCh) diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index a5bf6026..dcf22023 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -5,7 +5,6 @@ import ( "fmt" "math/big" - "github.com/pushchain/push-chain-node/universalClient/store" uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -39,11 +38,6 @@ type ChainClient interface { // GetTxBuilder returns the TxBuilder for this chain // Returns an error if txBuilder is not supported for this chain (e.g., Push chain) GetTxBuilder() (TxBuilder, error) - - // AddEvent stores an externally-produced event (e.g. a READ_REQUEST routed - // by the push watcher) in this chain's database for its event processor. - // Returns false if the event already exists. - AddEvent(event *store.Event) (bool, error) } // FundMigrationData contains the data needed to build a fund migration transaction. diff --git a/universalClient/externalchains/evm/client.go b/universalClient/externalchains/evm/client.go index b36cd282..908a2c6b 100644 --- a/universalClient/externalchains/evm/client.go +++ b/universalClient/externalchains/evm/client.go @@ -14,7 +14,6 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" - "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -89,15 +88,13 @@ func NewClient( if pushSigner != nil { inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled - // client is the reader for READ_REQUEST events routed into this chain's - // DB by the push event listener. client.eventProcessor = common.NewEventProcessor( pushSigner, database, chainIDStr, inboundEnabled, outboundEnabled, - client, + nil, log, ) } @@ -203,10 +200,6 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } -// AddEvent stores an externally-produced event in this chain's database. -func (c *Client) AddEvent(event *store.Event) (bool, error) { - return common.NewChainStore(c.database).InsertEventIfNotExists(event) -} // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go index a2057760..78fa0d7b 100644 --- a/universalClient/externalchains/evm/read_executor.go +++ b/universalClient/externalchains/evm/read_executor.go @@ -15,16 +15,24 @@ import ( var balanceOfSelector = []byte{0x70, 0xa0, 0x82, 0x31} // ExecuteRead implements common.ChainReader for EVM chains. -// All validators must produce byte-identical results, so every query runs at a -// deterministic block height. +// All validators must produce byte-identical results, so every query runs at the +// height pinned in the request; execution is gated until that height has +// min_confirmations confirmations so a reorg cannot invalidate the read. func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { env, err := decodeEvmQueryEnvelope(req.Query) if err != nil { return uread.NewErrorResult(err), nil } - height, err := c.resolveReadHeight(ctx, req, env) - if err != nil { + height := req.DestinationBlockHeight + if height == 0 { + height = env.BlockNumber + } + if height == 0 { + return uread.NewErrorResult(fmt.Errorf("read request has no target height")), nil + } + + if err := c.gateHeightConfirmed(ctx, height, uint64(req.MinConfirmations)); err != nil { return nil, err } blockNum := new(big.Int).SetUint64(height) @@ -102,24 +110,16 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea }, nil } -// resolveReadHeight picks the deterministic block height for a read. -// TODO(core): once x/uexecutor pins the height at request creation, -// PinnedBlockHeight is always set and the fallback below must be removed — -// latest-minConfirmations is NOT identical across validators. -func (c *Client) resolveReadHeight(ctx context.Context, req *uread.ReadRequest, env *evmQueryEnvelope) (uint64, error) { - if req.PinnedBlockHeight > 0 { - return req.PinnedBlockHeight, nil - } - if env.BlockNumber > 0 { - return env.BlockNumber, nil - } +// gateHeightConfirmed blocks execution until the target height has at least +// minConfirmations confirmations. An error is transient: the processor keeps +// the event CONFIRMED and retries next tick. +func (c *Client) gateHeightConfirmed(ctx context.Context, height, minConfirmations uint64) error { latest, err := c.rpcClient.GetLatestBlock(ctx) if err != nil { - return 0, fmt.Errorf("failed to get latest block: %w", err) + return fmt.Errorf("failed to get latest block: %w", err) } - conf := uint64(req.MinConfirmations) - if latest <= conf { - return 0, fmt.Errorf("chain height %d below min confirmations %d", latest, conf) + if latest < height+minConfirmations { + return fmt.Errorf("height %d needs %d confirmations, chain at %d; not final yet", height, minConfirmations, latest) } - return latest - conf, nil + return nil } diff --git a/universalClient/externalchains/evm/read_executor_test.go b/universalClient/externalchains/evm/read_executor_test.go index 661c083e..37c97491 100644 --- a/universalClient/externalchains/evm/read_executor_test.go +++ b/universalClient/externalchains/evm/read_executor_test.go @@ -50,6 +50,16 @@ type rpcFault struct { func newReadTestClient(t *testing.T, results map[string]any, faults map[string]rpcFault) *Client { t.Helper() + // every read is gated on the chain tip; default to a comfortably deep chain + // unless the test overrides eth_blockNumber + if results != nil { + if _, ok := results["eth_blockNumber"]; !ok { + if _, ok := faults["eth_blockNumber"]; !ok { + results["eth_blockNumber"] = "0x1000" + } + } + } + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req struct { ID json.RawMessage `json:"id"` @@ -83,11 +93,11 @@ func newReadTestClient(t *testing.T, results map[string]any, faults map[string]r func evmReadRequest(t *testing.T, queryType uint8, blockNumber uint64, payload []byte) *uread.ReadRequest { t.Helper() return &uread.ReadRequest{ - RequestID: "0xreq1", - TargetChain: "eip155:11155111", - Query: packEvmEnvelope(t, queryType, 0, blockNumber, payload), - MinConfirmations: 1, - PinnedBlockHeight: 100, + RequestID: "0xreq1", + DestinationChain: "eip155:11155111", + Query: packEvmEnvelope(t, queryType, 0, blockNumber, payload), + MinConfirmations: 1, + DestinationBlockHeight: 100, } } @@ -178,9 +188,9 @@ func TestExecuteRead_InvalidEnvelope(t *testing.T) { client := newReadTestClient(t, nil, nil) result, err := client.ExecuteRead(context.Background(), &uread.ReadRequest{ - RequestID: "0xreq1", - Query: []byte{0x01, 0x02}, - PinnedBlockHeight: 100, + RequestID: "0xreq1", + Query: []byte{0x01, 0x02}, + DestinationBlockHeight: 100, }) require.NoError(t, err) assert.Equal(t, uread.ReadStatusError, result.Status) @@ -191,7 +201,7 @@ func TestExecuteRead_RPCFailureIsTransient(t *testing.T) { payload, err := addressArgs.Pack(target) require.NoError(t, err) - client := newReadTestClient(t, nil, map[string]rpcFault{ + client := newReadTestClient(t, map[string]any{}, map[string]rpcFault{ "eth_getBlockByNumber": {code: -32000, message: "node is syncing"}, }) @@ -200,7 +210,7 @@ func TestExecuteRead_RPCFailureIsTransient(t *testing.T) { assert.Nil(t, result) } -func TestExecuteRead_EnvelopeBlockNumberFallback(t *testing.T) { +func TestExecuteRead_EnvelopeBlockNumberUsedWhenNotPinned(t *testing.T) { target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") payload, err := addressArgs.Pack(target) require.NoError(t, err) @@ -211,45 +221,60 @@ func TestExecuteRead_EnvelopeBlockNumberFallback(t *testing.T) { }, nil) req := evmReadRequest(t, uint8(evmQueryAccountBalance), 55, payload) - req.PinnedBlockHeight = 0 // TODO(core): fallback removed once core always pins + req.DestinationBlockHeight = 0 // client-provided height in the envelope result, err := client.ExecuteRead(context.Background(), req) require.NoError(t, err) assert.Equal(t, uint64(55), result.ObservedBlockHeight) } -func TestExecuteRead_LatestHeightFallback(t *testing.T) { +func TestExecuteRead_MissingHeightIsVotableError(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + client := newReadTestClient(t, nil, nil) + + req := evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload) + req.DestinationBlockHeight = 0 + + result, err := client.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) +} + +func TestExecuteRead_ConfirmationGate(t *testing.T) { target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") payload, err := addressArgs.Pack(target) require.NoError(t, err) - // TODO(core): delete along with the latest-minConfirmations fallback. - t.Run("uses latest minus min confirmations", func(t *testing.T) { + t.Run("height not deep enough is transient", func(t *testing.T) { client := newReadTestClient(t, map[string]any{ - "eth_blockNumber": "0x64", // 100 - "eth_getBlockByNumber": fakeHeader(99), - "eth_getBalance": "0x1", + "eth_blockNumber": "0x64", // 100 }, nil) req := evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload) - req.PinnedBlockHeight = 0 + req.DestinationBlockHeight = 100 + req.MinConfirmations = 5 // needs chain at >= 105 result, err := client.ExecuteRead(context.Background(), req) - require.NoError(t, err) - assert.Equal(t, uint64(99), result.ObservedBlockHeight) + require.Error(t, err) + assert.Nil(t, result) }) - t.Run("chain height below min confirmations is transient", func(t *testing.T) { + t.Run("executes once deep enough", func(t *testing.T) { client := newReadTestClient(t, map[string]any{ - "eth_blockNumber": "0x1", + "eth_blockNumber": "0x69", // 105 + "eth_getBlockByNumber": fakeHeader(100), + "eth_getBalance": "0x1", }, nil) req := evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload) - req.PinnedBlockHeight = 0 + req.DestinationBlockHeight = 100 req.MinConfirmations = 5 result, err := client.ExecuteRead(context.Background(), req) - require.Error(t, err) - assert.Nil(t, result) + require.NoError(t, err) + assert.Equal(t, uint64(100), result.ObservedBlockHeight) }) } diff --git a/universalClient/externalchains/svm/client.go b/universalClient/externalchains/svm/client.go index a73140a2..6b29c72c 100644 --- a/universalClient/externalchains/svm/client.go +++ b/universalClient/externalchains/svm/client.go @@ -12,7 +12,6 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" - "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -99,15 +98,13 @@ func NewClient( if pushSigner != nil { inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled - // client is the reader for READ_REQUEST events routed into this chain's - // DB by the push event listener. client.eventProcessor = common.NewEventProcessor( pushSigner, database, chainIDStr, inboundEnabled, outboundEnabled, - client, + nil, log, ) } @@ -213,10 +210,6 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } -// AddEvent stores an externally-produced event in this chain's database. -func (c *Client) AddEvent(event *store.Event) (bool, error) { - return common.NewChainStore(c.database).InsertEventIfNotExists(event) -} // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { diff --git a/universalClient/externalchains/svm/read_executor.go b/universalClient/externalchains/svm/read_executor.go index 4b305e29..d5fc7085 100644 --- a/universalClient/externalchains/svm/read_executor.go +++ b/universalClient/externalchains/svm/read_executor.go @@ -17,11 +17,10 @@ const splTokenAmountOffset = 64 // ExecuteRead implements common.ChainReader for Solana chains. // -// Determinism caveat: Solana RPC cannot query state at an exact past slot, only -// ">= minSlot" via minContextSlot, so ObservedBlockHeight may differ across -// validators. TODO(core): ballot key must cover ResultData only (drop -// slot/hash) for solana, or quorum will never converge — flagged in -// docs/read-from-chains-implementation-plan.md. +// Solana cannot query state at an exact past slot, so reads run at finalized +// commitment with minContextSlot as a staleness floor. ObservedBlockHeight (the +// context slot) may differ across validators; core's ballot key covers the +// result value only, never the observed slot. func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { env, err := decodeSolanaQueryEnvelope(req.Query) if err != nil { @@ -33,7 +32,7 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea } account := solana.PublicKeyFromBytes(req.Owner) - minSlot := max(env.MinSlot, req.PinnedBlockHeight) + minSlot := max(env.MinSlot, req.DestinationBlockHeight) switch env.QueryType { case solanaQueryLamportBalance: diff --git a/universalClient/externalchains/svm/read_executor_test.go b/universalClient/externalchains/svm/read_executor_test.go index b462b190..17331ec9 100644 --- a/universalClient/externalchains/svm/read_executor_test.go +++ b/universalClient/externalchains/svm/read_executor_test.go @@ -72,10 +72,10 @@ func svmReadRequest(t *testing.T, queryType uint8, minSlot uint64, owner []byte) }) require.NoError(t, err) return &uread.ReadRequest{ - RequestID: "0xreq1", - TargetChain: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", - Owner: owner, - Query: query, + RequestID: "0xreq1", + DestinationChain: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", + Owner: owner, + Query: query, } } diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 85e2d74a..503c01bb 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -10,29 +10,33 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushcore" + "github.com/pushchain/push-chain-node/universalClient/pushsigner" "github.com/rs/zerolog" ) // Client implements the ChainClient interface for Push chain type Client struct { - logger zerolog.Logger - pushCore *pushcore.Client - database *db.DB - eventListener *EventListener - eventCleaner *common.EventCleaner - ctx context.Context - cancel context.CancelFunc + logger zerolog.Logger + pushCore *pushcore.Client + database *db.DB + eventListener *EventListener + eventCleaner *common.EventCleaner + eventProcessor *common.EventProcessor + ctx context.Context + cancel context.CancelFunc } // NewClient creates a new Push chain client. -// chainResolver may be nil; the listener then skips read request polling. +// pushSigner and readResolver may be nil; the event processor (read request +// execution + voting) is only wired when both are present. func NewClient( database *db.DB, chainConfig *config.ChainSpecificConfig, pushCore *pushcore.Client, chainID string, logger zerolog.Logger, - chainResolver ExternalChainResolver, + pushSigner *pushsigner.Signer, + readResolver common.ChainResolver, ) (*Client, error) { // Normalize nil config so downstream uses don't need nil guards. if chainConfig == nil { @@ -45,7 +49,6 @@ func NewClient( database, logger, chainConfig, - chainResolver, ) if err != nil { return nil, fmt.Errorf("failed to create event listener: %w", err) @@ -67,6 +70,20 @@ func NewClient( eventCleaner: eventCleaner, } + // The push DB holds READ_REQUEST events; the processor executes them on + // their destination chains (via readResolver) and votes the results. + if pushSigner != nil && readResolver != nil { + client.eventProcessor = common.NewEventProcessor( + pushSigner, + database, + chainID, + false, + false, + readResolver, + logger, + ) + } + return client, nil } @@ -88,6 +105,13 @@ func (c *Client) Start(ctx context.Context) error { } } + // Start event processor if wired + if c.eventProcessor != nil { + if err := c.eventProcessor.Start(c.ctx); err != nil { + return fmt.Errorf("failed to start event processor: %w", err) + } + } + c.logger.Info().Msg("Push chain client started successfully") return nil } @@ -113,6 +137,13 @@ func (c *Client) Stop() error { c.eventCleaner.Stop() } + // Stop event processor + if c.eventProcessor != nil { + if err := c.eventProcessor.Stop(); err != nil { + c.logger.Error().Err(err).Str("subsystem", "event_processor").Msg("subsystem failed to stop") + } + } + c.logger.Info().Msg("Push chain client stopped") return nil } diff --git a/universalClient/pushwatcher/client_test.go b/universalClient/pushwatcher/client_test.go index 9f3a0b01..8df232c4 100644 --- a/universalClient/pushwatcher/client_test.go +++ b/universalClient/pushwatcher/client_test.go @@ -34,7 +34,7 @@ func TestNewClient(t *testing.T) { pc := newTestPushCoreClient() t.Run("success with nil config", func(t *testing.T) { - client, err := NewClient(database, nil, pc, "push-chain", logger, nil) + client, err := NewClient(database, nil, pc, "push-chain", logger, nil, nil) require.NoError(t, err) require.NotNil(t, client) assert.NotNil(t, client.eventListener) @@ -48,27 +48,27 @@ func TestNewClient(t *testing.T) { CleanupIntervalSeconds: &cleanup, RetentionPeriodSeconds: &retention, } - client, err := NewClient(database, cfg, pc, "push-chain", logger, nil) + client, err := NewClient(database, cfg, pc, "push-chain", logger, nil, nil) require.NoError(t, err) require.NotNil(t, client) assert.NotNil(t, client.eventCleaner) }) t.Run("nil pushcore fails", func(t *testing.T) { - _, err := NewClient(database, nil, nil, "push-chain", logger, nil) + _, err := NewClient(database, nil, nil, "push-chain", logger, nil, nil) require.Error(t, err) assert.Contains(t, err.Error(), "push client is nil") }) t.Run("nil database fails", func(t *testing.T) { - _, err := NewClient(nil, nil, pc, "push-chain", logger, nil) + _, err := NewClient(nil, nil, pc, "push-chain", logger, nil, nil) require.Error(t, err) assert.Contains(t, err.Error(), "database is nil") }) } func TestClient_StartStop(t *testing.T) { - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil, nil) require.NoError(t, err) ctx := context.Background() @@ -96,7 +96,7 @@ func TestClient_StopBeforeStart(t *testing.T) { // Stop on a freshly created client (never started) should not panic. // The cancel func is nil, eventListener.Stop() returns ErrNotRunning but // the client logs and swallows that error, returning nil. - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil, nil) require.NoError(t, err) // Should not panic or return error @@ -104,7 +104,7 @@ func TestClient_StopBeforeStart(t *testing.T) { } func TestClient_DoubleStop(t *testing.T) { - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil, nil) require.NoError(t, err) ctx := context.Background() @@ -122,7 +122,7 @@ func TestClient_StartStopWithEventCleaner(t *testing.T) { CleanupIntervalSeconds: &cleanup, RetentionPeriodSeconds: &retention, } - client, err := NewClient(newTestDB(t), cfg, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) + client, err := NewClient(newTestDB(t), cfg, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil, nil) require.NoError(t, err) require.NotNil(t, client.eventCleaner) @@ -140,7 +140,7 @@ func TestClient_StartStopWithEventCleaner(t *testing.T) { func TestClient_StartStopLifecycleMultiple(t *testing.T) { // Verify the client can be started and stopped multiple times (restart). - client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil) + client, err := NewClient(newTestDB(t), nil, newTestPushCoreClient(), "push-chain", zerolog.Nop(), nil, nil) require.NoError(t, err) ctx := context.Background() @@ -183,7 +183,7 @@ func TestNewClient_CleanerAlwaysWired(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - client, err := NewClient(database, tc.cfg, pc, "push-chain", logger, nil) + client, err := NewClient(database, tc.cfg, pc, "push-chain", logger, nil, nil) require.NoError(t, err) require.NotNil(t, client.eventCleaner, "cleaner must always be wired up") }) @@ -199,7 +199,7 @@ func TestNewClient_NegativePollInterval(t *testing.T) { cfg := &config.ChainSpecificConfig{ EventPollingIntervalSeconds: &poll, } - client, err := NewClient(database, cfg, pc, "push-chain", logger, nil) + client, err := NewClient(database, cfg, pc, "push-chain", logger, nil, nil) require.NoError(t, err) // Negative poll interval should fall back to default assert.Equal(t, DefaultPollInterval, client.eventListener.cfg.PollInterval) @@ -215,7 +215,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil, nil) + el, err := NewEventListener(pc, database, logger, nil) require.NoError(t, err) event := &store.Event{ @@ -236,7 +236,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil, nil) + el, err := NewEventListener(pc, database, logger, nil) require.NoError(t, err) event := &store.Event{ @@ -260,7 +260,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil, nil) + el, err := NewEventListener(pc, database, logger, nil) require.NoError(t, err) for i := 0; i < 5; i++ { @@ -282,7 +282,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil, nil) + el, err := NewEventListener(pc, database, logger, nil) require.NoError(t, err) event := &store.Event{ @@ -310,7 +310,7 @@ func TestStoreEvent(t *testing.T) { pc := newTestPushCoreClient() logger := zerolog.Nop() - el, err := NewEventListener(pc, database, logger, nil, nil) + el, err := NewEventListener(pc, database, logger, nil) require.NoError(t, err) event := &store.Event{ diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index 7b46d3fa..54dbb19c 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -28,23 +28,14 @@ type Config struct { PollInterval time.Duration } -// ExternalChainResolver resolves a CAIP-2 chain ID to its chain client. -// Satisfied by externalchains.Chains. -type ExternalChainResolver interface { - GetClient(chainID string) (common.ChainClient, error) -} - // EventListener polls Push chain for active TSS events, pending outbounds and // pending read requests via gRPC, converts them to store.Events, and inserts -// them into the local DB. Read request events are routed into the target -// chain's DB (via chainResolver) so that chain's own event processor executes -// and votes them. +// them into the local DB. type EventListener struct { - pushCore *pushcore.Client - chainStore *common.ChainStore - chainResolver ExternalChainResolver - cfg Config - logger zerolog.Logger + pushCore *pushcore.Client + chainStore *common.ChainStore + cfg Config + logger zerolog.Logger mu sync.Mutex running bool @@ -53,13 +44,11 @@ type EventListener struct { } // NewEventListener creates a new Push event listener. -// chainResolver may be nil; read request polling is skipped without it. func NewEventListener( pushCore *pushcore.Client, database *db.DB, logger zerolog.Logger, chainConfig *config.ChainSpecificConfig, - chainResolver ExternalChainResolver, ) (*EventListener, error) { if pushCore == nil { return nil, ErrNilClient @@ -74,11 +63,10 @@ func NewEventListener( } return &EventListener{ - pushCore: pushCore, - chainStore: common.NewChainStore(database), - chainResolver: chainResolver, - cfg: Config{PollInterval: pollInterval}, - logger: logger.With().Str("component", "push_event_listener").Logger(), + pushCore: pushCore, + chainStore: common.NewChainStore(database), + cfg: Config{PollInterval: pollInterval}, + logger: logger.With().Str("component", "push_event_listener").Logger(), }, nil } @@ -250,16 +238,9 @@ func (el *EventListener) pollFundMigrationEvents(ctx context.Context) int { return newCount } -// pollReadRequestEvents fetches pending external read requests and routes each -// into its target chain's DB, where that chain's event processor executes and -// votes it. Requests for chains this validator doesn't serve are skipped and -// retried next poll (core keeps returning them until fulfilled or expired). -// Returns new event count. +// pollReadRequestEvents fetches pending external read requests and inserts +// them into the DB. Returns new event count. func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { - if el.chainResolver == nil { - return 0 - } - requests, err := el.pushCore.GetAllPendingReadRequests(ctx) if err != nil { if errors.Is(err, pushcore.ErrReadQueriesNotAvailable) { @@ -272,30 +253,13 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { var newCount int for _, req := range requests { - targetClient, err := el.chainResolver.GetClient(req.DestinationChain) - if err != nil { - el.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("destination_chain", req.DestinationChain).Msg("target chain not served; skipping read request") - continue - } - event, err := convertReadRequestEvent(req) if err != nil { el.logger.Warn().Err(err).Str("request_id", req.RequestID).Msg("failed to convert read request") continue } - stored, err := targetClient.AddEvent(event) - if err != nil { - el.logger.Error().Err(err).Str("event_id", event.EventID).Str("destination_chain", req.DestinationChain).Msg("failed to store read request") - continue - } - if stored { - el.logger.Debug(). - Str("event_id", event.EventID). - Str("destination_chain", req.DestinationChain). - Msg("routed read request to target chain") - newCount++ - } + newCount += el.storeEvent(event) } return newCount diff --git a/universalClient/pushwatcher/event_listener_test.go b/universalClient/pushwatcher/event_listener_test.go index 4805df99..983b3fb3 100644 --- a/universalClient/pushwatcher/event_listener_test.go +++ b/universalClient/pushwatcher/event_listener_test.go @@ -17,7 +17,7 @@ func TestNewEventListener(t *testing.T) { client := newTestPushCoreClient() t.Run("success with defaults", func(t *testing.T) { - el, err := NewEventListener(client, db, logger, nil, nil) + el, err := NewEventListener(client, db, logger, nil) require.NoError(t, err) require.NotNil(t, el) assert.Equal(t, DefaultPollInterval, el.cfg.PollInterval) @@ -25,19 +25,19 @@ func TestNewEventListener(t *testing.T) { }) t.Run("nil client", func(t *testing.T) { - _, err := NewEventListener(nil, db, logger, nil, nil) + _, err := NewEventListener(nil, db, logger, nil) assert.ErrorIs(t, err, ErrNilClient) }) t.Run("nil database", func(t *testing.T) { - _, err := NewEventListener(client, nil, logger, nil, nil) + _, err := NewEventListener(client, nil, logger, nil) assert.ErrorIs(t, err, ErrNilDatabase) }) t.Run("custom poll interval from config", func(t *testing.T) { poll := 10 cfg := config.ChainSpecificConfig{EventPollingIntervalSeconds: &poll} - el, err := NewEventListener(client, db, logger, &cfg, nil) + el, err := NewEventListener(client, db, logger, &cfg) require.NoError(t, err) assert.Equal(t, 10*time.Second, el.cfg.PollInterval) }) @@ -45,14 +45,14 @@ func TestNewEventListener(t *testing.T) { t.Run("zero poll interval uses default", func(t *testing.T) { poll := 0 cfg := config.ChainSpecificConfig{EventPollingIntervalSeconds: &poll} - el, err := NewEventListener(client, db, logger, &cfg, nil) + el, err := NewEventListener(client, db, logger, &cfg) require.NoError(t, err) assert.Equal(t, DefaultPollInterval, el.cfg.PollInterval) }) } func TestEventListener_StartStop(t *testing.T) { - el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil, nil) + el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil) require.NoError(t, err) ctx := context.Background() @@ -73,7 +73,7 @@ func TestEventListener_StartStop(t *testing.T) { } func TestEventListener_RestartAfterStop(t *testing.T) { - el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil, nil) + el, err := NewEventListener(newTestPushCoreClient(), newTestDB(t), zerolog.Nop(), nil) require.NoError(t, err) ctx := context.Background() diff --git a/universalClient/tss/coordinator/coordinator_test.go b/universalClient/tss/coordinator/coordinator_test.go index de65f326..69cf0f26 100644 --- a/universalClient/tss/coordinator/coordinator_test.go +++ b/universalClient/tss/coordinator/coordinator_test.go @@ -81,10 +81,9 @@ type coordMockChainClient struct { builderErr error } -func (m *coordMockChainClient) Start(context.Context) error { return nil } -func (m *coordMockChainClient) Stop() error { return nil } -func (m *coordMockChainClient) IsHealthy() bool { return true } -func (m *coordMockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } +func (m *coordMockChainClient) Start(context.Context) error { return nil } +func (m *coordMockChainClient) Stop() error { return nil } +func (m *coordMockChainClient) IsHealthy() bool { return true } func (m *coordMockChainClient) GetTxBuilder() (common.TxBuilder, error) { if m.builderErr != nil { return nil, m.builderErr diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index bc693f72..653d13d0 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -79,11 +79,10 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } -func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } -func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { t.Helper() diff --git a/universalClient/tss/txresolver/resolver_test.go b/universalClient/tss/txresolver/resolver_test.go index beb79aa1..b7df913f 100644 --- a/universalClient/tss/txresolver/resolver_test.go +++ b/universalClient/tss/txresolver/resolver_test.go @@ -76,11 +76,10 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } -func (m *mockChainClient) AddEvent(event *store.Event) (bool, error) { return true, nil } -func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { t.Helper() From 4f822fc2b7d9bd9e0dc0f1abd30088dd88813c5b Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 16:53:45 +0530 Subject: [PATCH 15/54] fix: move type to client --- universalClient/externalchains/chains.go | 14 - universalClient/externalchains/chains_test.go | 3 + .../externalchains/common/event_processor.go | 78 +----- .../common/event_processor_test.go | 242 +++--------------- .../externalchains/common/types.go | 12 + universalClient/externalchains/evm/client.go | 9 +- universalClient/externalchains/svm/client.go | 9 +- universalClient/pushwatcher/client.go | 54 ++-- universalClient/pushwatcher/read_processor.go | 215 ++++++++++++++++ .../pushwatcher/read_processor_test.go | 216 ++++++++++++++++ .../tss/coordinator/coordinator_test.go | 3 + .../tss/txbroadcaster/broadcaster_test.go | 9 +- .../tss/txresolver/resolver_test.go | 9 +- 13 files changed, 541 insertions(+), 332 deletions(-) create mode 100644 universalClient/pushwatcher/read_processor.go create mode 100644 universalClient/pushwatcher/read_processor_test.go diff --git a/universalClient/externalchains/chains.go b/universalClient/externalchains/chains.go index 9df7ee62..df25f2b8 100644 --- a/universalClient/externalchains/chains.go +++ b/universalClient/externalchains/chains.go @@ -343,20 +343,6 @@ func (c *Chains) StopAll() { c.chainConfigs = make(map[string]*uregistrytypes.ChainConfig) } -// GetReader implements common.ChainResolver: resolves a CAIP-2 chain ID to a -// chain client that can execute external read requests. -func (c *Chains) GetReader(chainID string) (common.ChainReader, error) { - client, err := c.GetClient(chainID) - if err != nil { - return nil, err - } - reader, ok := client.(common.ChainReader) - if !ok { - return nil, fmt.Errorf("chain client for %s does not support reads", chainID) - } - return reader, nil -} - // GetClient returns the chain client for the specified chain ID func (c *Chains) GetClient(chainID string) (common.ChainClient, error) { c.chainsMu.RLock() diff --git a/universalClient/externalchains/chains_test.go b/universalClient/externalchains/chains_test.go index 20ed9d84..9426e0a8 100644 --- a/universalClient/externalchains/chains_test.go +++ b/universalClient/externalchains/chains_test.go @@ -413,6 +413,9 @@ type mockChainClient struct { func (m *mockChainClient) Start(ctx context.Context) error { m.startCalled = true; return nil } func (m *mockChainClient) Stop() error { m.stopCalled = true; return m.stopErr } func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + return nil, nil +} func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return nil, nil } diff --git a/universalClient/externalchains/common/event_processor.go b/universalClient/externalchains/common/event_processor.go index 8a52df7c..c49fcb3d 100644 --- a/universalClient/externalchains/common/event_processor.go +++ b/universalClient/externalchains/common/event_processor.go @@ -13,7 +13,6 @@ import ( "github.com/mr-tron/base58" "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" "github.com/rs/zerolog" ) @@ -23,19 +22,6 @@ import ( type VoteSigner interface { VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) - VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) -} - -// ChainReader executes an external read request against one chain. -// Implemented by the evm and svm chain clients. -type ChainReader interface { - ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) -} - -// ChainResolver resolves a CAIP-2 chain ID to its ChainReader. -// Implemented by externalchains.Chains. -type ChainResolver interface { - GetReader(chainID string) (ChainReader, error) } // EventProcessor processes events from the chain's database and votes on them @@ -46,12 +32,9 @@ type EventProcessor struct { chainID string inboundEnabled bool outboundEnabled bool - // readResolver resolves the destination chain of READ_REQUEST events (kept - // in the push chain DB). Nil disables read processing. - readResolver ChainResolver - running bool - stopCh chan struct{} - wg sync.WaitGroup + running bool + stopCh chan struct{} + wg sync.WaitGroup } // NewEventProcessor creates a new event processor @@ -61,7 +44,6 @@ func NewEventProcessor( chainID string, inboundEnabled bool, outboundEnabled bool, - readResolver ChainResolver, logger zerolog.Logger, ) *EventProcessor { return &EventProcessor{ @@ -70,7 +52,6 @@ func NewEventProcessor( chainID: chainID, inboundEnabled: inboundEnabled, outboundEnabled: outboundEnabled, - readResolver: readResolver, logger: logger.With().Str("component", "event_processor").Str("chain", chainID).Logger(), stopCh: make(chan struct{}), } @@ -136,7 +117,7 @@ func (ep *EventProcessor) processLoop(ctx context.Context) { } } -// processConfirmedEvents processes confirmed events (inbound, outbound and read requests) +// processConfirmedEvents processes confirmed events (both inbound and outbound) func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { events, err := ep.chainStore.GetConfirmedEvents(1000) if err != nil { @@ -168,18 +149,6 @@ func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { Msg("failed to vote on outbound event") continue } - } else if event.Type == store.EventTypeReadRequest { - if ep.readResolver == nil { - ep.logger.Warn().Str("event_id", event.EventID).Msg("no read resolver configured, skipping read request event processing") - continue - } - if err := ep.processReadRequestEvent(ctx, &event); err != nil { - ep.logger.Error(). - Err(err). - Str("event_id", event.EventID). - Msg("failed to vote on read request event") - continue - } } } @@ -241,39 +210,6 @@ func (ep *EventProcessor) processInboundEvent(ctx context.Context, event *store. return ep.markCompleted(event, voteTxHash) } -// processReadRequestEvent executes an external read request against its -// destination chain and votes the observation. Transient failures (chain not -// served, RPC errors, vote failure) keep the event CONFIRMED for retry; -// corrupt requests flip to REVERTED without voting. Expiry is core's job: -// expired requests leave the pending query, so they stop being stored here. -func (ep *EventProcessor) processReadRequestEvent(ctx context.Context, event *store.Event) error { - var req uread.ReadRequest - if err := json.Unmarshal(event.EventData, &req); err != nil { - ep.markReadReverted(event.EventID) - return fmt.Errorf("corrupt read request event data: %w", err) - } - - reader, err := ep.readResolver.GetReader(req.DestinationChain) - if err != nil { - // destination not served by this validator yet; retry next tick - ep.logger.Debug().Err(err).Str("request_id", req.RequestID).Str("destination_chain", req.DestinationChain).Msg("no reader for destination chain") - return nil - } - - result, err := reader.ExecuteRead(ctx, &req) - if err != nil { - return fmt.Errorf("read execution failed: %w", err) - } - - voteTxHash, err := ep.signer.VoteReadResult(ctx, req.RequestID, result) - if err != nil { - // TODO(core): ErrVoteReadNotAvailable falls through here until MsgVoteReadResult lands. - return fmt.Errorf("failed to vote read result: %w", err) - } - - return ep.markCompleted(event, voteTxHash) -} - // markCompleted atomically records the vote hash and flips CONFIRMED -> COMPLETED. func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) error { rowsAffected, err := ep.chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) @@ -294,12 +230,6 @@ func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) e return nil } -func (ep *EventProcessor) markReadReverted(eventID string) { - if _, err := ep.chainStore.UpdateEventStatus(eventID, store.StatusConfirmed, store.StatusReverted); err != nil { - ep.logger.Error().Err(err).Str("event_id", eventID).Msg("failed to mark read request reverted") - } -} - // constructInbound creates an Inbound message from event data func (ep *EventProcessor) constructInbound(event *store.Event) (*uexecutortypes.Inbound, error) { var eventData UniversalTx diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index ca3de799..caf7a44a 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -3,7 +3,6 @@ package common import ( "context" "encoding/json" - "fmt" "math/big" "testing" "time" @@ -14,191 +13,15 @@ import ( ucdb "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) -type fakeVoteSigner struct { - readVotes map[string]*uread.ReadResult - txHash string - err error -} - -func (f *fakeVoteSigner) VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) { - if f.err != nil { - return "", f.err - } - return "", fmt.Errorf("inbound vote not supported by fake") -} - -func (f *fakeVoteSigner) VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) { - if f.err != nil { - return "", f.err - } - return "", fmt.Errorf("outbound vote not supported by fake") -} - -func (f *fakeVoteSigner) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { - if f.err != nil { - return "", f.err - } - if f.readVotes == nil { - f.readVotes = make(map[string]*uread.ReadResult) - } - f.readVotes[requestID] = result - return f.txHash, nil -} - -type fakeChainReader struct { - result *uread.ReadResult - err error -} - -func (f *fakeChainReader) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { - return f.result, f.err -} - -type fakeChainResolver struct { - reader ChainReader -} - -func (f *fakeChainResolver) GetReader(chainID string) (ChainReader, error) { - if f.reader == nil { - return nil, fmt.Errorf("no reader for %s", chainID) - } - return f.reader, nil -} - -func testReadRequest() *uread.ReadRequest { - return &uread.ReadRequest{ - RequestID: "0xabc123", - DestinationChain: "eip155:11155111", - Query: []byte{0x01}, - MinConfirmations: 1, - DestinationBlockHeight: 100, - CreatedAtHeight: 7, - } -} - -func newReadTestProcessor(t *testing.T, signer VoteSigner, reader ChainReader) (*EventProcessor, *ChainStore) { - t.Helper() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - var resolver ChainResolver - if reader != nil { - resolver = &fakeChainResolver{reader: reader} - } - ep := NewEventProcessor(signer, database, "push_42101-1", false, false, resolver, zerolog.Nop()) - return ep, NewChainStore(database) -} - -func seedReadRequest(t *testing.T, cs *ChainStore, req *uread.ReadRequest) string { - t.Helper() - eventData, err := json.Marshal(req) - require.NoError(t, err) - eventID := "read:" + req.RequestID - stored, err := cs.InsertEventIfNotExists(&store.Event{ - EventID: eventID, - BlockHeight: req.CreatedAtHeight, - Type: store.EventTypeReadRequest, - ConfirmationType: store.ConfirmationInstant, - Status: store.StatusConfirmed, - EventData: eventData, - }) - require.NoError(t, err) - require.True(t, stored) - return eventID -} - -func eventStatus(t *testing.T, cs *ChainStore, eventID string) string { - t.Helper() - var event store.Event - require.NoError(t, cs.database.Client().Where("event_id = ?", eventID).First(&event).Error) - return event.Status -} - -func TestProcessReadRequest_SuccessFlow(t *testing.T) { - req := testReadRequest() - result := &uread.ReadResult{ - Status: uread.ReadStatusSuccess, - ResultData: []byte{0xaa}, - ObservedBlockHeight: 100, - } - signer := &fakeVoteSigner{txHash: "VOTE_TX"} - ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: result}) - eventID := seedReadRequest(t, cs, req) - - require.NoError(t, ep.processConfirmedEvents(context.Background())) - - require.Contains(t, signer.readVotes, req.RequestID) - assert.Equal(t, result, signer.readVotes[req.RequestID]) - assert.Equal(t, store.StatusCompleted, eventStatus(t, cs, eventID)) - - // second tick must not re-vote - signer.readVotes = nil - require.NoError(t, ep.processConfirmedEvents(context.Background())) - assert.Empty(t, signer.readVotes) -} - -func TestProcessReadRequest_VoteFailureKeepsConfirmed(t *testing.T) { - req := testReadRequest() - signer := &fakeVoteSigner{err: fmt.Errorf("MsgVoteReadResult not available")} - ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) - eventID := seedReadRequest(t, cs, req) - - require.NoError(t, ep.processConfirmedEvents(context.Background())) - - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestProcessReadRequest_ExecutionFailureRetries(t *testing.T) { - req := testReadRequest() - signer := &fakeVoteSigner{txHash: "VOTE_TX"} - ep, cs := newReadTestProcessor(t, signer, &fakeChainReader{err: fmt.Errorf("rpc down")}) - eventID := seedReadRequest(t, cs, req) - - require.NoError(t, ep.processConfirmedEvents(context.Background())) - - // no vote, still CONFIRMED (transient RPC failure) - assert.Empty(t, signer.readVotes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestProcessReadRequest_NoResolverSkips(t *testing.T) { - req := testReadRequest() - signer := &fakeVoteSigner{txHash: "VOTE_TX"} - // nil resolver -> read events are skipped, left CONFIRMED - ep, cs := newReadTestProcessor(t, signer, nil) - eventID := seedReadRequest(t, cs, req) - - require.NoError(t, ep.processConfirmedEvents(context.Background())) - - assert.Empty(t, signer.readVotes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestProcessReadRequest_UnservedChainRetries(t *testing.T) { - req := testReadRequest() - signer := &fakeVoteSigner{txHash: "VOTE_TX"} - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - // resolver present but has no reader for the destination chain - ep := NewEventProcessor(signer, database, "push_42101-1", false, false, &fakeChainResolver{}, zerolog.Nop()) - cs := NewChainStore(database) - eventID := seedReadRequest(t, cs, req) - - require.NoError(t, ep.processConfirmedEvents(context.Background())) - - assert.Empty(t, signer.readVotes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - func TestNewEventProcessor(t *testing.T) { t.Run("creates event processor with valid params", func(t *testing.T) { logger := zerolog.Nop() chainID := "eip155:1" - processor := NewEventProcessor(nil, nil, chainID, true, true, nil, logger) + processor := NewEventProcessor(nil, nil, chainID, true, true, logger) require.NotNil(t, processor) assert.Equal(t, chainID, processor.chainID) @@ -230,7 +53,7 @@ func TestEventProcessorStop(t *testing.T) { func TestEventProcessorBase58ToHex(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "test-chain", true, true, nil, logger) + processor := NewEventProcessor(nil, nil, "test-chain", true, true, logger) t.Run("empty string returns 0x", func(t *testing.T) { result, err := processor.base58ToHex("") @@ -264,7 +87,7 @@ func TestEventProcessorBase58ToHex(t *testing.T) { func TestEventProcessorConstructInbound(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) t.Run("nil event returns error", func(t *testing.T) { inbound, err := processor.constructInbound(nil) @@ -430,7 +253,7 @@ func TestEventProcessorConstructInbound(t *testing.T) { func TestEventProcessorParseOutboundEventData(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) t.Run("nil event returns error", func(t *testing.T) { data, err := processor.parseOutboundEventData(nil) @@ -509,7 +332,7 @@ func TestEventProcessorParseOutboundEventData(t *testing.T) { func TestEventProcessorBuildOutboundObservation(t *testing.T) { logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) + processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) t.Run("builds observation with gas fee from parsed data", func(t *testing.T) { outboundData := &OutboundEvent{ @@ -581,7 +404,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("nil event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) event := &store.Event{ EventID: "0xabc:0", @@ -595,7 +418,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("empty event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) event := &store.Event{ EventID: "0xabc:0", @@ -609,7 +432,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("invalid JSON event data returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) event := &store.Event{ EventID: "0xabc:0", @@ -623,7 +446,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("missing tx_id returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) eventData, _ := json.Marshal(OutboundEvent{ TxID: "", @@ -641,7 +464,7 @@ func TestProcessOutboundEvent(t *testing.T) { t.Run("missing universal_tx_id returns parse error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) eventData, _ := json.Marshal(OutboundEvent{ TxID: "0xtxid", @@ -671,7 +494,7 @@ func TestProcessInboundEvent(t *testing.T) { t.Run("nil event data returns construct error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) event := &store.Event{ EventID: "0xabc:0", @@ -685,7 +508,7 @@ func TestProcessInboundEvent(t *testing.T) { t.Run("invalid JSON event data returns construct error", func(t *testing.T) { database := setupDB(t) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) event := &store.Event{ EventID: "0xabc:0", @@ -715,7 +538,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { t.Run("no confirmed events returns nil", func(t *testing.T) { database := setupDB(t, nil) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -731,7 +554,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -758,7 +581,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) // Should not return error - errors on individual events are logged and skipped err := ep.processConfirmedEvents(ctx) @@ -788,7 +611,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -811,7 +634,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -831,7 +654,7 @@ func TestProcessConfirmedEventsRouting(t *testing.T) { }, }) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -849,7 +672,7 @@ func TestProcessLoopContextCancellation(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) t.Run("processLoop exits promptly on context cancel", func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) @@ -885,7 +708,7 @@ func TestProcessLoopStopChannel(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) t.Run("processLoop exits promptly on stop signal", func(t *testing.T) { ctx := context.Background() @@ -935,7 +758,6 @@ func TestEventProcessorStruct(t *testing.T) { ep := &EventProcessor{} assert.Nil(t, ep.signer) assert.Nil(t, ep.chainStore) - assert.Nil(t, ep.readResolver) assert.Empty(t, ep.chainID) assert.False(t, ep.running) assert.Nil(t, ep.stopCh) @@ -948,25 +770,25 @@ func TestNewEventProcessorEnabledFlags(t *testing.T) { logger := zerolog.Nop() t.Run("both enabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) assert.True(t, ep.inboundEnabled) assert.True(t, ep.outboundEnabled) }) t.Run("inbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, false, nil, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", true, false, logger) assert.True(t, ep.inboundEnabled) assert.False(t, ep.outboundEnabled) }) t.Run("outbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, true, nil, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", false, true, logger) assert.False(t, ep.inboundEnabled) assert.True(t, ep.outboundEnabled) }) t.Run("both disabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, false, nil, logger) + ep := NewEventProcessor(nil, nil, "eip155:1", false, false, logger) assert.False(t, ep.inboundEnabled) assert.False(t, ep.outboundEnabled) }) @@ -978,7 +800,7 @@ func TestEventProcessorStartDoubleStart(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1005,7 +827,7 @@ func TestEventProcessorStopIdempotent(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1035,7 +857,7 @@ func TestEventProcessorIsRunningStateTransitions(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) // Initial state: not running assert.False(t, ep.IsRunning()) @@ -1070,7 +892,7 @@ func TestEventProcessorStopViaContextCancel(t *testing.T) { require.NoError(t, err) defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) ctx, cancel := context.WithCancel(context.Background()) @@ -1135,7 +957,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { t.Run("inbound disabled skips inbound events, leaves them CONFIRMED", func(t *testing.T) { database := setupDB(t, makeEvents()) // inbound=false, outbound=false (no signer so outbound will also fail to vote, but that's ok) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1148,7 +970,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { t.Run("outbound disabled skips outbound events, leaves them CONFIRMED", func(t *testing.T) { database := setupDB(t, makeEvents()) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1169,7 +991,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { EventData: outboundEventData, }, }) - ep := NewEventProcessor(nil, database, "eip155:1", true, false, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", true, false, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) @@ -1190,7 +1012,7 @@ func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { EventData: inboundEventData, }, }) - ep := NewEventProcessor(nil, database, "eip155:1", false, true, nil, logger) + ep := NewEventProcessor(nil, database, "eip155:1", false, true, logger) err := ep.processConfirmedEvents(ctx) require.NoError(t, err) diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index dcf22023..a389437f 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -5,9 +5,16 @@ import ( "fmt" "math/big" + "github.com/pushchain/push-chain-node/universalClient/uread" uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) +// ReadRequestHandler executes a read request on one destination chain. +// Consumed by the push watcher's read processor. +type ReadRequestHandler interface { + ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) +} + // EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256) // so read results are byte-identical across validators and decodable by the // requesting contract. The bounds check guards against a malicious RPC value @@ -38,6 +45,11 @@ type ChainClient interface { // GetTxBuilder returns the TxBuilder for this chain // Returns an error if txBuilder is not supported for this chain (e.g., Push chain) GetTxBuilder() (TxBuilder, error) + + // GetReadRequestHandler returns the handler executing read requests + // destined for this chain + // Returns an error if reads are not available (e.g. client not started) + GetReadRequestHandler() (ReadRequestHandler, error) } // FundMigrationData contains the data needed to build a fund migration transaction. diff --git a/universalClient/externalchains/evm/client.go b/universalClient/externalchains/evm/client.go index 908a2c6b..07f83684 100644 --- a/universalClient/externalchains/evm/client.go +++ b/universalClient/externalchains/evm/client.go @@ -94,7 +94,6 @@ func NewClient( chainIDStr, inboundEnabled, outboundEnabled, - nil, log, ) } @@ -200,6 +199,14 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } +// GetReadRequestHandler returns the read request handler for this chain +func (c *Client) GetReadRequestHandler() (common.ReadRequestHandler, error) { + if c.rpcClient == nil { + return nil, fmt.Errorf("read handler not available for chain %s (client not started)", c.chainIDStr) + } + return c, nil +} + // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { diff --git a/universalClient/externalchains/svm/client.go b/universalClient/externalchains/svm/client.go index 6b29c72c..95e00104 100644 --- a/universalClient/externalchains/svm/client.go +++ b/universalClient/externalchains/svm/client.go @@ -104,7 +104,6 @@ func NewClient( chainIDStr, inboundEnabled, outboundEnabled, - nil, log, ) } @@ -210,6 +209,14 @@ func (c *Client) GetTxBuilder() (common.TxBuilder, error) { return c.txBuilder, nil } +// GetReadRequestHandler returns the read request handler for this chain +func (c *Client) GetReadRequestHandler() (common.ReadRequestHandler, error) { + if c.rpcClient == nil { + return nil, fmt.Errorf("read handler not available for chain %s (client not started)", c.chainIDStr) + } + return c, nil +} + // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 503c01bb..318e12b8 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -16,18 +16,18 @@ import ( // Client implements the ChainClient interface for Push chain type Client struct { - logger zerolog.Logger - pushCore *pushcore.Client - database *db.DB - eventListener *EventListener - eventCleaner *common.EventCleaner - eventProcessor *common.EventProcessor - ctx context.Context - cancel context.CancelFunc + logger zerolog.Logger + pushCore *pushcore.Client + database *db.DB + eventListener *EventListener + eventCleaner *common.EventCleaner + readProcessor *ReadProcessor + ctx context.Context + cancel context.CancelFunc } // NewClient creates a new Push chain client. -// pushSigner and readResolver may be nil; the event processor (read request +// pushSigner and chainResolver may be nil; the read processor (read request // execution + voting) is only wired when both are present. func NewClient( database *db.DB, @@ -36,7 +36,7 @@ func NewClient( chainID string, logger zerolog.Logger, pushSigner *pushsigner.Signer, - readResolver common.ChainResolver, + chainResolver ChainResolver, ) (*Client, error) { // Normalize nil config so downstream uses don't need nil guards. if chainConfig == nil { @@ -70,18 +70,20 @@ func NewClient( eventCleaner: eventCleaner, } - // The push DB holds READ_REQUEST events; the processor executes them on - // their destination chains (via readResolver) and votes the results. - if pushSigner != nil && readResolver != nil { - client.eventProcessor = common.NewEventProcessor( + // The push DB holds READ_REQUEST events; the read processor executes them + // on their destination chains (via chainResolver) and votes the results. + if pushSigner != nil && chainResolver != nil { + readProcessor, err := NewReadProcessor( pushSigner, + chainResolver, database, - chainID, - false, - false, - readResolver, + eventListener.cfg.PollInterval, logger, ) + if err != nil { + return nil, fmt.Errorf("failed to create read processor: %w", err) + } + client.readProcessor = readProcessor } return client, nil @@ -105,10 +107,10 @@ func (c *Client) Start(ctx context.Context) error { } } - // Start event processor if wired - if c.eventProcessor != nil { - if err := c.eventProcessor.Start(c.ctx); err != nil { - return fmt.Errorf("failed to start event processor: %w", err) + // Start read processor if wired + if c.readProcessor != nil { + if err := c.readProcessor.Start(c.ctx); err != nil { + return fmt.Errorf("failed to start read processor: %w", err) } } @@ -137,10 +139,10 @@ func (c *Client) Stop() error { c.eventCleaner.Stop() } - // Stop event processor - if c.eventProcessor != nil { - if err := c.eventProcessor.Stop(); err != nil { - c.logger.Error().Err(err).Str("subsystem", "event_processor").Msg("subsystem failed to stop") + // Stop read processor + if c.readProcessor != nil { + if err := c.readProcessor.Stop(); err != nil { + c.logger.Error().Err(err).Str("subsystem", "read_processor").Msg("subsystem failed to stop") } } diff --git a/universalClient/pushwatcher/read_processor.go b/universalClient/pushwatcher/read_processor.go new file mode 100644 index 00000000..b9c74b96 --- /dev/null +++ b/universalClient/pushwatcher/read_processor.go @@ -0,0 +1,215 @@ +package pushwatcher + +import ( + "context" + "encoding/json" + "sync" + "time" + + "github.com/pushchain/push-chain-node/universalClient/db" + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" + "github.com/rs/zerolog" +) + +const readProcessBatchSize = 1000 + +// ChainResolver resolves a CAIP-2 chain ID to its chain client. +// Satisfied by externalchains.Chains. +type ChainResolver interface { + GetClient(chainID string) (common.ChainClient, error) +} + +// readVoter submits a read observation vote to Push Chain. +// Satisfied by *pushsigner.Signer. +type readVoter interface { + VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) +} + +// ReadProcessor consumes READ_REQUEST events from the push chain DB, executes +// each request on its destination chain via the resolved handler, and votes +// the result. Transient failures (destination not served, RPC errors, vote +// failure) keep the event CONFIRMED for retry; corrupt events flip to +// REVERTED. Expiry is core's job: expired requests leave the pending query. +type ReadProcessor struct { + voter readVoter + resolver ChainResolver + chainStore *common.ChainStore + cfg Config + logger zerolog.Logger + + mu sync.Mutex + running bool + cancel context.CancelFunc + wg sync.WaitGroup +} + +// NewReadProcessor creates a new read processor. +func NewReadProcessor( + voter readVoter, + resolver ChainResolver, + database *db.DB, + pollInterval time.Duration, + logger zerolog.Logger, +) (*ReadProcessor, error) { + if database == nil { + return nil, ErrNilDatabase + } + + if pollInterval <= 0 { + pollInterval = DefaultPollInterval + } + + return &ReadProcessor{ + voter: voter, + resolver: resolver, + chainStore: common.NewChainStore(database), + cfg: Config{PollInterval: pollInterval}, + logger: logger.With().Str("component", "push_read_processor").Logger(), + }, nil +} + +// Start begins processing read request events. +func (p *ReadProcessor) Start(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.running { + return ErrAlreadyRunning + } + + childCtx, cancel := context.WithCancel(ctx) + p.cancel = cancel + p.running = true + + p.logger.Debug(). + Dur("poll_interval", p.cfg.PollInterval). + Msg("starting read processor") + + p.wg.Add(1) + go p.run(childCtx) + + return nil +} + +// Stop gracefully stops the processor. +func (p *ReadProcessor) Stop() error { + p.mu.Lock() + defer p.mu.Unlock() + + if !p.running { + return ErrNotRunning + } + + p.cancel() + p.wg.Wait() + p.running = false + + return nil +} + +func (p *ReadProcessor) run(ctx context.Context) { + defer p.wg.Done() + + p.processConfirmedReads(ctx) + + ticker := time.NewTicker(p.cfg.PollInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + p.processConfirmedReads(ctx) + } + } +} + +// processConfirmedReads executes and votes stored read request events. +func (p *ReadProcessor) processConfirmedReads(ctx context.Context) { + events, err := p.chainStore.GetConfirmedEvents(readProcessBatchSize) + if err != nil { + p.logger.Error().Err(err).Msg("failed to query confirmed events") + return + } + + for _, event := range events { + if event.Type != store.EventTypeReadRequest { + continue + } + + select { + case <-ctx.Done(): + return + default: + } + + if err := p.processOne(ctx, &event); err != nil { + p.logger.Error(). + Err(err). + Str("event_id", event.EventID). + Msg("failed to process read request event") + } + } +} + +func (p *ReadProcessor) processOne(ctx context.Context, event *store.Event) error { + var req uread.ReadRequest + if err := json.Unmarshal(event.EventData, &req); err != nil { + p.markReverted(event.EventID) + return err + } + + log := p.logger.With().Str("request_id", req.RequestID).Logger() + + destClient, err := p.resolver.GetClient(req.DestinationChain) + if err != nil { + // destination not served by this validator yet; retry next tick + log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("destination chain not served") + return nil + } + + handler, err := destClient.GetReadRequestHandler() + if err != nil { + // destination client not ready to serve reads yet; retry next tick + log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("read handler not available") + return nil + } + + result, err := handler.ExecuteRead(ctx, &req) + if err != nil { + log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("read execution failed; will retry") + return nil + } + + voteTxHash, err := p.voter.VoteReadResult(ctx, req.RequestID, result) + if err != nil { + // TODO(core): ErrVoteReadNotAvailable falls through here until MsgVoteReadResult lands. + log.Warn().Err(err).Msg("failed to vote read result; will retry") + return nil + } + + rowsAffected, err := p.chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) + if err != nil { + return err + } + if rowsAffected == 0 { + return nil + } + + log.Info(). + Str("vote_tx_hash", voteTxHash). + Int32("status", int32(result.Status)). + Uint64("observed_height", result.ObservedBlockHeight). + Msg("read request voted") + + return nil +} + +func (p *ReadProcessor) markReverted(eventID string) { + if _, err := p.chainStore.UpdateEventStatus(eventID, store.StatusConfirmed, store.StatusReverted); err != nil { + p.logger.Error().Err(err).Str("event_id", eventID).Msg("failed to mark read request reverted") + } +} diff --git a/universalClient/pushwatcher/read_processor_test.go b/universalClient/pushwatcher/read_processor_test.go new file mode 100644 index 00000000..784f0c1c --- /dev/null +++ b/universalClient/pushwatcher/read_processor_test.go @@ -0,0 +1,216 @@ +package pushwatcher + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +type fakeReadVoter struct { + votes map[string]*uread.ReadResult + txHash string + err error +} + +func (f *fakeReadVoter) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { + if f.err != nil { + return "", f.err + } + if f.votes == nil { + f.votes = make(map[string]*uread.ReadResult) + } + f.votes[requestID] = result + return f.txHash, nil +} + +type fakeDestClient struct { + result *uread.ReadResult + err error +} + +func (f *fakeDestClient) Start(ctx context.Context) error { return nil } +func (f *fakeDestClient) Stop() error { return nil } +func (f *fakeDestClient) IsHealthy() bool { return true } +func (f *fakeDestClient) GetTxBuilder() (common.TxBuilder, error) { + return nil, fmt.Errorf("not supported") +} +func (f *fakeDestClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + return f, nil +} +func (f *fakeDestClient) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + return f.result, f.err +} + +type fakeChainResolver struct { + client common.ChainClient +} + +func (f *fakeChainResolver) GetClient(chainID string) (common.ChainClient, error) { + if f.client == nil { + return nil, fmt.Errorf("no client for %s", chainID) + } + return f.client, nil +} + +func testReadRequest() *uread.ReadRequest { + return &uread.ReadRequest{ + RequestID: "0xabc123", + DestinationChain: "eip155:11155111", + Query: []byte{0x01}, + MinConfirmations: 1, + DestinationBlockHeight: 100, + CreatedAtHeight: 7, + } +} + +func newTestReadProcessor(t *testing.T, voter readVoter, destClient common.ChainClient) (*ReadProcessor, *common.ChainStore) { + t.Helper() + database := newTestDB(t) + p, err := NewReadProcessor(voter, &fakeChainResolver{client: destClient}, database, 0, zerolog.Nop()) + require.NoError(t, err) + return p, common.NewChainStore(database) +} + +func seedReadRequest(t *testing.T, cs *common.ChainStore, req *uread.ReadRequest) string { + t.Helper() + event, err := convertReadRequestEvent(req) + require.NoError(t, err) + stored, err := cs.InsertEventIfNotExists(event) + require.NoError(t, err) + require.True(t, stored) + return event.EventID +} + +func eventStatus(t *testing.T, cs *common.ChainStore, eventID string) string { + t.Helper() + events, err := cs.GetConfirmedEvents(100) + require.NoError(t, err) + for i := range events { + if events[i].EventID == eventID { + return events[i].Status + } + } + // not CONFIRMED anymore; caller asserts via CAS probes + return "" +} + +func TestReadProcessor_SuccessFlow(t *testing.T) { + req := testReadRequest() + result := &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: []byte{0xaa}, + ObservedBlockHeight: 100, + } + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: result}) + eventID := seedReadRequest(t, cs, req) + + p.processConfirmedReads(context.Background()) + + require.Contains(t, voter.votes, req.RequestID) + assert.Equal(t, result, voter.votes[req.RequestID]) + + // event flipped to COMPLETED with vote tx hash + rows, err := cs.UpdateEventStatus(eventID, store.StatusCompleted, store.StatusCompleted) + require.NoError(t, err) + assert.Equal(t, int64(1), rows) + + // second tick must not re-vote + voter.votes = nil + p.processConfirmedReads(context.Background()) + assert.Empty(t, voter.votes) +} + +func TestReadProcessor_VoteFailureKeepsConfirmed(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{err: fmt.Errorf("MsgVoteReadResult not available")} + p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + eventID := seedReadRequest(t, cs, req) + + p.processConfirmedReads(context.Background()) + + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + +func TestReadProcessor_ExecutionFailureRetries(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadProcessor(t, voter, &fakeDestClient{err: fmt.Errorf("rpc down")}) + eventID := seedReadRequest(t, cs, req) + + p.processConfirmedReads(context.Background()) + + assert.Empty(t, voter.votes) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + +func TestReadProcessor_UnservedChainRetries(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadProcessor(t, voter, nil) + eventID := seedReadRequest(t, cs, req) + + p.processConfirmedReads(context.Background()) + + assert.Empty(t, voter.votes) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) +} + +func TestReadProcessor_CorruptEventReverted(t *testing.T) { + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + + stored, err := cs.InsertEventIfNotExists(&store.Event{ + EventID: "corrupt-read", + Type: store.EventTypeReadRequest, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: []byte("not json"), + }) + require.NoError(t, err) + require.True(t, stored) + + p.processConfirmedReads(context.Background()) + + assert.Empty(t, voter.votes) + rows, err := cs.UpdateEventStatus("corrupt-read", store.StatusReverted, store.StatusReverted) + require.NoError(t, err) + assert.Equal(t, int64(1), rows) +} + +func TestReadProcessor_IgnoresOtherEventTypes(t *testing.T) { + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + + stored, err := cs.InsertEventIfNotExists(&store.Event{ + EventID: "tss-event", + Type: store.EventTypeKeygen, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: []byte("{}"), + }) + require.NoError(t, err) + require.True(t, stored) + + p.processConfirmedReads(context.Background()) + + assert.Empty(t, voter.votes) + assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, "tss-event")) +} + +func TestReadProcessor_StartStop(t *testing.T) { + p, _ := newTestReadProcessor(t, &fakeReadVoter{}, nil) + + require.NoError(t, p.Start(context.Background())) + assert.Equal(t, ErrAlreadyRunning, p.Start(context.Background())) + require.NoError(t, p.Stop()) + assert.Equal(t, ErrNotRunning, p.Stop()) +} diff --git a/universalClient/tss/coordinator/coordinator_test.go b/universalClient/tss/coordinator/coordinator_test.go index 69cf0f26..b4ea3cd4 100644 --- a/universalClient/tss/coordinator/coordinator_test.go +++ b/universalClient/tss/coordinator/coordinator_test.go @@ -84,6 +84,9 @@ type coordMockChainClient struct { func (m *coordMockChainClient) Start(context.Context) error { return nil } func (m *coordMockChainClient) Stop() error { return nil } func (m *coordMockChainClient) IsHealthy() bool { return true } +func (m *coordMockChainClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + return nil, nil +} func (m *coordMockChainClient) GetTxBuilder() (common.TxBuilder, error) { if m.builderErr != nil { return nil, m.builderErr diff --git a/universalClient/tss/txbroadcaster/broadcaster_test.go b/universalClient/tss/txbroadcaster/broadcaster_test.go index 653d13d0..dae85fb3 100644 --- a/universalClient/tss/txbroadcaster/broadcaster_test.go +++ b/universalClient/tss/txbroadcaster/broadcaster_test.go @@ -79,9 +79,12 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + return nil, nil +} func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { diff --git a/universalClient/tss/txresolver/resolver_test.go b/universalClient/tss/txresolver/resolver_test.go index b7df913f..b13aec47 100644 --- a/universalClient/tss/txresolver/resolver_test.go +++ b/universalClient/tss/txresolver/resolver_test.go @@ -76,9 +76,12 @@ func (m *mockTxBuilder) BroadcastFundMigrationTx(ctx context.Context, req *commo type mockChainClient struct{ builder *mockTxBuilder } -func (m *mockChainClient) Start(context.Context) error { return nil } -func (m *mockChainClient) Stop() error { return nil } -func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) Start(context.Context) error { return nil } +func (m *mockChainClient) Stop() error { return nil } +func (m *mockChainClient) IsHealthy() bool { return true } +func (m *mockChainClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + return nil, nil +} func (m *mockChainClient) GetTxBuilder() (common.TxBuilder, error) { return m.builder, nil } func setupTestDB(t *testing.T) (*eventstore.Store, *gorm.DB) { From 7212d2e90d7c9fdf80ea9a7d1fd4c0045b6ef20d Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 17:29:11 +0530 Subject: [PATCH 16/54] fix: read req handling --- universalClient/pushwatcher/client.go | 59 ++--- .../pushwatcher/event_processor.go | 151 ++++++++++++ .../pushwatcher/event_processor_test.go | 90 ++++++++ ...d_processor.go => read_event_processor.go} | 143 +++--------- .../pushwatcher/read_event_processor_test.go | 215 +++++++++++++++++ .../pushwatcher/read_processor_test.go | 216 ------------------ 6 files changed, 517 insertions(+), 357 deletions(-) create mode 100644 universalClient/pushwatcher/event_processor.go create mode 100644 universalClient/pushwatcher/event_processor_test.go rename universalClient/pushwatcher/{read_processor.go => read_event_processor.go} (53%) create mode 100644 universalClient/pushwatcher/read_event_processor_test.go delete mode 100644 universalClient/pushwatcher/read_processor_test.go diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 318e12b8..297246b2 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -11,24 +11,25 @@ import ( "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushcore" "github.com/pushchain/push-chain-node/universalClient/pushsigner" + "github.com/pushchain/push-chain-node/universalClient/store" "github.com/rs/zerolog" ) // Client implements the ChainClient interface for Push chain type Client struct { - logger zerolog.Logger - pushCore *pushcore.Client - database *db.DB - eventListener *EventListener - eventCleaner *common.EventCleaner - readProcessor *ReadProcessor - ctx context.Context - cancel context.CancelFunc + logger zerolog.Logger + pushCore *pushcore.Client + database *db.DB + eventListener *EventListener + eventCleaner *common.EventCleaner + eventProcessor *EventProcessor + ctx context.Context + cancel context.CancelFunc } // NewClient creates a new Push chain client. -// pushSigner and chainResolver may be nil; the read processor (read request -// execution + voting) is only wired when both are present. +// pushSigner and chainResolver may be nil; the READ_REQUEST handler is only +// registered when both are present. func NewClient( database *db.DB, chainConfig *config.ChainSpecificConfig, @@ -70,21 +71,21 @@ func NewClient( eventCleaner: eventCleaner, } - // The push DB holds READ_REQUEST events; the read processor executes them - // on their destination chains (via chainResolver) and votes the results. + eventProcessor, err := NewEventProcessor(database, eventListener.cfg.PollInterval, logger) + if err != nil { + return nil, fmt.Errorf("failed to create event processor: %w", err) + } + + // READ_REQUEST events are executed on their destination chains (via + // chainResolver) and the results voted back. if pushSigner != nil && chainResolver != nil { - readProcessor, err := NewReadProcessor( - pushSigner, - chainResolver, - database, - eventListener.cfg.PollInterval, - logger, - ) + readEventProcessor, err := NewReadEventProcessor(pushSigner, chainResolver, database, logger) if err != nil { - return nil, fmt.Errorf("failed to create read processor: %w", err) + return nil, fmt.Errorf("failed to create read event processor: %w", err) } - client.readProcessor = readProcessor + eventProcessor.RegisterHandler(store.EventTypeReadRequest, readEventProcessor) } + client.eventProcessor = eventProcessor return client, nil } @@ -107,10 +108,10 @@ func (c *Client) Start(ctx context.Context) error { } } - // Start read processor if wired - if c.readProcessor != nil { - if err := c.readProcessor.Start(c.ctx); err != nil { - return fmt.Errorf("failed to start read processor: %w", err) + // Start event processor + if c.eventProcessor != nil { + if err := c.eventProcessor.Start(c.ctx); err != nil { + return fmt.Errorf("failed to start event processor: %w", err) } } @@ -139,10 +140,10 @@ func (c *Client) Stop() error { c.eventCleaner.Stop() } - // Stop read processor - if c.readProcessor != nil { - if err := c.readProcessor.Stop(); err != nil { - c.logger.Error().Err(err).Str("subsystem", "read_processor").Msg("subsystem failed to stop") + // Stop event processor + if c.eventProcessor != nil { + if err := c.eventProcessor.Stop(); err != nil { + c.logger.Error().Err(err).Str("subsystem", "event_processor").Msg("subsystem failed to stop") } } diff --git a/universalClient/pushwatcher/event_processor.go b/universalClient/pushwatcher/event_processor.go new file mode 100644 index 00000000..f848ae76 --- /dev/null +++ b/universalClient/pushwatcher/event_processor.go @@ -0,0 +1,151 @@ +package pushwatcher + +import ( + "context" + "sync" + "time" + + "github.com/pushchain/push-chain-node/universalClient/db" + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/rs/zerolog" +) + +const eventProcessBatchSize = 1000 + +// EventHandler processes one CONFIRMED push chain event of a registered type. +// Handlers own the event's status transitions; a returned error is logged and +// the event is retried next tick. +type EventHandler interface { + HandleEvent(ctx context.Context, event *store.Event) error +} + +// EventProcessor drains CONFIRMED events from the push chain DB and dispatches +// them to the handler registered for their type. Event types without a handler +// are ignored (e.g. TSS events, which are consumed by the TSS subsystem). +type EventProcessor struct { + chainStore *common.ChainStore + handlers map[string]EventHandler + cfg Config + logger zerolog.Logger + + mu sync.Mutex + running bool + cancel context.CancelFunc + wg sync.WaitGroup +} + +// NewEventProcessor creates a new push event processor. Register handlers +// before Start. +func NewEventProcessor( + database *db.DB, + pollInterval time.Duration, + logger zerolog.Logger, +) (*EventProcessor, error) { + if database == nil { + return nil, ErrNilDatabase + } + + if pollInterval <= 0 { + pollInterval = DefaultPollInterval + } + + return &EventProcessor{ + chainStore: common.NewChainStore(database), + handlers: make(map[string]EventHandler), + cfg: Config{PollInterval: pollInterval}, + logger: logger.With().Str("component", "push_event_processor").Logger(), + }, nil +} + +// RegisterHandler registers a handler for an event type. Must be called before Start. +func (p *EventProcessor) RegisterHandler(eventType string, handler EventHandler) { + p.handlers[eventType] = handler +} + +// Start begins processing events. +func (p *EventProcessor) Start(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.running { + return ErrAlreadyRunning + } + + childCtx, cancel := context.WithCancel(ctx) + p.cancel = cancel + p.running = true + + p.logger.Debug(). + Dur("poll_interval", p.cfg.PollInterval). + Msg("starting push event processor") + + p.wg.Add(1) + go p.run(childCtx) + + return nil +} + +// Stop gracefully stops the processor. +func (p *EventProcessor) Stop() error { + p.mu.Lock() + defer p.mu.Unlock() + + if !p.running { + return ErrNotRunning + } + + p.cancel() + p.wg.Wait() + p.running = false + + return nil +} + +func (p *EventProcessor) run(ctx context.Context) { + defer p.wg.Done() + + p.processConfirmedEvents(ctx) + + ticker := time.NewTicker(p.cfg.PollInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + p.processConfirmedEvents(ctx) + } + } +} + +// processConfirmedEvents dispatches CONFIRMED events to their registered handlers. +func (p *EventProcessor) processConfirmedEvents(ctx context.Context) { + events, err := p.chainStore.GetConfirmedEvents(eventProcessBatchSize) + if err != nil { + p.logger.Error().Err(err).Msg("failed to query confirmed events") + return + } + + for _, event := range events { + handler, ok := p.handlers[event.Type] + if !ok { + continue + } + + select { + case <-ctx.Done(): + return + default: + } + + if err := handler.HandleEvent(ctx, &event); err != nil { + p.logger.Error(). + Err(err). + Str("event_id", event.EventID). + Str("type", event.Type). + Msg("failed to process event") + } + } +} diff --git a/universalClient/pushwatcher/event_processor_test.go b/universalClient/pushwatcher/event_processor_test.go new file mode 100644 index 00000000..92e60bcd --- /dev/null +++ b/universalClient/pushwatcher/event_processor_test.go @@ -0,0 +1,90 @@ +package pushwatcher + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" +) + +type fakeEventHandler struct { + handled []string + err error +} + +func (f *fakeEventHandler) HandleEvent(ctx context.Context, event *store.Event) error { + f.handled = append(f.handled, event.EventID) + return f.err +} + +func seedEvent(t *testing.T, cs *common.ChainStore, eventID, eventType string) { + t.Helper() + stored, err := cs.InsertEventIfNotExists(&store.Event{ + EventID: eventID, + Type: eventType, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: []byte("{}"), + }) + require.NoError(t, err) + require.True(t, stored) +} + +func TestEventProcessor_DispatchesByType(t *testing.T) { + database := newTestDB(t) + p, err := NewEventProcessor(database, 0, zerolog.Nop()) + require.NoError(t, err) + cs := common.NewChainStore(database) + + readHandler := &fakeEventHandler{} + p.RegisterHandler(store.EventTypeReadRequest, readHandler) + + seedEvent(t, cs, "read-1", store.EventTypeReadRequest) + seedEvent(t, cs, "tss-1", store.EventTypeKeygen) // no handler registered + + p.processConfirmedEvents(context.Background()) + + assert.Equal(t, []string{"read-1"}, readHandler.handled) +} + +func TestEventProcessor_HandlerErrorKeepsProcessing(t *testing.T) { + database := newTestDB(t) + p, err := NewEventProcessor(database, 0, zerolog.Nop()) + require.NoError(t, err) + cs := common.NewChainStore(database) + + failing := &fakeEventHandler{err: fmt.Errorf("boom")} + p.RegisterHandler(store.EventTypeReadRequest, failing) + + seedEvent(t, cs, "read-1", store.EventTypeReadRequest) + seedEvent(t, cs, "read-2", store.EventTypeReadRequest) + + p.processConfirmedEvents(context.Background()) + + // both attempted despite errors, both still CONFIRMED for retry + assert.Len(t, failing.handled, 2) + events, err := cs.GetConfirmedEvents(10) + require.NoError(t, err) + assert.Len(t, events, 2) +} + +func TestEventProcessor_NilDatabase(t *testing.T) { + _, err := NewEventProcessor(nil, 0, zerolog.Nop()) + assert.ErrorIs(t, err, ErrNilDatabase) +} + +func TestEventProcessor_StartStop(t *testing.T) { + p, err := NewEventProcessor(newTestDB(t), 0, zerolog.Nop()) + require.NoError(t, err) + + require.NoError(t, p.Start(context.Background())) + assert.Equal(t, ErrAlreadyRunning, p.Start(context.Background())) + require.NoError(t, p.Stop()) + assert.Equal(t, ErrNotRunning, p.Stop()) +} diff --git a/universalClient/pushwatcher/read_processor.go b/universalClient/pushwatcher/read_event_processor.go similarity index 53% rename from universalClient/pushwatcher/read_processor.go rename to universalClient/pushwatcher/read_event_processor.go index b9c74b96..1544c91e 100644 --- a/universalClient/pushwatcher/read_processor.go +++ b/universalClient/pushwatcher/read_event_processor.go @@ -3,8 +3,6 @@ package pushwatcher import ( "context" "encoding/json" - "sync" - "time" "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" @@ -13,8 +11,6 @@ import ( "github.com/rs/zerolog" ) -const readProcessBatchSize = 1000 - // ChainResolver resolves a CAIP-2 chain ID to its chain client. // Satisfied by externalchains.Chains. type ChainResolver interface { @@ -27,135 +23,45 @@ type readVoter interface { VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) } -// ReadProcessor consumes READ_REQUEST events from the push chain DB, executes -// each request on its destination chain via the resolved handler, and votes -// the result. Transient failures (destination not served, RPC errors, vote -// failure) keep the event CONFIRMED for retry; corrupt events flip to -// REVERTED. Expiry is core's job: expired requests leave the pending query. -type ReadProcessor struct { +// ReadEventProcessor handles READ_REQUEST events: it executes each request on +// its destination chain via the resolved read handler and votes the result. +// Transient failures (destination not served, RPC errors, vote failure) keep +// the event CONFIRMED for retry; corrupt events flip to REVERTED. Expiry is +// core's job: expired requests leave the pending query. +type ReadEventProcessor struct { voter readVoter resolver ChainResolver chainStore *common.ChainStore - cfg Config logger zerolog.Logger - - mu sync.Mutex - running bool - cancel context.CancelFunc - wg sync.WaitGroup } -// NewReadProcessor creates a new read processor. -func NewReadProcessor( +// NewReadEventProcessor creates the handler for READ_REQUEST events. +func NewReadEventProcessor( voter readVoter, resolver ChainResolver, database *db.DB, - pollInterval time.Duration, logger zerolog.Logger, -) (*ReadProcessor, error) { +) (*ReadEventProcessor, error) { if database == nil { return nil, ErrNilDatabase } - if pollInterval <= 0 { - pollInterval = DefaultPollInterval - } - - return &ReadProcessor{ + return &ReadEventProcessor{ voter: voter, resolver: resolver, chainStore: common.NewChainStore(database), - cfg: Config{PollInterval: pollInterval}, - logger: logger.With().Str("component", "push_read_processor").Logger(), + logger: logger.With().Str("component", "push_read_event_processor").Logger(), }, nil } -// Start begins processing read request events. -func (p *ReadProcessor) Start(ctx context.Context) error { - p.mu.Lock() - defer p.mu.Unlock() - - if p.running { - return ErrAlreadyRunning - } - - childCtx, cancel := context.WithCancel(ctx) - p.cancel = cancel - p.running = true - - p.logger.Debug(). - Dur("poll_interval", p.cfg.PollInterval). - Msg("starting read processor") - - p.wg.Add(1) - go p.run(childCtx) - - return nil -} - -// Stop gracefully stops the processor. -func (p *ReadProcessor) Stop() error { - p.mu.Lock() - defer p.mu.Unlock() - - if !p.running { - return ErrNotRunning - } - - p.cancel() - p.wg.Wait() - p.running = false - - return nil -} - -func (p *ReadProcessor) run(ctx context.Context) { - defer p.wg.Done() - - p.processConfirmedReads(ctx) - - ticker := time.NewTicker(p.cfg.PollInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - p.processConfirmedReads(ctx) - } - } -} - -// processConfirmedReads executes and votes stored read request events. -func (p *ReadProcessor) processConfirmedReads(ctx context.Context) { - events, err := p.chainStore.GetConfirmedEvents(readProcessBatchSize) - if err != nil { - p.logger.Error().Err(err).Msg("failed to query confirmed events") - return - } - - for _, event := range events { - if event.Type != store.EventTypeReadRequest { - continue - } - - select { - case <-ctx.Done(): - return - default: - } - - if err := p.processOne(ctx, &event); err != nil { - p.logger.Error(). - Err(err). - Str("event_id", event.EventID). - Msg("failed to process read request event") - } +// HandleEvent implements EventHandler for READ_REQUEST events. +func (p *ReadEventProcessor) HandleEvent(ctx context.Context, event *store.Event) error { + if p.isExpired(event) { + p.logger.Info().Str("event_id", event.EventID).Msg("read request expired; marking reverted") + p.markReverted(event.EventID) + return nil } -} -func (p *ReadProcessor) processOne(ctx context.Context, event *store.Event) error { var req uread.ReadRequest if err := json.Unmarshal(event.EventData, &req); err != nil { p.markReverted(event.EventID) @@ -208,7 +114,20 @@ func (p *ReadProcessor) processOne(ctx context.Context, event *store.Event) erro return nil } -func (p *ReadProcessor) markReverted(eventID string) { +// isExpired reports whether the request's expiry Push chain height has been +// reached, using the chain height persisted by the event listener. +func (p *ReadEventProcessor) isExpired(event *store.Event) bool { + if event.ExpiryBlockHeight == 0 { + return false + } + pushHeight, err := p.chainStore.GetChainHeight() + if err != nil { + return false + } + return pushHeight >= event.ExpiryBlockHeight +} + +func (p *ReadEventProcessor) markReverted(eventID string) { if _, err := p.chainStore.UpdateEventStatus(eventID, store.StatusConfirmed, store.StatusReverted); err != nil { p.logger.Error().Err(err).Str("event_id", eventID).Msg("failed to mark read request reverted") } diff --git a/universalClient/pushwatcher/read_event_processor_test.go b/universalClient/pushwatcher/read_event_processor_test.go new file mode 100644 index 00000000..e1ad5bd6 --- /dev/null +++ b/universalClient/pushwatcher/read_event_processor_test.go @@ -0,0 +1,215 @@ +package pushwatcher + +import ( + "context" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/store" + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +type fakeReadVoter struct { + votes map[string]*uread.ReadResult + txHash string + err error +} + +func (f *fakeReadVoter) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { + if f.err != nil { + return "", f.err + } + if f.votes == nil { + f.votes = make(map[string]*uread.ReadResult) + } + f.votes[requestID] = result + return f.txHash, nil +} + +type fakeDestClient struct { + result *uread.ReadResult + err error + notStarted bool +} + +func (f *fakeDestClient) Start(ctx context.Context) error { return nil } +func (f *fakeDestClient) Stop() error { return nil } +func (f *fakeDestClient) IsHealthy() bool { return true } +func (f *fakeDestClient) GetTxBuilder() (common.TxBuilder, error) { + return nil, fmt.Errorf("not supported") +} +func (f *fakeDestClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { + if f.notStarted { + return nil, fmt.Errorf("client not started") + } + return f, nil +} +func (f *fakeDestClient) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + return f.result, f.err +} + +type fakeChainResolver struct { + client common.ChainClient +} + +func (f *fakeChainResolver) GetClient(chainID string) (common.ChainClient, error) { + if f.client == nil { + return nil, fmt.Errorf("no client for %s", chainID) + } + return f.client, nil +} + +func testReadRequest() *uread.ReadRequest { + return &uread.ReadRequest{ + RequestID: "0xabc123", + DestinationChain: "eip155:11155111", + Query: []byte{0x01}, + MinConfirmations: 1, + DestinationBlockHeight: 100, + CreatedAtHeight: 7, + } +} + +func newTestReadEventProcessor(t *testing.T, voter readVoter, destClient common.ChainClient) (*ReadEventProcessor, *common.ChainStore) { + t.Helper() + database := newTestDB(t) + p, err := NewReadEventProcessor(voter, &fakeChainResolver{client: destClient}, database, zerolog.Nop()) + require.NoError(t, err) + return p, common.NewChainStore(database) +} + +func seedReadRequest(t *testing.T, cs *common.ChainStore, req *uread.ReadRequest) *store.Event { + t.Helper() + event, err := convertReadRequestEvent(req) + require.NoError(t, err) + stored, err := cs.InsertEventIfNotExists(event) + require.NoError(t, err) + require.True(t, stored) + return event +} + +func assertStatus(t *testing.T, cs *common.ChainStore, eventID, status string) { + t.Helper() + rows, err := cs.UpdateEventStatus(eventID, status, status) + require.NoError(t, err) + assert.Equal(t, int64(1), rows, "event %s not in status %s", eventID, status) +} + +func TestReadEventProcessor_SuccessFlow(t *testing.T) { + req := testReadRequest() + result := &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: []byte{0xaa}, + ObservedBlockHeight: 100, + } + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: result}) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + require.Contains(t, voter.votes, req.RequestID) + assert.Equal(t, result, voter.votes[req.RequestID]) + assertStatus(t, cs, event.EventID, store.StatusCompleted) +} + +func TestReadEventProcessor_VoteFailureKeepsConfirmed(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{err: fmt.Errorf("MsgVoteReadResult not available")} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assertStatus(t, cs, event.EventID, store.StatusConfirmed) +} + +func TestReadEventProcessor_ExecutionFailureRetries(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{err: fmt.Errorf("rpc down")}) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusConfirmed) +} + +func TestReadEventProcessor_UnservedChainRetries(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, nil) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusConfirmed) +} + +func TestReadEventProcessor_HandlerUnavailableRetries(t *testing.T) { + req := testReadRequest() + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{notStarted: true}) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusConfirmed) +} + +func TestReadEventProcessor_CorruptEventReverted(t *testing.T) { + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + + event := &store.Event{ + EventID: "corrupt-read", + Type: store.EventTypeReadRequest, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: []byte("not json"), + } + stored, err := cs.InsertEventIfNotExists(event) + require.NoError(t, err) + require.True(t, stored) + + require.Error(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusReverted) +} + +func TestReadEventProcessor_ExpiredMarkedReverted(t *testing.T) { + req := testReadRequest() + req.ExpiryBlockHeight = 50 + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + require.NoError(t, cs.UpdateChainHeight(100)) // push chain past expiry + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusReverted) +} + +func TestReadEventProcessor_NotExpiredProcessesNormally(t *testing.T) { + req := testReadRequest() + req.ExpiryBlockHeight = 200 + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + require.NoError(t, cs.UpdateChainHeight(100)) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + require.Contains(t, voter.votes, req.RequestID) + assertStatus(t, cs, event.EventID, store.StatusCompleted) +} diff --git a/universalClient/pushwatcher/read_processor_test.go b/universalClient/pushwatcher/read_processor_test.go deleted file mode 100644 index 784f0c1c..00000000 --- a/universalClient/pushwatcher/read_processor_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package pushwatcher - -import ( - "context" - "fmt" - "testing" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/pushchain/push-chain-node/universalClient/externalchains/common" - "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" -) - -type fakeReadVoter struct { - votes map[string]*uread.ReadResult - txHash string - err error -} - -func (f *fakeReadVoter) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { - if f.err != nil { - return "", f.err - } - if f.votes == nil { - f.votes = make(map[string]*uread.ReadResult) - } - f.votes[requestID] = result - return f.txHash, nil -} - -type fakeDestClient struct { - result *uread.ReadResult - err error -} - -func (f *fakeDestClient) Start(ctx context.Context) error { return nil } -func (f *fakeDestClient) Stop() error { return nil } -func (f *fakeDestClient) IsHealthy() bool { return true } -func (f *fakeDestClient) GetTxBuilder() (common.TxBuilder, error) { - return nil, fmt.Errorf("not supported") -} -func (f *fakeDestClient) GetReadRequestHandler() (common.ReadRequestHandler, error) { - return f, nil -} -func (f *fakeDestClient) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { - return f.result, f.err -} - -type fakeChainResolver struct { - client common.ChainClient -} - -func (f *fakeChainResolver) GetClient(chainID string) (common.ChainClient, error) { - if f.client == nil { - return nil, fmt.Errorf("no client for %s", chainID) - } - return f.client, nil -} - -func testReadRequest() *uread.ReadRequest { - return &uread.ReadRequest{ - RequestID: "0xabc123", - DestinationChain: "eip155:11155111", - Query: []byte{0x01}, - MinConfirmations: 1, - DestinationBlockHeight: 100, - CreatedAtHeight: 7, - } -} - -func newTestReadProcessor(t *testing.T, voter readVoter, destClient common.ChainClient) (*ReadProcessor, *common.ChainStore) { - t.Helper() - database := newTestDB(t) - p, err := NewReadProcessor(voter, &fakeChainResolver{client: destClient}, database, 0, zerolog.Nop()) - require.NoError(t, err) - return p, common.NewChainStore(database) -} - -func seedReadRequest(t *testing.T, cs *common.ChainStore, req *uread.ReadRequest) string { - t.Helper() - event, err := convertReadRequestEvent(req) - require.NoError(t, err) - stored, err := cs.InsertEventIfNotExists(event) - require.NoError(t, err) - require.True(t, stored) - return event.EventID -} - -func eventStatus(t *testing.T, cs *common.ChainStore, eventID string) string { - t.Helper() - events, err := cs.GetConfirmedEvents(100) - require.NoError(t, err) - for i := range events { - if events[i].EventID == eventID { - return events[i].Status - } - } - // not CONFIRMED anymore; caller asserts via CAS probes - return "" -} - -func TestReadProcessor_SuccessFlow(t *testing.T) { - req := testReadRequest() - result := &uread.ReadResult{ - Status: uread.ReadStatusSuccess, - ResultData: []byte{0xaa}, - ObservedBlockHeight: 100, - } - voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: result}) - eventID := seedReadRequest(t, cs, req) - - p.processConfirmedReads(context.Background()) - - require.Contains(t, voter.votes, req.RequestID) - assert.Equal(t, result, voter.votes[req.RequestID]) - - // event flipped to COMPLETED with vote tx hash - rows, err := cs.UpdateEventStatus(eventID, store.StatusCompleted, store.StatusCompleted) - require.NoError(t, err) - assert.Equal(t, int64(1), rows) - - // second tick must not re-vote - voter.votes = nil - p.processConfirmedReads(context.Background()) - assert.Empty(t, voter.votes) -} - -func TestReadProcessor_VoteFailureKeepsConfirmed(t *testing.T) { - req := testReadRequest() - voter := &fakeReadVoter{err: fmt.Errorf("MsgVoteReadResult not available")} - p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) - eventID := seedReadRequest(t, cs, req) - - p.processConfirmedReads(context.Background()) - - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestReadProcessor_ExecutionFailureRetries(t *testing.T) { - req := testReadRequest() - voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadProcessor(t, voter, &fakeDestClient{err: fmt.Errorf("rpc down")}) - eventID := seedReadRequest(t, cs, req) - - p.processConfirmedReads(context.Background()) - - assert.Empty(t, voter.votes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestReadProcessor_UnservedChainRetries(t *testing.T) { - req := testReadRequest() - voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadProcessor(t, voter, nil) - eventID := seedReadRequest(t, cs, req) - - p.processConfirmedReads(context.Background()) - - assert.Empty(t, voter.votes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, eventID)) -} - -func TestReadProcessor_CorruptEventReverted(t *testing.T) { - voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) - - stored, err := cs.InsertEventIfNotExists(&store.Event{ - EventID: "corrupt-read", - Type: store.EventTypeReadRequest, - ConfirmationType: store.ConfirmationInstant, - Status: store.StatusConfirmed, - EventData: []byte("not json"), - }) - require.NoError(t, err) - require.True(t, stored) - - p.processConfirmedReads(context.Background()) - - assert.Empty(t, voter.votes) - rows, err := cs.UpdateEventStatus("corrupt-read", store.StatusReverted, store.StatusReverted) - require.NoError(t, err) - assert.Equal(t, int64(1), rows) -} - -func TestReadProcessor_IgnoresOtherEventTypes(t *testing.T) { - voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) - - stored, err := cs.InsertEventIfNotExists(&store.Event{ - EventID: "tss-event", - Type: store.EventTypeKeygen, - ConfirmationType: store.ConfirmationInstant, - Status: store.StatusConfirmed, - EventData: []byte("{}"), - }) - require.NoError(t, err) - require.True(t, stored) - - p.processConfirmedReads(context.Background()) - - assert.Empty(t, voter.votes) - assert.Equal(t, store.StatusConfirmed, eventStatus(t, cs, "tss-event")) -} - -func TestReadProcessor_StartStop(t *testing.T) { - p, _ := newTestReadProcessor(t, &fakeReadVoter{}, nil) - - require.NoError(t, p.Start(context.Background())) - assert.Equal(t, ErrAlreadyRunning, p.Start(context.Background())) - require.NoError(t, p.Stop()) - assert.Equal(t, ErrNotRunning, p.Stop()) -} From 03ebd48989d5fbce6d091fa076f6ad309c859641 Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 17:37:38 +0530 Subject: [PATCH 17/54] refactor: naming changes --- universalClient/core/client.go | 21 +++++++++---------- .../externalchains/common/chain_store.go | 1 - 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/universalClient/core/client.go b/universalClient/core/client.go index f65808f9..cf1cdb93 100644 --- a/universalClient/core/client.go +++ b/universalClient/core/client.go @@ -31,7 +31,7 @@ type UniversalClient struct { pushCore *pushcore.Client pushSigner *pushsigner.Signer chains *externalchains.Chains - pushChain *pushwatcher.Client + pushWatcher *pushwatcher.Client tssNode *tss.Node } @@ -70,14 +70,13 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie chainsManager := externalchains.NewChains(pushCore, pushSigner, cfg, log) - // Push chain DB is shared by the push chain client and the TSS node. + // Push chain DB is shared by the push watcher and the TSS node. pushDB, err := openPushDB(cfg) if err != nil { return nil, err } - // chainsManager resolves destination chains for read request execution. - pushChain, err := pushwatcher.NewClient( + pushWatcher, err := pushwatcher.NewClient( pushDB, cfg.GetChainConfig(cfg.PushChainID), pushCore, @@ -87,7 +86,7 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie chainsManager, ) if err != nil { - return nil, fmt.Errorf("failed to create push chain client: %w", err) + return nil, fmt.Errorf("failed to create push watcher: %w", err) } tssNode, err := initTSS(ctx, cfg, pushCore, chainsManager, pushSigner, pushDB, log) @@ -105,7 +104,7 @@ func NewUniversalClient(ctx context.Context, cfg *config.Config) (*UniversalClie pushCore: pushCore, pushSigner: pushSigner, chains: chainsManager, - pushChain: pushChain, + pushWatcher: pushWatcher, tssNode: tssNode, }, nil } @@ -133,8 +132,8 @@ func (uc *UniversalClient) Start() error { return fmt.Errorf("failed to start chains manager: %w", err) } - if err := uc.pushChain.Start(uc.ctx); err != nil { - return fmt.Errorf("failed to start push chain client: %w", err) + if err := uc.pushWatcher.Start(uc.ctx); err != nil { + return fmt.Errorf("failed to start push watcher: %w", err) } if uc.tssNode != nil { @@ -169,9 +168,9 @@ func (uc *UniversalClient) shutdown() { } } - if uc.pushChain != nil { - if err := uc.pushChain.Stop(); err != nil { - uc.log.Error().Err(err).Str("subsystem", "push_chain").Msg("subsystem failed to stop") + if uc.pushWatcher != nil { + if err := uc.pushWatcher.Stop(); err != nil { + uc.log.Error().Err(err).Str("subsystem", "push_watcher").Msg("subsystem failed to stop") } } diff --git a/universalClient/externalchains/common/chain_store.go b/universalClient/externalchains/common/chain_store.go index a7005741..b67bd1f8 100644 --- a/universalClient/externalchains/common/chain_store.go +++ b/universalClient/externalchains/common/chain_store.go @@ -22,7 +22,6 @@ func NewChainStore(database *db.DB) *ChainStore { } } - // GetChainHeight returns the last processed block height for the chain. // Creates a new entry with height 0 if one doesn't exist (atomic via FirstOrCreate). func (cs *ChainStore) GetChainHeight() (uint64, error) { From 5958d430f4652173b77a96257e3c75754cf572b4 Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 17:48:17 +0530 Subject: [PATCH 18/54] refactor: remove unused fn --- .../externalchains/common/chain_store.go | 18 --------------- .../externalchains/common/chain_store_test.go | 22 ------------------- 2 files changed, 40 deletions(-) diff --git a/universalClient/externalchains/common/chain_store.go b/universalClient/externalchains/common/chain_store.go index b67bd1f8..b711087c 100644 --- a/universalClient/externalchains/common/chain_store.go +++ b/universalClient/externalchains/common/chain_store.go @@ -154,24 +154,6 @@ func (cs *ChainStore) UpdateStatusAndEventData(eventID, oldStatus, newStatus str return res.RowsAffected, nil } -// UpdateVoteTxHash updates the vote_tx_hash field for an event -func (cs *ChainStore) UpdateVoteTxHash(eventID string, voteTxHash string) error { - if cs.database == nil { - return fmt.Errorf("database is nil") - } - - result := cs.database.Client(). - Model(&store.Event{}). - Where("event_id = ?", eventID). - Update("vote_tx_hash", voteTxHash) - - if result.Error != nil { - return fmt.Errorf("failed to update vote_tx_hash: %w", result.Error) - } - - return nil -} - // DeleteTerminalEvents deletes events in terminal states (COMPLETED, REVERTED, EXPIRED) // that were updated before the given time func (cs *ChainStore) DeleteTerminalEvents(updatedBefore any) (int64, error) { diff --git a/universalClient/externalchains/common/chain_store_test.go b/universalClient/externalchains/common/chain_store_test.go index a3b80989..461b2608 100644 --- a/universalClient/externalchains/common/chain_store_test.go +++ b/universalClient/externalchains/common/chain_store_test.go @@ -56,11 +56,6 @@ func TestChainStoreNilDatabase(t *testing.T) { assert.Contains(t, err.Error(), "database is nil") }) - t.Run("UpdateVoteTxHash returns error for nil database", func(t *testing.T) { - err := store.UpdateVoteTxHash("event-1", "0x123") - require.Error(t, err) - assert.Contains(t, err.Error(), "database is nil") - }) t.Run("InsertEventIfNotExists returns error for nil database", func(t *testing.T) { inserted, err := store.InsertEventIfNotExists(nil) @@ -222,23 +217,6 @@ func TestChainStore_UpdateStatusAndEventData(t *testing.T) { assert.Equal(t, int64(1), rows) } -func TestChainStore_UpdateVoteTxHash(t *testing.T) { - cs := newTestChainStore(t) - - event := &storemodels.Event{ - EventID: "evt-5", - BlockHeight: 50, - Type: storemodels.EventTypeOutbound, - ConfirmationType: storemodels.ConfirmationStandard, - Status: storemodels.StatusConfirmed, - } - _, err := cs.InsertEventIfNotExists(event) - require.NoError(t, err) - - err = cs.UpdateVoteTxHash("evt-5", "0xvotehash") - require.NoError(t, err) -} - func TestChainStore_GetPendingEventsLimit(t *testing.T) { cs := newTestChainStore(t) From 9b3cbf94946f6a6a57590a8ac7a1da9369517997 Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 18:37:00 +0530 Subject: [PATCH 19/54] refactor: observation event processor --- .../externalchains/common/event_processor.go | 299 +---- .../common/event_processor_test.go | 1090 +++-------------- .../inbound_observation_event_processor.go | 118 ++ ...nbound_observation_event_processor_test.go | 214 ++++ .../outbound_observation_event_processor.go | 105 ++ ...tbound_observation_event_processor_test.go | 191 +++ universalClient/externalchains/evm/client.go | 20 +- universalClient/externalchains/svm/client.go | 20 +- 8 files changed, 864 insertions(+), 1193 deletions(-) create mode 100644 universalClient/externalchains/common/inbound_observation_event_processor.go create mode 100644 universalClient/externalchains/common/inbound_observation_event_processor_test.go create mode 100644 universalClient/externalchains/common/outbound_observation_event_processor.go create mode 100644 universalClient/externalchains/common/outbound_observation_event_processor_test.go diff --git a/universalClient/externalchains/common/event_processor.go b/universalClient/externalchains/common/event_processor.go index c49fcb3d..280ccf40 100644 --- a/universalClient/externalchains/common/event_processor.go +++ b/universalClient/externalchains/common/event_processor.go @@ -3,9 +3,7 @@ package common import ( "context" "encoding/hex" - "encoding/json" "fmt" - "strconv" "strings" "sync" "time" @@ -17,46 +15,55 @@ import ( "github.com/rs/zerolog" ) -// VoteSigner is the subset of pushsigner.Signer used by EventProcessor. +const eventProcessBatchSize = 1000 + +// VoteSigner is the subset of pushsigner.Signer used by the event processors. // Defined here (consumer-side) so tests can provide mock implementations. type VoteSigner interface { VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) } -// EventProcessor processes events from the chain's database and votes on them +// EventHandler processes one CONFIRMED event of a registered type. +// Handlers own the event's status transitions; a returned error is logged and +// the event is retried next tick. +type EventHandler interface { + HandleEvent(ctx context.Context, event *store.Event) error +} + +// EventProcessor drains CONFIRMED events from the chain's database and +// dispatches them to the handler registered for their type. Event types +// without a handler are ignored. type EventProcessor struct { - signer VoteSigner - chainStore *ChainStore - logger zerolog.Logger - chainID string - inboundEnabled bool - outboundEnabled bool - running bool - stopCh chan struct{} - wg sync.WaitGroup + chainStore *ChainStore + handlers map[string]EventHandler + chainID string + logger zerolog.Logger + running bool + stopCh chan struct{} + wg sync.WaitGroup } -// NewEventProcessor creates a new event processor +// NewEventProcessor creates a new event processor. Register handlers before Start. func NewEventProcessor( - signer VoteSigner, database *db.DB, chainID string, - inboundEnabled bool, - outboundEnabled bool, logger zerolog.Logger, ) *EventProcessor { return &EventProcessor{ - signer: signer, - chainStore: NewChainStore(database), - chainID: chainID, - inboundEnabled: inboundEnabled, - outboundEnabled: outboundEnabled, - logger: logger.With().Str("component", "event_processor").Str("chain", chainID).Logger(), - stopCh: make(chan struct{}), + chainStore: NewChainStore(database), + handlers: make(map[string]EventHandler), + chainID: chainID, + logger: logger.With().Str("component", "event_processor").Str("chain", chainID).Logger(), + stopCh: make(chan struct{}), } } +// RegisterHandler registers a handler for an event type. Must be called before Start. +func (ep *EventProcessor) RegisterHandler(eventType string, handler EventHandler) { + ep.handlers[eventType] = handler +} + // Start begins processing events func (ep *EventProcessor) Start(ctx context.Context) error { if ep.running { @@ -109,7 +116,6 @@ func (ep *EventProcessor) processLoop(ctx context.Context) { ep.logger.Debug().Msg("stop signal received, stopping event processor") return case <-ticker.C: - // Fetch 1000 CONFIRMED events and process them if err := ep.processConfirmedEvents(ctx); err != nil { ep.logger.Error().Err(err).Msg("failed to process confirmed events") } @@ -117,102 +123,34 @@ func (ep *EventProcessor) processLoop(ctx context.Context) { } } -// processConfirmedEvents processes confirmed events (both inbound and outbound) +// processConfirmedEvents dispatches CONFIRMED events to their registered handlers. func (ep *EventProcessor) processConfirmedEvents(ctx context.Context) error { - events, err := ep.chainStore.GetConfirmedEvents(1000) + events, err := ep.chainStore.GetConfirmedEvents(eventProcessBatchSize) if err != nil { return fmt.Errorf("failed to get confirmed events: %w", err) } for _, event := range events { - if event.Type == store.EventTypeInbound { - if !ep.inboundEnabled { - ep.logger.Warn().Str("event_id", event.EventID).Msg("inbound disabled, skipping inbound event processing") - continue - } - if err := ep.processInboundEvent(ctx, &event); err != nil { - ep.logger.Error(). - Err(err). - Str("event_id", event.EventID). - Msg("failed to vote on inbound event") - continue - } - } else if event.Type == store.EventTypeOutbound { - if !ep.outboundEnabled { - ep.logger.Warn().Str("event_id", event.EventID).Msg("outbound disabled, skipping outbound event processing") - continue - } - if err := ep.processOutboundEvent(ctx, &event); err != nil { - ep.logger.Error(). - Err(err). - Str("event_id", event.EventID). - Msg("failed to vote on outbound event") - continue - } + handler, ok := ep.handlers[event.Type] + if !ok { + continue } - } - return nil -} - -// processOutboundEvent processes an outbound event by voting on it -func (ep *EventProcessor) processOutboundEvent(ctx context.Context, event *store.Event) error { - ep.logger.Debug(). - Str("event_id", event.EventID). - Msg("processing outbound event") - - // Parse outbound event data once - outboundData, err := ep.parseOutboundEventData(event) - if err != nil { - return fmt.Errorf("failed to parse outbound event data: %w", err) - } - - txID := outboundData.TxID - utxID := outboundData.UniversalTxID - - // Build observation from parsed data - observation, err := ep.buildOutboundObservation(event, outboundData) - if err != nil { - return fmt.Errorf("failed to build outbound observation: %w", err) - } - - // Vote on outbound - voteTxHash, err := ep.signer.VoteOutbound(ctx, txID, utxID, observation) - if err != nil { - return fmt.Errorf("failed to vote on outbound: %w", err) - } - - return ep.markCompleted(event, voteTxHash) -} - -// processInboundEvent processes an inbound event by voting on it and confirming it -func (ep *EventProcessor) processInboundEvent(ctx context.Context, event *store.Event) error { - ep.logger.Debug(). - Str("event_id", event.EventID). - Msg("processing inbound event") - - // Extract inbound data from event - inbound, err := ep.constructInbound(event) - if err != nil { - return fmt.Errorf("failed to construct inbound: %w", err) - } - - // Execute vote on blockchain - voteTxHash, err := ep.signer.VoteInbound(ctx, inbound) - if err != nil { - ep.logger.Error(). - Str("event_id", event.EventID). - Err(err). - Msg("failed to vote on event - keeping status for retry") - return err + if err := handler.HandleEvent(ctx, &event); err != nil { + ep.logger.Error(). + Err(err). + Str("event_id", event.EventID). + Str("type", event.Type). + Msg("failed to process event") + } } - return ep.markCompleted(event, voteTxHash) + return nil } -// markCompleted atomically records the vote hash and flips CONFIRMED -> COMPLETED. -func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) error { - rowsAffected, err := ep.chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) +// markEventCompleted atomically records the vote hash and flips CONFIRMED -> COMPLETED. +func markEventCompleted(chainStore *ChainStore, logger zerolog.Logger, event *store.Event, voteTxHash string) error { + rowsAffected, err := chainStore.UpdateStatusAndVoteTxHash(event.EventID, store.StatusConfirmed, store.StatusCompleted, voteTxHash) if err != nil { return fmt.Errorf("failed to update event status after successful vote: %w", err) } @@ -221,7 +159,7 @@ func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) e return nil // already completed } - ep.logger.Info(). + logger.Info(). Str("event_id", event.EventID). Str("type", event.Type). Str("vote_tx_hash", voteTxHash). @@ -230,86 +168,25 @@ func (ep *EventProcessor) markCompleted(event *store.Event, voteTxHash string) e return nil } -// constructInbound creates an Inbound message from event data -func (ep *EventProcessor) constructInbound(event *store.Event) (*uexecutortypes.Inbound, error) { - var eventData UniversalTx - - if event == nil { - return nil, fmt.Errorf("event is nil") - } - - if event.EventData == nil { - return nil, fmt.Errorf("event data is missing for event_id: %s", event.EventID) - } - - if err := json.Unmarshal(event.EventData, &eventData); err != nil { - return nil, fmt.Errorf("failed to unmarshal event data: %w", err) - } - - // Map txType from eventData to proper enum value - txType := uexecutortypes.TxType_UNSPECIFIED_TX - switch eventData.TxType { - case 0: - txType = uexecutortypes.TxType_GAS - case 1: - txType = uexecutortypes.TxType_GAS_AND_PAYLOAD - case 2: - txType = uexecutortypes.TxType_FUNDS - case 3: - txType = uexecutortypes.TxType_FUNDS_AND_PAYLOAD - default: - txType = uexecutortypes.TxType_UNSPECIFIED_TX - } - - // Extract txHash from EventID (format: "txHash:logIndex") +// eventTxHash extracts the tx hash from an EventID (format: "txHash:logIndex" +// or "signature:logIndex"), converting base58 signatures to 0x-prefixed hex. +// Falls back to the raw value if conversion fails. +func eventTxHash(eventID string) string { txHash := "" - parts := strings.Split(event.EventID, ":") + parts := strings.Split(eventID, ":") if len(parts) > 0 { txHash = parts[0] } - // Convert txHash to hex format if it's in base58 - txHashHex, err := ep.base58ToHex(txHash) + txHashHex, err := base58ToHex(txHash) if err != nil { - ep.logger.Warn(). - Str("tx_hash", txHash). - Err(err). - Msg("failed to convert txHash to hex, using original value") - txHashHex = txHash - } - - inboundMsg := &uexecutortypes.Inbound{ - SourceChain: eventData.SourceChain, - TxHash: txHashHex, - Sender: eventData.Sender, - Recipient: eventData.Recipient, - Amount: eventData.Amount, - AssetAddr: eventData.Token, - LogIndex: strconv.FormatUint(uint64(eventData.LogIndex), 10), - TxType: txType, - IsCEA: eventData.FromCEA, - RawPayload: eventData.RawPayload, - } - - // Set revert instructions if revert fund recipient is present - if eventData.RevertFundRecipient != "" { - inboundMsg.RevertInstructions = &uexecutortypes.RevertInstructions{ - FundRecipient: eventData.RevertFundRecipient, - } + return txHash } - - // Use event's VerificationData if present, otherwise fall back to txHash - if eventData.VerificationData == "" || eventData.VerificationData == "0x" { - inboundMsg.VerificationData = txHashHex - } else { - inboundMsg.VerificationData = eventData.VerificationData - } - - return inboundMsg, nil + return txHashHex } // base58ToHex converts a base58 encoded string to hex format (0x...) -func (ep *EventProcessor) base58ToHex(base58Str string) (string, error) { +func base58ToHex(base58Str string) (string, error) { if base58Str == "" { return "0x", nil } @@ -328,65 +205,3 @@ func (ep *EventProcessor) base58ToHex(base58Str string) (string, error) { // Convert to hex with 0x prefix return "0x" + hex.EncodeToString(decoded), nil } - -// parseOutboundEventData unmarshals event data into an OutboundEvent struct -func (ep *EventProcessor) parseOutboundEventData(event *store.Event) (*OutboundEvent, error) { - if event == nil { - return nil, fmt.Errorf("event is nil") - } - - if len(event.EventData) == 0 { - return nil, fmt.Errorf("event data is empty") - } - - var eventData OutboundEvent - if err := json.Unmarshal(event.EventData, &eventData); err != nil { - return nil, fmt.Errorf("failed to unmarshal event data: %w", err) - } - - if eventData.TxID == "" { - return nil, fmt.Errorf("tx_id not found in event data") - } - - if eventData.UniversalTxID == "" { - return nil, fmt.Errorf("universal_tx_id not found in event data") - } - - return &eventData, nil -} - -// buildOutboundObservation builds an OutboundObservation from event metadata and parsed outbound data -func (ep *EventProcessor) buildOutboundObservation(event *store.Event, outboundData *OutboundEvent) (*uexecutortypes.OutboundObservation, error) { - // Extract txHash from EventID (format: "txHash:logIndex" or "signature:logIndex") - txHash := "" - parts := strings.Split(event.EventID, ":") - if len(parts) > 0 { - txHash = parts[0] - } - - // Convert txHash to hex format if it's in base58 - txHashHex, err := ep.base58ToHex(txHash) - if err != nil { - ep.logger.Warn(). - Str("tx_hash", txHash). - Err(err). - Msg("failed to convert txHash to hex, using original value") - txHashHex = txHash - } - - gasFeeUsed := "0" - if outboundData.GasFeeUsed != "" { - gasFeeUsed = outboundData.GasFeeUsed - } - - observation := &uexecutortypes.OutboundObservation{ - Success: true, - BlockHeight: event.BlockHeight, - TxHash: txHashHex, - ErrorMsg: "", - GasFeeUsed: gasFeeUsed, - Pc20WrapperAddress: outboundData.Pc20WrapperAddress, - } - - return observation, nil -} diff --git a/universalClient/externalchains/common/event_processor_test.go b/universalClient/externalchains/common/event_processor_test.go index caf7a44a..df8a1f12 100644 --- a/universalClient/externalchains/common/event_processor_test.go +++ b/universalClient/externalchains/common/event_processor_test.go @@ -2,7 +2,7 @@ package common import ( "context" - "encoding/json" + "fmt" "math/big" "testing" "time" @@ -16,1012 +16,244 @@ import ( uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) -func TestNewEventProcessor(t *testing.T) { - t.Run("creates event processor with valid params", func(t *testing.T) { - logger := zerolog.Nop() - chainID := "eip155:1" - - processor := NewEventProcessor(nil, nil, chainID, true, true, logger) - - require.NotNil(t, processor) - assert.Equal(t, chainID, processor.chainID) - assert.False(t, processor.running) - assert.NotNil(t, processor.stopCh) - assert.NotNil(t, processor.chainStore) - }) +type fakeVoteSigner struct { + inboundVotes int + outboundVotes int + txHash string + err error } -func TestEventProcessorIsRunning(t *testing.T) { - t.Run("returns false when not running", func(t *testing.T) { - processor := &EventProcessor{running: false} - assert.False(t, processor.IsRunning()) - }) - - t.Run("returns true when running", func(t *testing.T) { - processor := &EventProcessor{running: true} - assert.True(t, processor.IsRunning()) - }) +func (f *fakeVoteSigner) VoteInbound(ctx context.Context, inbound *uexecutortypes.Inbound) (string, error) { + if f.err != nil { + return "", f.err + } + f.inboundVotes++ + return f.txHash, nil } -func TestEventProcessorStop(t *testing.T) { - t.Run("stop when not running returns nil", func(t *testing.T) { - processor := &EventProcessor{running: false} - err := processor.Stop() - assert.NoError(t, err) - }) +func (f *fakeVoteSigner) VoteOutbound(ctx context.Context, txID string, utxID string, observation *uexecutortypes.OutboundObservation) (string, error) { + if f.err != nil { + return "", f.err + } + f.outboundVotes++ + return f.txHash, nil } -func TestEventProcessorBase58ToHex(t *testing.T) { - logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "test-chain", true, true, logger) - - t.Run("empty string returns 0x", func(t *testing.T) { - result, err := processor.base58ToHex("") - require.NoError(t, err) - assert.Equal(t, "0x", result) - }) - - t.Run("already hex returns as is", func(t *testing.T) { - input := "0xabcdef1234567890" - result, err := processor.base58ToHex(input) - require.NoError(t, err) - assert.Equal(t, input, result) - }) - - t.Run("valid base58 converts to hex", func(t *testing.T) { - // "3yZe7d" is base58 for bytes [1, 2, 3, 4] - input := "2VfUX" - result, err := processor.base58ToHex(input) - require.NoError(t, err) - assert.True(t, len(result) > 2) - assert.Equal(t, "0x", result[:2]) - }) - - t.Run("invalid base58 returns error", func(t *testing.T) { - // Base58 doesn't include 0, O, I, l - input := "0OIl" - _, err := processor.base58ToHex(input) - require.Error(t, err) - }) +type fakeEventHandler struct { + handled []string + err error } -func TestEventProcessorConstructInbound(t *testing.T) { - logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) - - t.Run("nil event returns error", func(t *testing.T) { - inbound, err := processor.constructInbound(nil) - require.Error(t, err) - assert.Nil(t, inbound) - assert.Contains(t, err.Error(), "event is nil") - }) - - t.Run("nil event data returns error", func(t *testing.T) { - event := &store.Event{ - EventID: "0x123:0", - EventData: nil, - } - inbound, err := processor.constructInbound(event) - require.Error(t, err) - assert.Nil(t, inbound) - assert.Contains(t, err.Error(), "event data is missing") - }) - - t.Run("invalid JSON returns error", func(t *testing.T) { - event := &store.Event{ - EventID: "0x123:0", - EventData: []byte("invalid json"), - } - inbound, err := processor.constructInbound(event) - require.Error(t, err) - assert.Nil(t, inbound) - }) - - t.Run("valid event data constructs inbound", func(t *testing.T) { - eventData := UniversalTx{ - SourceChain: "eip155:1", - LogIndex: 5, - Sender: "0xsender123", - Recipient: "push1recipient", - Token: "0xtoken", - Amount: "1000000", - TxType: 2, // FUNDS - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "0xabc123:5", - EventData: eventDataBytes, - } - - inbound, err := processor.constructInbound(event) - require.NoError(t, err) - require.NotNil(t, inbound) - assert.Equal(t, "eip155:1", inbound.SourceChain) - assert.Equal(t, "0xsender123", inbound.Sender) - assert.Equal(t, "1000000", inbound.Amount) - assert.Equal(t, uexecutortypes.TxType_FUNDS, inbound.TxType) - }) - - t.Run("passes all fields unconditionally to inbound", func(t *testing.T) { - eventData := UniversalTx{ - SourceChain: "eip155:1", - LogIndex: 3, - Sender: "0xsender", - Recipient: "0xrecipient", - Token: "0xtoken", - Amount: "500", - RawPayload: "0xdeadbeef", - VerificationData: "0xsigdata", - RevertFundRecipient: "0xrevert", - TxType: 3, // FUNDS_AND_PAYLOAD - FromCEA: true, - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "0xtxhash:3", - EventData: eventDataBytes, - } - - inbound, err := processor.constructInbound(event) - require.NoError(t, err) - require.NotNil(t, inbound) - assert.Equal(t, "0xrecipient", inbound.Recipient) - assert.Equal(t, "0xdeadbeef", inbound.RawPayload) - assert.Equal(t, "0xsigdata", inbound.VerificationData) - assert.True(t, inbound.IsCEA) - require.NotNil(t, inbound.RevertInstructions) - assert.Equal(t, "0xrevert", inbound.RevertInstructions.FundRecipient) - }) - - t.Run("passes raw payload and verification data for non-payload tx types", func(t *testing.T) { - // Core will strip these — UV just passes everything through - eventData := UniversalTx{ - SourceChain: "eip155:1", - Sender: "0xsender", - Recipient: "0xrecipient", - Amount: "1000", - RawPayload: "0xcafe", - VerificationData: "0xsig", - TxType: 2, // FUNDS (non-payload type) - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "0xhash:0", - EventData: eventDataBytes, - } - - inbound, err := processor.constructInbound(event) - require.NoError(t, err) - assert.Equal(t, "0xrecipient", inbound.Recipient) - assert.Equal(t, "0xcafe", inbound.RawPayload) - assert.Equal(t, "0xsig", inbound.VerificationData) - }) - - t.Run("no revert instructions when revert recipient is empty", func(t *testing.T) { - eventData := UniversalTx{ - SourceChain: "eip155:1", - Sender: "0xsender", - Amount: "100", - TxType: 0, // GAS - RevertFundRecipient: "", - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "0xhash:0", - EventData: eventDataBytes, - } - - inbound, err := processor.constructInbound(event) - require.NoError(t, err) - assert.Nil(t, inbound.RevertInstructions) - }) - - t.Run("tx type mapping", func(t *testing.T) { - testCases := []struct { - txType uint - expected uexecutortypes.TxType - }{ - {0, uexecutortypes.TxType_GAS}, - {1, uexecutortypes.TxType_GAS_AND_PAYLOAD}, - {2, uexecutortypes.TxType_FUNDS}, - {3, uexecutortypes.TxType_FUNDS_AND_PAYLOAD}, - {99, uexecutortypes.TxType_UNSPECIFIED_TX}, // Unknown defaults to unspecified - } - - for _, tc := range testCases { - eventData := UniversalTx{ - SourceChain: "eip155:1", - TxType: tc.txType, - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "0xabc:0", - EventData: eventDataBytes, - } - - inbound, err := processor.constructInbound(event) - require.NoError(t, err) - assert.Equal(t, tc.expected, inbound.TxType, "TxType %d should map to %v", tc.txType, tc.expected) - } - }) +func (f *fakeEventHandler) HandleEvent(ctx context.Context, event *store.Event) error { + f.handled = append(f.handled, event.EventID) + return f.err } -func TestEventProcessorParseOutboundEventData(t *testing.T) { - logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) - - t.Run("nil event returns error", func(t *testing.T) { - data, err := processor.parseOutboundEventData(nil) - require.Error(t, err) - assert.Nil(t, data) - assert.Contains(t, err.Error(), "event is nil") - }) - - t.Run("empty event data returns error", func(t *testing.T) { - event := &store.Event{ - EventID: "test", - EventData: []byte{}, - } - data, err := processor.parseOutboundEventData(event) - require.Error(t, err) - assert.Nil(t, data) - assert.Contains(t, err.Error(), "event data is empty") - }) - - t.Run("valid outbound event extracts IDs and gas fee", func(t *testing.T) { - eventData := OutboundEvent{ - TxID: "0x1234", - UniversalTxID: "0xabcd", - GasFeeUsed: "42000000000000", - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "test", - EventData: eventDataBytes, - } - - data, err := processor.parseOutboundEventData(event) - require.NoError(t, err) - assert.Equal(t, "0x1234", data.TxID) - assert.Equal(t, "0xabcd", data.UniversalTxID) - assert.Equal(t, "42000000000000", data.GasFeeUsed) - }) - - t.Run("missing tx_id returns error", func(t *testing.T) { - eventData := OutboundEvent{ - TxID: "", - UniversalTxID: "0xabcd", - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "test", - EventData: eventDataBytes, - } - - data, err := processor.parseOutboundEventData(event) - require.Error(t, err) - assert.Nil(t, data) - assert.Contains(t, err.Error(), "tx_id not found") - }) - - t.Run("missing universal_tx_id returns error", func(t *testing.T) { - eventData := OutboundEvent{ - TxID: "0x1234", - UniversalTxID: "", - } - eventDataBytes, _ := json.Marshal(eventData) - - event := &store.Event{ - EventID: "test", - EventData: eventDataBytes, - } - - data, err := processor.parseOutboundEventData(event) - require.Error(t, err) - assert.Nil(t, data) - assert.Contains(t, err.Error(), "universal_tx_id not found") - }) +func newTestDB(t *testing.T) *ucdb.DB { + t.Helper() + database, err := ucdb.OpenInMemoryDB(true) + require.NoError(t, err) + t.Cleanup(func() { _ = database.Close() }) + return database } -func TestEventProcessorBuildOutboundObservation(t *testing.T) { - logger := zerolog.Nop() - processor := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) - - t.Run("builds observation with gas fee from parsed data", func(t *testing.T) { - outboundData := &OutboundEvent{ - TxID: "0x1234", - UniversalTxID: "0xabcd", - GasFeeUsed: "42000000000000", - } - - event := &store.Event{ - EventID: "0xabc123:5", - BlockHeight: 12345, - } - - obs, err := processor.buildOutboundObservation(event, outboundData) - require.NoError(t, err) - require.NotNil(t, obs) - assert.True(t, obs.Success) - assert.Equal(t, uint64(12345), obs.BlockHeight) - assert.Equal(t, "0xabc123", obs.TxHash) - assert.Equal(t, "42000000000000", obs.GasFeeUsed) - }) - - t.Run("missing gas fee defaults to 0", func(t *testing.T) { - outboundData := &OutboundEvent{ - TxID: "0x1234", - UniversalTxID: "0xabcd", - } - - event := &store.Event{ - EventID: "0xabc123:5", - BlockHeight: 12345, - } - - obs, err := processor.buildOutboundObservation(event, outboundData) - require.NoError(t, err) - require.NotNil(t, obs) - assert.Equal(t, "0", obs.GasFeeUsed) - }) - - t.Run("handles base58 tx hash", func(t *testing.T) { - outboundData := &OutboundEvent{ - TxID: "0x1234", - UniversalTxID: "0xabcd", - } - - event := &store.Event{ - EventID: "2VfUX:0", // Base58 encoded - BlockHeight: 100, - } - - obs, err := processor.buildOutboundObservation(event, outboundData) - require.NoError(t, err) - require.NotNil(t, obs) - assert.True(t, len(obs.TxHash) >= 2) +func seedConfirmedEvent(t *testing.T, database *ucdb.DB, eventID, eventType string, eventData []byte) { + t.Helper() + result := database.Client().Create(&store.Event{ + EventID: eventID, + Type: eventType, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusConfirmed, + EventData: eventData, }) + require.NoError(t, result.Error) } -func TestProcessOutboundEvent(t *testing.T) { - logger := zerolog.Nop() - ctx := context.Background() - - setupDB := func(t *testing.T) *ucdb.DB { - t.Helper() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - return database - } - - t.Run("nil event data returns parse error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - event := &store.Event{ - EventID: "0xabc:0", - EventData: nil, - } - err := ep.processOutboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to parse outbound event data") - }) - - t.Run("empty event data returns parse error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - event := &store.Event{ - EventID: "0xabc:0", - EventData: []byte{}, - } - err := ep.processOutboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to parse outbound event data") - }) - - t.Run("invalid JSON event data returns parse error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - event := &store.Event{ - EventID: "0xabc:0", - EventData: []byte("not json"), - } - err := ep.processOutboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to parse outbound event data") - }) - - t.Run("missing tx_id returns parse error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - eventData, _ := json.Marshal(OutboundEvent{ - TxID: "", - UniversalTxID: "0xutxid", - }) - event := &store.Event{ - EventID: "0xabc:0", - EventData: eventData, - } - err := ep.processOutboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to parse outbound event data") - }) - - t.Run("missing universal_tx_id returns parse error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - eventData, _ := json.Marshal(OutboundEvent{ - TxID: "0xtxid", - UniversalTxID: "", - }) - event := &store.Event{ - EventID: "0xabc:0", - EventData: eventData, - } - err := ep.processOutboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to parse outbound event data") - }) +func TestNewEventProcessor(t *testing.T) { + processor := NewEventProcessor(nil, "eip155:1", zerolog.Nop()) + + require.NotNil(t, processor) + assert.Equal(t, "eip155:1", processor.chainID) + assert.False(t, processor.running) + assert.NotNil(t, processor.stopCh) + assert.NotNil(t, processor.chainStore) + assert.Empty(t, processor.handlers) } -func TestProcessInboundEvent(t *testing.T) { - logger := zerolog.Nop() - ctx := context.Background() +func TestEventProcessor_DispatchesByType(t *testing.T) { + database := newTestDB(t) + ep := NewEventProcessor(database, "eip155:1", zerolog.Nop()) - setupDB := func(t *testing.T) *ucdb.DB { - t.Helper() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - return database - } + inboundHandler := &fakeEventHandler{} + ep.RegisterHandler(store.EventTypeInbound, inboundHandler) - t.Run("nil event data returns construct error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + seedConfirmedEvent(t, database, "0xin:0", store.EventTypeInbound, []byte("{}")) + seedConfirmedEvent(t, database, "0xout:0", store.EventTypeOutbound, []byte("{}")) // no handler registered - event := &store.Event{ - EventID: "0xabc:0", - EventData: nil, - } - err := ep.processInboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to construct inbound") - }) + require.NoError(t, ep.processConfirmedEvents(context.Background())) - t.Run("invalid JSON event data returns construct error", func(t *testing.T) { - database := setupDB(t) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - event := &store.Event{ - EventID: "0xabc:0", - EventData: []byte("{not valid json}"), - } - err := ep.processInboundEvent(ctx, event) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to construct inbound") - }) + assert.Equal(t, []string{"0xin:0"}, inboundHandler.handled) } -func TestProcessConfirmedEventsRouting(t *testing.T) { - logger := zerolog.Nop() - ctx := context.Background() - - setupDB := func(t *testing.T, events []store.Event) *ucdb.DB { - t.Helper() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - for _, e := range events { - result := database.Client().Create(&e) - require.NoError(t, result.Error) - } - return database - } - - t.Run("no confirmed events returns nil", func(t *testing.T) { - database := setupDB(t, nil) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) +func TestEventProcessor_HandlerErrorKeepsProcessing(t *testing.T) { + database := newTestDB(t) + ep := NewEventProcessor(database, "eip155:1", zerolog.Nop()) - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - }) + failing := &fakeEventHandler{err: fmt.Errorf("boom")} + ep.RegisterHandler(store.EventTypeInbound, failing) - t.Run("only pending events are ignored", func(t *testing.T) { - database := setupDB(t, []store.Event{ - { - EventID: "0xpending:0", - Status: store.StatusPending, - Type: store.EventTypeInbound, - EventData: []byte("{}"), - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) + seedConfirmedEvent(t, database, "0xin:0", store.EventTypeInbound, []byte("{}")) + seedConfirmedEvent(t, database, "0xin:1", store.EventTypeInbound, []byte("{}")) - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) + require.NoError(t, ep.processConfirmedEvents(context.Background())) - // Event should remain PENDING (not picked up) - var evt store.Event - database.Client().Where("event_id = ?", "0xpending:0").First(&evt) - assert.Equal(t, store.StatusPending, evt.Status) - }) - - t.Run("inbound with bad data fails gracefully and continues to next event", func(t *testing.T) { - database := setupDB(t, []store.Event{ - { - EventID: "0xbad_inbound:0", - Status: store.StatusConfirmed, - Type: store.EventTypeInbound, - EventData: []byte("not json"), - }, - { - EventID: "0xbad_inbound2:0", - Status: store.StatusConfirmed, - Type: store.EventTypeInbound, - EventData: nil, - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - // Should not return error - errors on individual events are logged and skipped - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - - // Both events should remain CONFIRMED (failed to process, not updated) - var evt1, evt2 store.Event - database.Client().Where("event_id = ?", "0xbad_inbound:0").First(&evt1) - assert.Equal(t, store.StatusConfirmed, evt1.Status) - database.Client().Where("event_id = ?", "0xbad_inbound2:0").First(&evt2) - assert.Equal(t, store.StatusConfirmed, evt2.Status) - }) - - t.Run("outbound with bad data fails gracefully and continues to next event", func(t *testing.T) { - database := setupDB(t, []store.Event{ - { - EventID: "0xbad_outbound:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: []byte("not json"), - }, - { - EventID: "0xbad_outbound2:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: []byte{}, - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - - // Both events should remain CONFIRMED - var evt1, evt2 store.Event - database.Client().Where("event_id = ?", "0xbad_outbound:0").First(&evt1) - assert.Equal(t, store.StatusConfirmed, evt1.Status) - database.Client().Where("event_id = ?", "0xbad_outbound2:0").First(&evt2) - assert.Equal(t, store.StatusConfirmed, evt2.Status) - }) - - t.Run("read request without reader is skipped", func(t *testing.T) { - database := setupDB(t, []store.Event{ - { - EventID: "0xread:0", - Status: store.StatusConfirmed, - Type: store.EventTypeReadRequest, - EventData: []byte("{}"), - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - - var evt store.Event - database.Client().Where("event_id = ?", "0xread:0").First(&evt) - assert.Equal(t, store.StatusConfirmed, evt.Status) - }) - - t.Run("unknown event type is silently skipped", func(t *testing.T) { - database := setupDB(t, []store.Event{ - { - EventID: "0xunknown:0", - Status: store.StatusConfirmed, - Type: "UNKNOWN_TYPE", - EventData: []byte("{}"), - }, - }) - defer database.Close() - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) - - // Event should remain CONFIRMED (no handler for this type) - var evt store.Event - database.Client().Where("event_id = ?", "0xunknown:0").First(&evt) - assert.Equal(t, store.StatusConfirmed, evt.Status) - }) -} - -func TestProcessLoopContextCancellation(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - t.Run("processLoop exits promptly on context cancel", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - - err := ep.Start(ctx) - require.NoError(t, err) - assert.True(t, ep.IsRunning()) - - // Cancel context and wait for stop - cancel() - - // The wg.Wait inside Stop() will block until processLoop exits - done := make(chan struct{}) - go func() { - ep.Stop() - close(done) - }() - - select { - case <-done: - // processLoop exited within reasonable time - case <-time.After(10 * time.Second): - t.Fatal("processLoop did not exit within 10 seconds after context cancellation") - } - - assert.False(t, ep.IsRunning()) - }) -} - -func TestProcessLoopStopChannel(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) + // both attempted despite errors, both still CONFIRMED for retry + assert.Len(t, failing.handled, 2) + events, err := NewChainStore(database).GetConfirmedEvents(10) require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - t.Run("processLoop exits promptly on stop signal", func(t *testing.T) { - ctx := context.Background() - - err := ep.Start(ctx) - require.NoError(t, err) - assert.True(t, ep.IsRunning()) - - done := make(chan struct{}) - go func() { - ep.Stop() - close(done) - }() - - select { - case <-done: - // processLoop exited promptly - case <-time.After(10 * time.Second): - t.Fatal("processLoop did not exit within 10 seconds after stop signal") - } - - assert.False(t, ep.IsRunning()) - }) + assert.Len(t, events, 2) } -func TestProcessConfirmedEventsDBError(t *testing.T) { - logger := zerolog.Nop() - ctx := context.Background() +func TestEventProcessor_PendingEventsIgnored(t *testing.T) { + database := newTestDB(t) + ep := NewEventProcessor(database, "eip155:1", zerolog.Nop()) - t.Run("nil database returns error", func(t *testing.T) { - ep := &EventProcessor{ - chainStore: NewChainStore(nil), - logger: logger, - chainID: "eip155:1", - inboundEnabled: true, - outboundEnabled: true, - } + handler := &fakeEventHandler{} + ep.RegisterHandler(store.EventTypeInbound, handler) - err := ep.processConfirmedEvents(ctx) - require.Error(t, err) - assert.Contains(t, err.Error(), "failed to get confirmed events") + result := database.Client().Create(&store.Event{ + EventID: "0xpending:0", + Type: store.EventTypeInbound, + ConfirmationType: store.ConfirmationInstant, + Status: store.StatusPending, + EventData: []byte("{}"), }) -} + require.NoError(t, result.Error) -func TestEventProcessorStruct(t *testing.T) { - t.Run("struct has expected fields", func(t *testing.T) { - ep := &EventProcessor{} - assert.Nil(t, ep.signer) - assert.Nil(t, ep.chainStore) - assert.Empty(t, ep.chainID) - assert.False(t, ep.running) - assert.Nil(t, ep.stopCh) - assert.False(t, ep.inboundEnabled) - assert.False(t, ep.outboundEnabled) - }) -} + require.NoError(t, ep.processConfirmedEvents(context.Background())) -func TestNewEventProcessorEnabledFlags(t *testing.T) { - logger := zerolog.Nop() - - t.Run("both enabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, true, logger) - assert.True(t, ep.inboundEnabled) - assert.True(t, ep.outboundEnabled) - }) - - t.Run("inbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", true, false, logger) - assert.True(t, ep.inboundEnabled) - assert.False(t, ep.outboundEnabled) - }) - - t.Run("outbound only", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, true, logger) - assert.False(t, ep.inboundEnabled) - assert.True(t, ep.outboundEnabled) - }) - - t.Run("both disabled", func(t *testing.T) { - ep := NewEventProcessor(nil, nil, "eip155:1", false, false, logger) - assert.False(t, ep.inboundEnabled) - assert.False(t, ep.outboundEnabled) - }) + assert.Empty(t, handler.handled) } -func TestEventProcessorStartDoubleStart(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() +func TestEventProcessor_NilDatabaseErrors(t *testing.T) { + ep := NewEventProcessor(nil, "eip155:1", zerolog.Nop()) + ep.RegisterHandler(store.EventTypeInbound, &fakeEventHandler{}) - // First start should succeed - err = ep.Start(ctx) - require.NoError(t, err) - assert.True(t, ep.IsRunning()) - - // Second start should be rejected - err = ep.Start(ctx) + err := ep.processConfirmedEvents(context.Background()) require.Error(t, err) - assert.Contains(t, err.Error(), "already running") - assert.True(t, ep.IsRunning()) - - // Clean up - err = ep.Stop() - require.NoError(t, err) - assert.False(t, ep.IsRunning()) + assert.Contains(t, err.Error(), "failed to get confirmed events") } -func TestEventProcessorStopIdempotent(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) +func TestEventProcessor_Lifecycle(t *testing.T) { + database := newTestDB(t) + ep := NewEventProcessor(database, "eip155:1", zerolog.Nop()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // Start the processor - err = ep.Start(ctx) - require.NoError(t, err) - assert.True(t, ep.IsRunning()) - - // First stop - err = ep.Stop() - require.NoError(t, err) - assert.False(t, ep.IsRunning()) - - // Second stop should be idempotent (no error, no panic) - err = ep.Stop() - require.NoError(t, err) + // initial state assert.False(t, ep.IsRunning()) - // Third stop also fine - err = ep.Stop() - require.NoError(t, err) -} - -func TestEventProcessorIsRunningStateTransitions(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) - - // Initial state: not running - assert.False(t, ep.IsRunning()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // After start: running - err = ep.Start(ctx) - require.NoError(t, err) + // start + require.NoError(t, ep.Start(ctx)) assert.True(t, ep.IsRunning()) - // After stop: not running - err = ep.Stop() - require.NoError(t, err) + // double start rejected + err := ep.Start(ctx) + require.Error(t, err) + assert.Contains(t, err.Error(), "already running") + + // stop, idempotent + require.NoError(t, ep.Stop()) assert.False(t, ep.IsRunning()) + require.NoError(t, ep.Stop()) - // Can restart after stop - err = ep.Start(ctx) - require.NoError(t, err) + // restart works + require.NoError(t, ep.Start(ctx)) assert.True(t, ep.IsRunning()) - - // Clean up - err = ep.Stop() - require.NoError(t, err) - assert.False(t, ep.IsRunning()) + require.NoError(t, ep.Stop()) } -func TestEventProcessorStopViaContextCancel(t *testing.T) { - logger := zerolog.Nop() - database, err := ucdb.OpenInMemoryDB(true) - require.NoError(t, err) - defer database.Close() - - ep := NewEventProcessor(nil, database, "eip155:1", true, true, logger) +func TestEventProcessor_StopViaContextCancel(t *testing.T) { + database := newTestDB(t) + ep := NewEventProcessor(database, "eip155:1", zerolog.Nop()) ctx, cancel := context.WithCancel(context.Background()) - - err = ep.Start(ctx) - require.NoError(t, err) + require.NoError(t, ep.Start(ctx)) assert.True(t, ep.IsRunning()) - // Cancel context - the processLoop should exit cancel() - // Stop should still work cleanly after context cancellation - err = ep.Stop() - require.NoError(t, err) + done := make(chan struct{}) + go func() { + _ = ep.Stop() + close(done) + }() + + select { + case <-done: + case <-time.After(10 * time.Second): + t.Fatal("processLoop did not exit after context cancellation") + } assert.False(t, ep.IsRunning()) } -func TestProcessConfirmedEventsEnabledFlags(t *testing.T) { - logger := zerolog.Nop() - ctx := context.Background() - - // Helper to create an in-memory DB and seed confirmed events - setupDB := func(t *testing.T, events []store.Event) *ucdb.DB { - t.Helper() - database, err := ucdb.OpenInMemoryDB(true) +func TestBase58ToHex(t *testing.T) { + t.Run("empty string returns 0x", func(t *testing.T) { + result, err := base58ToHex("") require.NoError(t, err) - for _, e := range events { - result := database.Client().Create(&e) - require.NoError(t, result.Error) - } - return database - } - - inboundEventData, _ := json.Marshal(UniversalTx{ - SourceChain: "eip155:1", - Sender: "0xsender", - Amount: "1000", - TxType: 2, - }) - - outboundEventData, _ := json.Marshal(OutboundEvent{ - TxID: "0xtxid", - UniversalTxID: "0xutxid", + assert.Equal(t, "0x", result) }) - makeEvents := func() []store.Event { - return []store.Event{ - { - EventID: "0xaaa:0", - Status: store.StatusConfirmed, - Type: store.EventTypeInbound, - EventData: inboundEventData, - }, - { - EventID: "0xbbb:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: outboundEventData, - }, - } - } - - t.Run("inbound disabled skips inbound events, leaves them CONFIRMED", func(t *testing.T) { - database := setupDB(t, makeEvents()) - // inbound=false, outbound=false (no signer so outbound will also fail to vote, but that's ok) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) - - err := ep.processConfirmedEvents(ctx) + t.Run("already hex returns as is", func(t *testing.T) { + input := "0xabcdef1234567890" + result, err := base58ToHex(input) require.NoError(t, err) - - // Inbound event should still be CONFIRMED (skipped, not processed) - var inboundEvt store.Event - database.Client().Where("event_id = ?", "0xaaa:0").First(&inboundEvt) - assert.Equal(t, store.StatusConfirmed, inboundEvt.Status) + assert.Equal(t, input, result) }) - t.Run("outbound disabled skips outbound events, leaves them CONFIRMED", func(t *testing.T) { - database := setupDB(t, makeEvents()) - ep := NewEventProcessor(nil, database, "eip155:1", false, false, logger) - - err := ep.processConfirmedEvents(ctx) + t.Run("valid base58 converts to hex", func(t *testing.T) { + result, err := base58ToHex("2VfUX") require.NoError(t, err) + assert.True(t, len(result) > 2) + assert.Equal(t, "0x", result[:2]) + }) - // Outbound event should still be CONFIRMED (skipped, not processed) - var outboundEvt store.Event - database.Client().Where("event_id = ?", "0xbbb:0").First(&outboundEvt) - assert.Equal(t, store.StatusConfirmed, outboundEvt.Status) + t.Run("invalid base58 returns error", func(t *testing.T) { + // Base58 doesn't include 0, O, I, l + _, err := base58ToHex("0OIl") + require.Error(t, err) }) +} - t.Run("inbound enabled but outbound disabled skips only outbound", func(t *testing.T) { - // Seed only outbound events so we don't hit nil signer panic on inbound - database := setupDB(t, []store.Event{ - { - EventID: "0xbbb:0", - Status: store.StatusConfirmed, - Type: store.EventTypeOutbound, - EventData: outboundEventData, - }, - }) - ep := NewEventProcessor(nil, database, "eip155:1", true, false, logger) +func TestEventTxHash(t *testing.T) { + t.Run("hex event id", func(t *testing.T) { + assert.Equal(t, "0xabc123", eventTxHash("0xabc123:5")) + }) - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) + t.Run("base58 event id converts", func(t *testing.T) { + got := eventTxHash("2VfUX:0") + assert.Equal(t, "0x", got[:2]) + }) - // Outbound event should still be CONFIRMED (skipped due to outbound disabled) - var outboundEvt store.Event - database.Client().Where("event_id = ?", "0xbbb:0").First(&outboundEvt) - assert.Equal(t, store.StatusConfirmed, outboundEvt.Status) + t.Run("invalid base58 falls back to raw value", func(t *testing.T) { + assert.Equal(t, "0OIl", eventTxHash("0OIl:0")) }) +} - t.Run("outbound enabled but inbound disabled skips only inbound", func(t *testing.T) { - // Seed only inbound events so we don't hit nil signer panic on outbound - database := setupDB(t, []store.Event{ - { - EventID: "0xaaa:0", - Status: store.StatusConfirmed, - Type: store.EventTypeInbound, - EventData: inboundEventData, - }, - }) - ep := NewEventProcessor(nil, database, "eip155:1", false, true, logger) +func TestMarkEventCompleted(t *testing.T) { + database := newTestDB(t) + cs := NewChainStore(database) + seedConfirmedEvent(t, database, "0xdone:0", store.EventTypeInbound, []byte("{}")) - err := ep.processConfirmedEvents(ctx) - require.NoError(t, err) + event := &store.Event{EventID: "0xdone:0", Type: store.EventTypeInbound} + require.NoError(t, markEventCompleted(cs, zerolog.Nop(), event, "0xvote")) - // Inbound event should still be CONFIRMED (skipped due to inbound disabled) - var inboundEvt store.Event - database.Client().Where("event_id = ?", "0xaaa:0").First(&inboundEvt) - assert.Equal(t, store.StatusConfirmed, inboundEvt.Status) - }) + rows, err := cs.UpdateEventStatus("0xdone:0", store.StatusCompleted, store.StatusCompleted) + require.NoError(t, err) + assert.Equal(t, int64(1), rows) + + // already completed: no-op, no error + require.NoError(t, markEventCompleted(cs, zerolog.Nop(), event, "0xvote2")) } func TestEncodeUint256Result(t *testing.T) { diff --git a/universalClient/externalchains/common/inbound_observation_event_processor.go b/universalClient/externalchains/common/inbound_observation_event_processor.go new file mode 100644 index 00000000..e86272f5 --- /dev/null +++ b/universalClient/externalchains/common/inbound_observation_event_processor.go @@ -0,0 +1,118 @@ +package common + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + + "github.com/pushchain/push-chain-node/universalClient/db" + "github.com/pushchain/push-chain-node/universalClient/store" + uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" + "github.com/rs/zerolog" +) + +// InboundObservationEventProcessor handles INBOUND events: it builds the +// inbound observation from the stored event and votes it on Push chain. +type InboundObservationEventProcessor struct { + signer VoteSigner + chainStore *ChainStore + logger zerolog.Logger +} + +// NewInboundObservationEventProcessor creates the handler for INBOUND events. +func NewInboundObservationEventProcessor( + signer VoteSigner, + database *db.DB, + logger zerolog.Logger, +) *InboundObservationEventProcessor { + return &InboundObservationEventProcessor{ + signer: signer, + chainStore: NewChainStore(database), + logger: logger.With().Str("component", "inbound_observation_event_processor").Logger(), + } +} + +// HandleEvent implements EventHandler for INBOUND events. +func (p *InboundObservationEventProcessor) HandleEvent(ctx context.Context, event *store.Event) error { + p.logger.Debug(). + Str("event_id", event.EventID). + Msg("processing inbound event") + + // Extract inbound data from event + inbound, err := p.buildInboundObservation(event) + if err != nil { + return fmt.Errorf("failed to build inbound observation: %w", err) + } + + // Execute vote on blockchain + voteTxHash, err := p.signer.VoteInbound(ctx, inbound) + if err != nil { + return fmt.Errorf("failed to vote on inbound - keeping status for retry: %w", err) + } + + return markEventCompleted(p.chainStore, p.logger, event, voteTxHash) +} + +// buildInboundObservation builds an Inbound observation from event data +func (p *InboundObservationEventProcessor) buildInboundObservation(event *store.Event) (*uexecutortypes.Inbound, error) { + var eventData UniversalTx + + if event == nil { + return nil, fmt.Errorf("event is nil") + } + + if event.EventData == nil { + return nil, fmt.Errorf("event data is missing for event_id: %s", event.EventID) + } + + if err := json.Unmarshal(event.EventData, &eventData); err != nil { + return nil, fmt.Errorf("failed to unmarshal event data: %w", err) + } + + // Map txType from eventData to proper enum value + txType := uexecutortypes.TxType_UNSPECIFIED_TX + switch eventData.TxType { + case 0: + txType = uexecutortypes.TxType_GAS + case 1: + txType = uexecutortypes.TxType_GAS_AND_PAYLOAD + case 2: + txType = uexecutortypes.TxType_FUNDS + case 3: + txType = uexecutortypes.TxType_FUNDS_AND_PAYLOAD + default: + txType = uexecutortypes.TxType_UNSPECIFIED_TX + } + + txHashHex := eventTxHash(event.EventID) + + inboundMsg := &uexecutortypes.Inbound{ + SourceChain: eventData.SourceChain, + TxHash: txHashHex, + Sender: eventData.Sender, + Recipient: eventData.Recipient, + Amount: eventData.Amount, + AssetAddr: eventData.Token, + LogIndex: strconv.FormatUint(uint64(eventData.LogIndex), 10), + TxType: txType, + IsCEA: eventData.FromCEA, + RawPayload: eventData.RawPayload, + } + + // Set revert instructions if revert fund recipient is present + if eventData.RevertFundRecipient != "" { + inboundMsg.RevertInstructions = &uexecutortypes.RevertInstructions{ + FundRecipient: eventData.RevertFundRecipient, + } + } + + // Use event's VerificationData if present, otherwise fall back to txHash + if eventData.VerificationData == "" || eventData.VerificationData == "0x" { + inboundMsg.VerificationData = txHashHex + } else { + inboundMsg.VerificationData = eventData.VerificationData + } + + return inboundMsg, nil +} diff --git a/universalClient/externalchains/common/inbound_observation_event_processor_test.go b/universalClient/externalchains/common/inbound_observation_event_processor_test.go new file mode 100644 index 00000000..30440e9e --- /dev/null +++ b/universalClient/externalchains/common/inbound_observation_event_processor_test.go @@ -0,0 +1,214 @@ +package common + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/store" + uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" +) + +func TestInboundBuildInboundObservation(t *testing.T) { + processor := NewInboundObservationEventProcessor(nil, nil, zerolog.Nop()) + + t.Run("nil event returns error", func(t *testing.T) { + inbound, err := processor.buildInboundObservation(nil) + require.Error(t, err) + assert.Nil(t, inbound) + assert.Contains(t, err.Error(), "event is nil") + }) + + t.Run("nil event data returns error", func(t *testing.T) { + event := &store.Event{ + EventID: "0x123:0", + EventData: nil, + } + inbound, err := processor.buildInboundObservation(event) + require.Error(t, err) + assert.Nil(t, inbound) + assert.Contains(t, err.Error(), "event data is missing") + }) + + t.Run("invalid JSON returns error", func(t *testing.T) { + event := &store.Event{ + EventID: "0x123:0", + EventData: []byte("invalid json"), + } + inbound, err := processor.buildInboundObservation(event) + require.Error(t, err) + assert.Nil(t, inbound) + }) + + t.Run("valid event data constructs inbound", func(t *testing.T) { + eventData := UniversalTx{ + SourceChain: "eip155:1", + LogIndex: 5, + Sender: "0xsender123", + Recipient: "push1recipient", + Token: "0xtoken", + Amount: "1000000", + TxType: 2, // FUNDS + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "0xabc123:5", + EventData: eventDataBytes, + } + + inbound, err := processor.buildInboundObservation(event) + require.NoError(t, err) + require.NotNil(t, inbound) + assert.Equal(t, "eip155:1", inbound.SourceChain) + assert.Equal(t, "0xsender123", inbound.Sender) + assert.Equal(t, "1000000", inbound.Amount) + assert.Equal(t, "0xabc123", inbound.TxHash) + assert.Equal(t, uexecutortypes.TxType_FUNDS, inbound.TxType) + }) + + t.Run("passes all fields unconditionally to inbound", func(t *testing.T) { + eventData := UniversalTx{ + SourceChain: "eip155:1", + LogIndex: 3, + Sender: "0xsender", + Recipient: "0xrecipient", + Token: "0xtoken", + Amount: "500", + RawPayload: "0xdeadbeef", + VerificationData: "0xsigdata", + RevertFundRecipient: "0xrevert", + TxType: 3, // FUNDS_AND_PAYLOAD + FromCEA: true, + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "0xtxhash:3", + EventData: eventDataBytes, + } + + inbound, err := processor.buildInboundObservation(event) + require.NoError(t, err) + require.NotNil(t, inbound) + assert.Equal(t, "0xrecipient", inbound.Recipient) + assert.Equal(t, "0xdeadbeef", inbound.RawPayload) + assert.Equal(t, "0xsigdata", inbound.VerificationData) + assert.True(t, inbound.IsCEA) + require.NotNil(t, inbound.RevertInstructions) + assert.Equal(t, "0xrevert", inbound.RevertInstructions.FundRecipient) + }) + + t.Run("no revert instructions when revert recipient is empty", func(t *testing.T) { + eventData := UniversalTx{ + SourceChain: "eip155:1", + Sender: "0xsender", + Amount: "100", + TxType: 0, // GAS + RevertFundRecipient: "", + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "0xhash:0", + EventData: eventDataBytes, + } + + inbound, err := processor.buildInboundObservation(event) + require.NoError(t, err) + assert.Nil(t, inbound.RevertInstructions) + }) + + t.Run("falls back verification data to tx hash", func(t *testing.T) { + eventData := UniversalTx{ + SourceChain: "eip155:1", + VerificationData: "", + TxType: 0, + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "0xhash:0", + EventData: eventDataBytes, + } + + inbound, err := processor.buildInboundObservation(event) + require.NoError(t, err) + assert.Equal(t, "0xhash", inbound.VerificationData) + }) + + t.Run("tx type mapping", func(t *testing.T) { + testCases := []struct { + txType uint + expected uexecutortypes.TxType + }{ + {0, uexecutortypes.TxType_GAS}, + {1, uexecutortypes.TxType_GAS_AND_PAYLOAD}, + {2, uexecutortypes.TxType_FUNDS}, + {3, uexecutortypes.TxType_FUNDS_AND_PAYLOAD}, + {99, uexecutortypes.TxType_UNSPECIFIED_TX}, // Unknown defaults to unspecified + } + + for _, tc := range testCases { + eventData := UniversalTx{ + SourceChain: "eip155:1", + TxType: tc.txType, + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "0xabc:0", + EventData: eventDataBytes, + } + + inbound, err := processor.buildInboundObservation(event) + require.NoError(t, err) + assert.Equal(t, tc.expected, inbound.TxType, "TxType %d should map to %v", tc.txType, tc.expected) + } + }) +} + +func TestInboundHandleEvent(t *testing.T) { + ctx := context.Background() + + t.Run("construct failure returns error, event stays CONFIRMED", func(t *testing.T) { + database := newTestDB(t) + processor := NewInboundObservationEventProcessor(&fakeVoteSigner{txHash: "0xvote"}, database, zerolog.Nop()) + seedConfirmedEvent(t, database, "0xbad:0", store.EventTypeInbound, []byte("not json")) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xbad:0", EventData: []byte("not json")}) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to build inbound observation") + }) + + t.Run("vote failure returns error", func(t *testing.T) { + database := newTestDB(t) + processor := NewInboundObservationEventProcessor(&fakeVoteSigner{err: fmt.Errorf("broadcast failed")}, database, zerolog.Nop()) + eventData, _ := json.Marshal(UniversalTx{SourceChain: "eip155:1", TxType: 0}) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xin:0", EventData: eventData}) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to vote on inbound") + }) + + t.Run("successful vote marks event completed", func(t *testing.T) { + database := newTestDB(t) + signer := &fakeVoteSigner{txHash: "0xvote"} + processor := NewInboundObservationEventProcessor(signer, database, zerolog.Nop()) + eventData, _ := json.Marshal(UniversalTx{SourceChain: "eip155:1", TxType: 0}) + seedConfirmedEvent(t, database, "0xin:0", store.EventTypeInbound, eventData) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xin:0", Type: store.EventTypeInbound, EventData: eventData}) + require.NoError(t, err) + assert.Equal(t, 1, signer.inboundVotes) + + rows, err := NewChainStore(database).UpdateEventStatus("0xin:0", store.StatusCompleted, store.StatusCompleted) + require.NoError(t, err) + assert.Equal(t, int64(1), rows) + }) +} diff --git a/universalClient/externalchains/common/outbound_observation_event_processor.go b/universalClient/externalchains/common/outbound_observation_event_processor.go new file mode 100644 index 00000000..56974ee6 --- /dev/null +++ b/universalClient/externalchains/common/outbound_observation_event_processor.go @@ -0,0 +1,105 @@ +package common + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/pushchain/push-chain-node/universalClient/db" + "github.com/pushchain/push-chain-node/universalClient/store" + uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" + "github.com/rs/zerolog" +) + +// OutboundObservationEventProcessor handles OUTBOUND events: it builds the +// outbound observation from the stored event and votes it on Push chain. +type OutboundObservationEventProcessor struct { + signer VoteSigner + chainStore *ChainStore + logger zerolog.Logger +} + +// NewOutboundObservationEventProcessor creates the handler for OUTBOUND events. +func NewOutboundObservationEventProcessor( + signer VoteSigner, + database *db.DB, + logger zerolog.Logger, +) *OutboundObservationEventProcessor { + return &OutboundObservationEventProcessor{ + signer: signer, + chainStore: NewChainStore(database), + logger: logger.With().Str("component", "outbound_observation_event_processor").Logger(), + } +} + +// HandleEvent implements EventHandler for OUTBOUND events. +func (p *OutboundObservationEventProcessor) HandleEvent(ctx context.Context, event *store.Event) error { + p.logger.Debug(). + Str("event_id", event.EventID). + Msg("processing outbound event") + + // Parse outbound event data once + outboundData, err := p.parseOutboundEventData(event) + if err != nil { + return fmt.Errorf("failed to parse outbound event data: %w", err) + } + + // Build observation from parsed data + observation, err := p.buildOutboundObservation(event, outboundData) + if err != nil { + return fmt.Errorf("failed to build outbound observation: %w", err) + } + + // Vote on outbound + voteTxHash, err := p.signer.VoteOutbound(ctx, outboundData.TxID, outboundData.UniversalTxID, observation) + if err != nil { + return fmt.Errorf("failed to vote on outbound: %w", err) + } + + return markEventCompleted(p.chainStore, p.logger, event, voteTxHash) +} + +// parseOutboundEventData unmarshals event data into an OutboundEvent struct +func (p *OutboundObservationEventProcessor) parseOutboundEventData(event *store.Event) (*OutboundEvent, error) { + if event == nil { + return nil, fmt.Errorf("event is nil") + } + + if len(event.EventData) == 0 { + return nil, fmt.Errorf("event data is empty") + } + + var eventData OutboundEvent + if err := json.Unmarshal(event.EventData, &eventData); err != nil { + return nil, fmt.Errorf("failed to unmarshal event data: %w", err) + } + + if eventData.TxID == "" { + return nil, fmt.Errorf("tx_id not found in event data") + } + + if eventData.UniversalTxID == "" { + return nil, fmt.Errorf("universal_tx_id not found in event data") + } + + return &eventData, nil +} + +// buildOutboundObservation builds an OutboundObservation from event metadata and parsed outbound data +func (p *OutboundObservationEventProcessor) buildOutboundObservation(event *store.Event, outboundData *OutboundEvent) (*uexecutortypes.OutboundObservation, error) { + gasFeeUsed := "0" + if outboundData.GasFeeUsed != "" { + gasFeeUsed = outboundData.GasFeeUsed + } + + observation := &uexecutortypes.OutboundObservation{ + Success: true, + BlockHeight: event.BlockHeight, + TxHash: eventTxHash(event.EventID), + ErrorMsg: "", + GasFeeUsed: gasFeeUsed, + Pc20WrapperAddress: outboundData.Pc20WrapperAddress, + } + + return observation, nil +} diff --git a/universalClient/externalchains/common/outbound_observation_event_processor_test.go b/universalClient/externalchains/common/outbound_observation_event_processor_test.go new file mode 100644 index 00000000..8a456a41 --- /dev/null +++ b/universalClient/externalchains/common/outbound_observation_event_processor_test.go @@ -0,0 +1,191 @@ +package common + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/store" +) + +func TestOutboundParseOutboundEventData(t *testing.T) { + processor := NewOutboundObservationEventProcessor(nil, nil, zerolog.Nop()) + + t.Run("nil event returns error", func(t *testing.T) { + data, err := processor.parseOutboundEventData(nil) + require.Error(t, err) + assert.Nil(t, data) + assert.Contains(t, err.Error(), "event is nil") + }) + + t.Run("empty event data returns error", func(t *testing.T) { + event := &store.Event{ + EventID: "test", + EventData: []byte{}, + } + data, err := processor.parseOutboundEventData(event) + require.Error(t, err) + assert.Nil(t, data) + assert.Contains(t, err.Error(), "event data is empty") + }) + + t.Run("valid outbound event extracts IDs and gas fee", func(t *testing.T) { + eventData := OutboundEvent{ + TxID: "0x1234", + UniversalTxID: "0xabcd", + GasFeeUsed: "42000000000000", + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "test", + EventData: eventDataBytes, + } + + data, err := processor.parseOutboundEventData(event) + require.NoError(t, err) + assert.Equal(t, "0x1234", data.TxID) + assert.Equal(t, "0xabcd", data.UniversalTxID) + assert.Equal(t, "42000000000000", data.GasFeeUsed) + }) + + t.Run("missing tx_id returns error", func(t *testing.T) { + eventData := OutboundEvent{ + TxID: "", + UniversalTxID: "0xabcd", + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "test", + EventData: eventDataBytes, + } + + data, err := processor.parseOutboundEventData(event) + require.Error(t, err) + assert.Nil(t, data) + assert.Contains(t, err.Error(), "tx_id not found") + }) + + t.Run("missing universal_tx_id returns error", func(t *testing.T) { + eventData := OutboundEvent{ + TxID: "0x1234", + UniversalTxID: "", + } + eventDataBytes, _ := json.Marshal(eventData) + + event := &store.Event{ + EventID: "test", + EventData: eventDataBytes, + } + + data, err := processor.parseOutboundEventData(event) + require.Error(t, err) + assert.Nil(t, data) + assert.Contains(t, err.Error(), "universal_tx_id not found") + }) +} + +func TestOutboundBuildOutboundObservation(t *testing.T) { + processor := NewOutboundObservationEventProcessor(nil, nil, zerolog.Nop()) + + t.Run("builds observation with gas fee from parsed data", func(t *testing.T) { + outboundData := &OutboundEvent{ + TxID: "0x1234", + UniversalTxID: "0xabcd", + GasFeeUsed: "42000000000000", + } + + event := &store.Event{ + EventID: "0xabc123:5", + BlockHeight: 12345, + } + + obs, err := processor.buildOutboundObservation(event, outboundData) + require.NoError(t, err) + require.NotNil(t, obs) + assert.True(t, obs.Success) + assert.Equal(t, uint64(12345), obs.BlockHeight) + assert.Equal(t, "0xabc123", obs.TxHash) + assert.Equal(t, "42000000000000", obs.GasFeeUsed) + }) + + t.Run("missing gas fee defaults to 0", func(t *testing.T) { + outboundData := &OutboundEvent{ + TxID: "0x1234", + UniversalTxID: "0xabcd", + } + + event := &store.Event{ + EventID: "0xabc123:5", + BlockHeight: 12345, + } + + obs, err := processor.buildOutboundObservation(event, outboundData) + require.NoError(t, err) + require.NotNil(t, obs) + assert.Equal(t, "0", obs.GasFeeUsed) + }) + + t.Run("handles base58 tx hash", func(t *testing.T) { + outboundData := &OutboundEvent{ + TxID: "0x1234", + UniversalTxID: "0xabcd", + } + + event := &store.Event{ + EventID: "2VfUX:0", // Base58 encoded + BlockHeight: 100, + } + + obs, err := processor.buildOutboundObservation(event, outboundData) + require.NoError(t, err) + require.NotNil(t, obs) + assert.True(t, len(obs.TxHash) >= 2) + assert.Equal(t, "0x", obs.TxHash[:2]) + }) +} + +func TestOutboundHandleEvent(t *testing.T) { + ctx := context.Background() + + t.Run("parse failure returns error", func(t *testing.T) { + database := newTestDB(t) + processor := NewOutboundObservationEventProcessor(&fakeVoteSigner{txHash: "0xvote"}, database, zerolog.Nop()) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xbad:0", EventData: []byte("not json")}) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to parse outbound event data") + }) + + t.Run("vote failure returns error", func(t *testing.T) { + database := newTestDB(t) + processor := NewOutboundObservationEventProcessor(&fakeVoteSigner{err: fmt.Errorf("broadcast failed")}, database, zerolog.Nop()) + eventData, _ := json.Marshal(OutboundEvent{TxID: "0xtxid", UniversalTxID: "0xutxid"}) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xout:0", EventData: eventData}) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to vote on outbound") + }) + + t.Run("successful vote marks event completed", func(t *testing.T) { + database := newTestDB(t) + signer := &fakeVoteSigner{txHash: "0xvote"} + processor := NewOutboundObservationEventProcessor(signer, database, zerolog.Nop()) + eventData, _ := json.Marshal(OutboundEvent{TxID: "0xtxid", UniversalTxID: "0xutxid"}) + seedConfirmedEvent(t, database, "0xout:0", store.EventTypeOutbound, eventData) + + err := processor.HandleEvent(ctx, &store.Event{EventID: "0xout:0", Type: store.EventTypeOutbound, EventData: eventData}) + require.NoError(t, err) + assert.Equal(t, 1, signer.outboundVotes) + + rows, err := NewChainStore(database).UpdateEventStatus("0xout:0", store.StatusCompleted, store.StatusCompleted) + require.NoError(t, err) + assert.Equal(t, int64(1), rows) + }) +} diff --git a/universalClient/externalchains/evm/client.go b/universalClient/externalchains/evm/client.go index 07f83684..2b2af36a 100644 --- a/universalClient/externalchains/evm/client.go +++ b/universalClient/externalchains/evm/client.go @@ -14,6 +14,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" + "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -86,16 +87,14 @@ func NewClient( // Initialize components that don't require RPC client if pushSigner != nil { - inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled - outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled - client.eventProcessor = common.NewEventProcessor( - pushSigner, - database, - chainIDStr, - inboundEnabled, - outboundEnabled, - log, - ) + ep := common.NewEventProcessor(database, chainIDStr, log) + if config.Enabled != nil && config.Enabled.IsInboundEnabled { + ep.RegisterHandler(store.EventTypeInbound, common.NewInboundObservationEventProcessor(pushSigner, database, log)) + } + if config.Enabled != nil && config.Enabled.IsOutboundEnabled { + ep.RegisterHandler(store.EventTypeOutbound, common.NewOutboundObservationEventProcessor(pushSigner, database, log)) + } + client.eventProcessor = ep } return client, nil @@ -207,7 +206,6 @@ func (c *Client) GetReadRequestHandler() (common.ReadRequestHandler, error) { return c, nil } - // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { // Create event listener if gateway is configured diff --git a/universalClient/externalchains/svm/client.go b/universalClient/externalchains/svm/client.go index 95e00104..e0afd3a0 100644 --- a/universalClient/externalchains/svm/client.go +++ b/universalClient/externalchains/svm/client.go @@ -12,6 +12,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/pushsigner" + "github.com/pushchain/push-chain-node/universalClient/store" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" ) @@ -96,16 +97,14 @@ func NewClient( // Initialize components that don't require RPC client if pushSigner != nil { - inboundEnabled := config.Enabled != nil && config.Enabled.IsInboundEnabled - outboundEnabled := config.Enabled != nil && config.Enabled.IsOutboundEnabled - client.eventProcessor = common.NewEventProcessor( - pushSigner, - database, - chainIDStr, - inboundEnabled, - outboundEnabled, - log, - ) + ep := common.NewEventProcessor(database, chainIDStr, log) + if config.Enabled != nil && config.Enabled.IsInboundEnabled { + ep.RegisterHandler(store.EventTypeInbound, common.NewInboundObservationEventProcessor(pushSigner, database, log)) + } + if config.Enabled != nil && config.Enabled.IsOutboundEnabled { + ep.RegisterHandler(store.EventTypeOutbound, common.NewOutboundObservationEventProcessor(pushSigner, database, log)) + } + client.eventProcessor = ep } return client, nil @@ -217,7 +216,6 @@ func (c *Client) GetReadRequestHandler() (common.ReadRequestHandler, error) { return c, nil } - // initializeComponents creates all components that require the RPC client func (c *Client) initializeComponents() error { // Create event listener if gateway is configured From b4d539af9bfae45c8ca2d09c48b473de5fe0fd07 Mon Sep 17 00:00:00 2001 From: aman035 Date: Mon, 3 Aug 2026 18:48:54 +0530 Subject: [PATCH 20/54] refactor: observeration events --- .../externalchains/common/chain_store_test.go | 1 - .../inbound_observation_event_processor.go | 17 ++++++++++- ...nbound_observation_event_processor_test.go | 14 ++++----- .../outbound_observation_event_processor.go | 22 +++++++++++--- ...tbound_observation_event_processor_test.go | 16 +++++----- .../externalchains/common/types.go | 30 ------------------- .../externalchains/evm/event_confirmer.go | 2 +- .../evm/event_confirmer_test.go | 4 +-- .../externalchains/evm/event_parser.go | 10 +++---- .../externalchains/evm/event_parser_test.go | 26 ++++++++-------- .../externalchains/svm/event_parser.go | 6 ++-- .../externalchains/svm/event_parser_test.go | 26 ++++++++-------- 12 files changed, 86 insertions(+), 88 deletions(-) diff --git a/universalClient/externalchains/common/chain_store_test.go b/universalClient/externalchains/common/chain_store_test.go index 461b2608..bc413cac 100644 --- a/universalClient/externalchains/common/chain_store_test.go +++ b/universalClient/externalchains/common/chain_store_test.go @@ -56,7 +56,6 @@ func TestChainStoreNilDatabase(t *testing.T) { assert.Contains(t, err.Error(), "database is nil") }) - t.Run("InsertEventIfNotExists returns error for nil database", func(t *testing.T) { inserted, err := store.InsertEventIfNotExists(nil) require.Error(t, err) diff --git a/universalClient/externalchains/common/inbound_observation_event_processor.go b/universalClient/externalchains/common/inbound_observation_event_processor.go index e86272f5..c72f8842 100644 --- a/universalClient/externalchains/common/inbound_observation_event_processor.go +++ b/universalClient/externalchains/common/inbound_observation_event_processor.go @@ -12,6 +12,21 @@ import ( "github.com/rs/zerolog" ) +// InboundObservation is the inbound observation payload stored for INBOUND events +type InboundObservation struct { + SourceChain string `json:"sourceChain"` + LogIndex uint `json:"logIndex"` + Sender string `json:"sender"` + Recipient string `json:"recipient"` + Token string `json:"bridgeToken"` + Amount string `json:"bridgeAmount"` // uint256 as decimal string + RawPayload string `json:"rawPayload,omitempty"` // hex-encoded raw payload bytes from source chain + VerificationData string `json:"verificationData"` + RevertFundRecipient string `json:"revertFundRecipient,omitempty"` + TxType uint `json:"txType"` // enum backing uint as decimal string + FromCEA bool `json:"fromCEA"` // true if inbound is initiated by a CEA +} + // InboundObservationEventProcessor handles INBOUND events: it builds the // inbound observation from the stored event and votes it on Push chain. type InboundObservationEventProcessor struct { @@ -56,7 +71,7 @@ func (p *InboundObservationEventProcessor) HandleEvent(ctx context.Context, even // buildInboundObservation builds an Inbound observation from event data func (p *InboundObservationEventProcessor) buildInboundObservation(event *store.Event) (*uexecutortypes.Inbound, error) { - var eventData UniversalTx + var eventData InboundObservation if event == nil { return nil, fmt.Errorf("event is nil") diff --git a/universalClient/externalchains/common/inbound_observation_event_processor_test.go b/universalClient/externalchains/common/inbound_observation_event_processor_test.go index 30440e9e..4abfe0ab 100644 --- a/universalClient/externalchains/common/inbound_observation_event_processor_test.go +++ b/universalClient/externalchains/common/inbound_observation_event_processor_test.go @@ -46,7 +46,7 @@ func TestInboundBuildInboundObservation(t *testing.T) { }) t.Run("valid event data constructs inbound", func(t *testing.T) { - eventData := UniversalTx{ + eventData := InboundObservation{ SourceChain: "eip155:1", LogIndex: 5, Sender: "0xsender123", @@ -73,7 +73,7 @@ func TestInboundBuildInboundObservation(t *testing.T) { }) t.Run("passes all fields unconditionally to inbound", func(t *testing.T) { - eventData := UniversalTx{ + eventData := InboundObservation{ SourceChain: "eip155:1", LogIndex: 3, Sender: "0xsender", @@ -105,7 +105,7 @@ func TestInboundBuildInboundObservation(t *testing.T) { }) t.Run("no revert instructions when revert recipient is empty", func(t *testing.T) { - eventData := UniversalTx{ + eventData := InboundObservation{ SourceChain: "eip155:1", Sender: "0xsender", Amount: "100", @@ -125,7 +125,7 @@ func TestInboundBuildInboundObservation(t *testing.T) { }) t.Run("falls back verification data to tx hash", func(t *testing.T) { - eventData := UniversalTx{ + eventData := InboundObservation{ SourceChain: "eip155:1", VerificationData: "", TxType: 0, @@ -155,7 +155,7 @@ func TestInboundBuildInboundObservation(t *testing.T) { } for _, tc := range testCases { - eventData := UniversalTx{ + eventData := InboundObservation{ SourceChain: "eip155:1", TxType: tc.txType, } @@ -189,7 +189,7 @@ func TestInboundHandleEvent(t *testing.T) { t.Run("vote failure returns error", func(t *testing.T) { database := newTestDB(t) processor := NewInboundObservationEventProcessor(&fakeVoteSigner{err: fmt.Errorf("broadcast failed")}, database, zerolog.Nop()) - eventData, _ := json.Marshal(UniversalTx{SourceChain: "eip155:1", TxType: 0}) + eventData, _ := json.Marshal(InboundObservation{SourceChain: "eip155:1", TxType: 0}) err := processor.HandleEvent(ctx, &store.Event{EventID: "0xin:0", EventData: eventData}) require.Error(t, err) @@ -200,7 +200,7 @@ func TestInboundHandleEvent(t *testing.T) { database := newTestDB(t) signer := &fakeVoteSigner{txHash: "0xvote"} processor := NewInboundObservationEventProcessor(signer, database, zerolog.Nop()) - eventData, _ := json.Marshal(UniversalTx{SourceChain: "eip155:1", TxType: 0}) + eventData, _ := json.Marshal(InboundObservation{SourceChain: "eip155:1", TxType: 0}) seedConfirmedEvent(t, database, "0xin:0", store.EventTypeInbound, eventData) err := processor.HandleEvent(ctx, &store.Event{EventID: "0xin:0", Type: store.EventTypeInbound, EventData: eventData}) diff --git a/universalClient/externalchains/common/outbound_observation_event_processor.go b/universalClient/externalchains/common/outbound_observation_event_processor.go index 56974ee6..8ba4ee29 100644 --- a/universalClient/externalchains/common/outbound_observation_event_processor.go +++ b/universalClient/externalchains/common/outbound_observation_event_processor.go @@ -11,6 +11,20 @@ import ( "github.com/rs/zerolog" ) +// OutboundObservation is the outbound observation payload stored for OUTBOUND events +// Event structure: +// - txID at 1st indexed position (bytes32) +// - universalTxID at 2nd indexed position (bytes32) +type OutboundObservation struct { + TxID string `json:"tx_id"` // bytes32 hex-encoded (0x...) + UniversalTxID string `json:"universal_tx_id"` // bytes32 hex-encoded (0x...) + GasFeeUsed string `json:"gas_fee_used,omitempty"` // gas fee used in wei (decimal string) + // PC20 export only: wrapper token address deployed/minted on the destination + // at settlement (observed in the finalize event). Core uses it to flip the + // PC20 deploy flag; empty for non-PC20 settlements. + Pc20WrapperAddress string `json:"pc20_wrapper_address,omitempty"` +} + // OutboundObservationEventProcessor handles OUTBOUND events: it builds the // outbound observation from the stored event and votes it on Push chain. type OutboundObservationEventProcessor struct { @@ -59,8 +73,8 @@ func (p *OutboundObservationEventProcessor) HandleEvent(ctx context.Context, eve return markEventCompleted(p.chainStore, p.logger, event, voteTxHash) } -// parseOutboundEventData unmarshals event data into an OutboundEvent struct -func (p *OutboundObservationEventProcessor) parseOutboundEventData(event *store.Event) (*OutboundEvent, error) { +// parseOutboundEventData unmarshals event data into an OutboundObservation struct +func (p *OutboundObservationEventProcessor) parseOutboundEventData(event *store.Event) (*OutboundObservation, error) { if event == nil { return nil, fmt.Errorf("event is nil") } @@ -69,7 +83,7 @@ func (p *OutboundObservationEventProcessor) parseOutboundEventData(event *store. return nil, fmt.Errorf("event data is empty") } - var eventData OutboundEvent + var eventData OutboundObservation if err := json.Unmarshal(event.EventData, &eventData); err != nil { return nil, fmt.Errorf("failed to unmarshal event data: %w", err) } @@ -86,7 +100,7 @@ func (p *OutboundObservationEventProcessor) parseOutboundEventData(event *store. } // buildOutboundObservation builds an OutboundObservation from event metadata and parsed outbound data -func (p *OutboundObservationEventProcessor) buildOutboundObservation(event *store.Event, outboundData *OutboundEvent) (*uexecutortypes.OutboundObservation, error) { +func (p *OutboundObservationEventProcessor) buildOutboundObservation(event *store.Event, outboundData *OutboundObservation) (*uexecutortypes.OutboundObservation, error) { gasFeeUsed := "0" if outboundData.GasFeeUsed != "" { gasFeeUsed = outboundData.GasFeeUsed diff --git a/universalClient/externalchains/common/outbound_observation_event_processor_test.go b/universalClient/externalchains/common/outbound_observation_event_processor_test.go index 8a456a41..af3db22c 100644 --- a/universalClient/externalchains/common/outbound_observation_event_processor_test.go +++ b/universalClient/externalchains/common/outbound_observation_event_processor_test.go @@ -35,7 +35,7 @@ func TestOutboundParseOutboundEventData(t *testing.T) { }) t.Run("valid outbound event extracts IDs and gas fee", func(t *testing.T) { - eventData := OutboundEvent{ + eventData := OutboundObservation{ TxID: "0x1234", UniversalTxID: "0xabcd", GasFeeUsed: "42000000000000", @@ -55,7 +55,7 @@ func TestOutboundParseOutboundEventData(t *testing.T) { }) t.Run("missing tx_id returns error", func(t *testing.T) { - eventData := OutboundEvent{ + eventData := OutboundObservation{ TxID: "", UniversalTxID: "0xabcd", } @@ -73,7 +73,7 @@ func TestOutboundParseOutboundEventData(t *testing.T) { }) t.Run("missing universal_tx_id returns error", func(t *testing.T) { - eventData := OutboundEvent{ + eventData := OutboundObservation{ TxID: "0x1234", UniversalTxID: "", } @@ -95,7 +95,7 @@ func TestOutboundBuildOutboundObservation(t *testing.T) { processor := NewOutboundObservationEventProcessor(nil, nil, zerolog.Nop()) t.Run("builds observation with gas fee from parsed data", func(t *testing.T) { - outboundData := &OutboundEvent{ + outboundData := &OutboundObservation{ TxID: "0x1234", UniversalTxID: "0xabcd", GasFeeUsed: "42000000000000", @@ -116,7 +116,7 @@ func TestOutboundBuildOutboundObservation(t *testing.T) { }) t.Run("missing gas fee defaults to 0", func(t *testing.T) { - outboundData := &OutboundEvent{ + outboundData := &OutboundObservation{ TxID: "0x1234", UniversalTxID: "0xabcd", } @@ -133,7 +133,7 @@ func TestOutboundBuildOutboundObservation(t *testing.T) { }) t.Run("handles base58 tx hash", func(t *testing.T) { - outboundData := &OutboundEvent{ + outboundData := &OutboundObservation{ TxID: "0x1234", UniversalTxID: "0xabcd", } @@ -166,7 +166,7 @@ func TestOutboundHandleEvent(t *testing.T) { t.Run("vote failure returns error", func(t *testing.T) { database := newTestDB(t) processor := NewOutboundObservationEventProcessor(&fakeVoteSigner{err: fmt.Errorf("broadcast failed")}, database, zerolog.Nop()) - eventData, _ := json.Marshal(OutboundEvent{TxID: "0xtxid", UniversalTxID: "0xutxid"}) + eventData, _ := json.Marshal(OutboundObservation{TxID: "0xtxid", UniversalTxID: "0xutxid"}) err := processor.HandleEvent(ctx, &store.Event{EventID: "0xout:0", EventData: eventData}) require.Error(t, err) @@ -177,7 +177,7 @@ func TestOutboundHandleEvent(t *testing.T) { database := newTestDB(t) signer := &fakeVoteSigner{txHash: "0xvote"} processor := NewOutboundObservationEventProcessor(signer, database, zerolog.Nop()) - eventData, _ := json.Marshal(OutboundEvent{TxID: "0xtxid", UniversalTxID: "0xutxid"}) + eventData, _ := json.Marshal(OutboundObservation{TxID: "0xtxid", UniversalTxID: "0xutxid"}) seedConfirmedEvent(t, database, "0xout:0", store.EventTypeOutbound, eventData) err := processor.HandleEvent(ctx, &store.Event{EventID: "0xout:0", Type: store.EventTypeOutbound, EventData: eventData}) diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index a389437f..54a26cee 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -119,33 +119,3 @@ type TxBuilder interface { // BroadcastFundMigrationTx assembles and broadcasts a signed fund migration transaction. BroadcastFundMigrationTx(ctx context.Context, req *UnsignedSigningReq, data *FundMigrationData, signature []byte) (string, error) } - -// UniversalTx Payload -type UniversalTx struct { - SourceChain string `json:"sourceChain"` - LogIndex uint `json:"logIndex"` - Sender string `json:"sender"` - Recipient string `json:"recipient"` - Token string `json:"bridgeToken"` - Amount string `json:"bridgeAmount"` // uint256 as decimal string - RawPayload string `json:"rawPayload,omitempty"` // hex-encoded raw payload bytes from source chain - VerificationData string `json:"verificationData"` - RevertFundRecipient string `json:"revertFundRecipient,omitempty"` - TxType uint `json:"txType"` // enum backing uint as decimal string - FromCEA bool `json:"fromCEA"` // true if inbound is initiated by a CEA -} - -// OutboundEvent represents an outbound observation event from the gateway contract -// Event structure: -// - txID at 1st indexed position (bytes32) -// - universalTxID at 2nd indexed position (bytes32) -type OutboundEvent struct { - TxID string `json:"tx_id"` // bytes32 hex-encoded (0x...) - UniversalTxID string `json:"universal_tx_id"` // bytes32 hex-encoded (0x...) - GasFeeUsed string `json:"gas_fee_used,omitempty"` // gas fee used in wei (decimal string) - // PC20 export only: wrapper token address deployed/minted on the destination - // at settlement (observed in the finalize event). Core uses it to flip the - // PC20 deploy flag; empty for non-PC20 settlements. - Pc20WrapperAddress string `json:"pc20_wrapper_address,omitempty"` -} - diff --git a/universalClient/externalchains/evm/event_confirmer.go b/universalClient/externalchains/evm/event_confirmer.go index 45cabc08..b4c73a83 100644 --- a/universalClient/externalchains/evm/event_confirmer.go +++ b/universalClient/externalchains/evm/event_confirmer.go @@ -181,7 +181,7 @@ func (ec *EventConfirmer) processPendingEvents(ctx context.Context) error { gasFeeUsed := new(big.Int).Mul(gasUsed, gasPrice).String() // Unmarshal, set GasFeeUsed, re-marshal - var outboundEvent chaincommon.OutboundEvent + var outboundEvent chaincommon.OutboundObservation if unmarshalErr := json.Unmarshal(event.EventData, &outboundEvent); unmarshalErr != nil { ec.logger.Error(). Err(unmarshalErr). diff --git a/universalClient/externalchains/evm/event_confirmer_test.go b/universalClient/externalchains/evm/event_confirmer_test.go index 022812cb..55ae7f51 100644 --- a/universalClient/externalchains/evm/event_confirmer_test.go +++ b/universalClient/externalchains/evm/event_confirmer_test.go @@ -313,7 +313,7 @@ func TestEventConfirmer_UpdateStatusAndEventData_WithDB(t *testing.T) { _, memDB := newTestEventConfirmerWithDB(t) cs := common.NewChainStore(memDB) - outbound := common.OutboundEvent{ + outbound := common.OutboundObservation{ TxID: "0xtx1", UniversalTxID: "0xuni1", } @@ -347,7 +347,7 @@ func TestEventConfirmer_UpdateStatusAndEventData_WithDB(t *testing.T) { require.Len(t, confirmed, 1) assert.Equal(t, "0xoutbound1:0", confirmed[0].EventID) - var stored common.OutboundEvent + var stored common.OutboundObservation require.NoError(t, json.Unmarshal(confirmed[0].EventData, &stored)) assert.Equal(t, "123456789", stored.GasFeeUsed) } diff --git a/universalClient/externalchains/evm/event_parser.go b/universalClient/externalchains/evm/event_parser.go index 3d44d470..a603f8ad 100644 --- a/universalClient/externalchains/evm/event_parser.go +++ b/universalClient/externalchains/evm/event_parser.go @@ -125,7 +125,7 @@ func parseOutboundObservationEvent(log *types.Log, eventType string, logger zero } // Create OutboundEvent payload - payload := common.OutboundEvent{ + payload := common.OutboundObservation{ TxID: txID, UniversalTxID: universalTxID, Pc20WrapperAddress: wrapperAddr, @@ -168,7 +168,7 @@ func parseUniversalTxEvent(event *store.Event, log *types.Log, chainID string, l return } - payload := common.UniversalTx{ + payload := common.InboundObservation{ SourceChain: chainID, Sender: ethcommon.BytesToAddress(log.Topics[1].Bytes()).Hex(), Recipient: ethcommon.BytesToAddress(log.Topics[2].Bytes()).Hex(), @@ -215,7 +215,7 @@ func readWord(data []byte, i int) []byte { // decodePayload reads the raw payload bytes at the given offset and stores the hex string. // The core validator will decode the universal payload from these raw bytes. -func decodePayload(data []byte, dataOffset uint64, payload *common.UniversalTx, logger zerolog.Logger) { +func decodePayload(data []byte, dataOffset uint64, payload *common.InboundObservation, logger zerolog.Logger) { if dataOffset < uint64(32*5) { return } @@ -240,7 +240,7 @@ func decodeSignatureData(data []byte, w []byte, minOffset uint64) string { } // finalizeEvent marshals the payload and sets confirmation type on the event. -func finalizeEvent(event *store.Event, payload *common.UniversalTx, logger zerolog.Logger) { +func finalizeEvent(event *store.Event, payload *common.InboundObservation, logger zerolog.Logger) { if b, err := json.Marshal(payload); err == nil { event.EventData = b } else { @@ -266,7 +266,7 @@ UniversalTx Event (V2 - upgraded chains): - signatureData (bytes) — Word 5 (offset) - fromCEA (bool) — Word 6 */ -func parseUniversalTx(event *store.Event, log *types.Log, dataOffset uint64, payload *common.UniversalTx, logger zerolog.Logger) { +func parseUniversalTx(event *store.Event, log *types.Log, dataOffset uint64, payload *common.InboundObservation, logger zerolog.Logger) { data := log.Data decodePayload(data, dataOffset, payload, logger) diff --git a/universalClient/externalchains/evm/event_parser_test.go b/universalClient/externalchains/evm/event_parser_test.go index 9fb77e1e..88dbc878 100644 --- a/universalClient/externalchains/evm/event_parser_test.go +++ b/universalClient/externalchains/evm/event_parser_test.go @@ -337,7 +337,7 @@ func TestParseOutboundObservation_PC20Wrapper(t *testing.T) { wrapperOf := func(t *testing.T, e *store.Event) string { t.Helper() - var ob common.OutboundEvent + var ob common.OutboundObservation require.NoError(t, json.Unmarshal(e.EventData, &ob)) return ob.Pc20WrapperAddress } @@ -544,21 +544,21 @@ func TestDecodePayload(t *testing.T) { big.NewInt(int64(len(inner))).FillBytes(data[160:192]) copy(data[192:196], inner) - payload := &common.UniversalTx{} + payload := &common.InboundObservation{} decodePayload(data, 160, payload, logger) assert.Equal(t, "0xdeadbeef", payload.RawPayload) }) t.Run("offset too small is ignored", func(t *testing.T) { data := make([]byte, 256) - payload := &common.UniversalTx{} + payload := &common.InboundObservation{} decodePayload(data, 32, payload, logger) // < 32*5 assert.Empty(t, payload.RawPayload) }) t.Run("offset zero is ignored", func(t *testing.T) { data := make([]byte, 256) - payload := &common.UniversalTx{} + payload := &common.InboundObservation{} decodePayload(data, 0, payload, logger) assert.Empty(t, payload.RawPayload) }) @@ -566,7 +566,7 @@ func TestDecodePayload(t *testing.T) { t.Run("readDynamicBytes fails gracefully", func(t *testing.T) { // Data is too short for the length word at the offset data := make([]byte, 168) // offset 160 + only 8 bytes; need 32 for length - payload := &common.UniversalTx{} + payload := &common.InboundObservation{} decodePayload(data, 160, payload, logger) assert.Empty(t, payload.RawPayload) }) @@ -634,13 +634,13 @@ func TestFinalizeEvent(t *testing.T) { t.Run("txType 0 sets FAST confirmation", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{TxType: 0, Sender: "0xabc"} + payload := &common.InboundObservation{TxType: 0, Sender: "0xabc"} finalizeEvent(event, payload, logger) assert.Equal(t, store.ConfirmationFast, event.ConfirmationType) assert.NotNil(t, event.EventData) - var decoded common.UniversalTx + var decoded common.InboundObservation err := json.Unmarshal(event.EventData, &decoded) require.NoError(t, err) assert.Equal(t, "0xabc", decoded.Sender) @@ -648,7 +648,7 @@ func TestFinalizeEvent(t *testing.T) { t.Run("txType 1 sets FAST confirmation", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{TxType: 1} + payload := &common.InboundObservation{TxType: 1} finalizeEvent(event, payload, logger) assert.Equal(t, store.ConfirmationFast, event.ConfirmationType) @@ -656,7 +656,7 @@ func TestFinalizeEvent(t *testing.T) { t.Run("txType 2 sets STANDARD confirmation", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{TxType: 2} + payload := &common.InboundObservation{TxType: 2} finalizeEvent(event, payload, logger) assert.Equal(t, store.ConfirmationStandard, event.ConfirmationType) @@ -664,7 +664,7 @@ func TestFinalizeEvent(t *testing.T) { t.Run("txType 3 sets STANDARD confirmation", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{TxType: 3} + payload := &common.InboundObservation{TxType: 3} finalizeEvent(event, payload, logger) assert.Equal(t, store.ConfirmationStandard, event.ConfirmationType) @@ -672,7 +672,7 @@ func TestFinalizeEvent(t *testing.T) { t.Run("high txType sets STANDARD confirmation", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{TxType: 255} + payload := &common.InboundObservation{TxType: 255} finalizeEvent(event, payload, logger) assert.Equal(t, store.ConfirmationStandard, event.ConfirmationType) @@ -680,7 +680,7 @@ func TestFinalizeEvent(t *testing.T) { t.Run("event data is valid JSON", func(t *testing.T) { event := &store.Event{} - payload := &common.UniversalTx{ + payload := &common.InboundObservation{ SourceChain: "eip155:1", Sender: "0xsender", Recipient: "0xrecipient", @@ -690,7 +690,7 @@ func TestFinalizeEvent(t *testing.T) { } finalizeEvent(event, payload, logger) - var decoded common.UniversalTx + var decoded common.InboundObservation err := json.Unmarshal(event.EventData, &decoded) require.NoError(t, err) assert.Equal(t, "eip155:1", decoded.SourceChain) diff --git a/universalClient/externalchains/svm/event_parser.go b/universalClient/externalchains/svm/event_parser.go index 68f1dd4d..cc65ea4c 100644 --- a/universalClient/externalchains/svm/event_parser.go +++ b/universalClient/externalchains/svm/event_parser.go @@ -177,7 +177,7 @@ func parseOutboundObservationEvent(log string, signature string, slot uint64, lo } // Create OutboundEvent payload - payload := common.OutboundEvent{ + payload := common.OutboundObservation{ TxID: txID, UniversalTxID: universalTxID, GasFeeUsed: fmt.Sprintf("%d", gasUsed), @@ -250,7 +250,7 @@ func parseUniversalTxEvent(event *store.Event, decoded []byte, logIndex uint, ch } // decodeUniversalTxEvent decodes a TxWithFunds event -func decodeUniversalTxEvent(data []byte, logger zerolog.Logger) (*common.UniversalTx, error) { +func decodeUniversalTxEvent(data []byte, logger zerolog.Logger) (*common.InboundObservation, error) { if len(data) < 120 { logger.Warn(). Int("data_len", len(data)). @@ -258,7 +258,7 @@ func decodeUniversalTxEvent(data []byte, logger zerolog.Logger) (*common.Univers } offset := 8 - payload := &common.UniversalTx{} + payload := &common.InboundObservation{} // Parse sender (32 bytes) if len(data) < offset+32 { diff --git a/universalClient/externalchains/svm/event_parser_test.go b/universalClient/externalchains/svm/event_parser_test.go index 88ae4436..f7c6f060 100644 --- a/universalClient/externalchains/svm/event_parser_test.go +++ b/universalClient/externalchains/svm/event_parser_test.go @@ -307,7 +307,7 @@ func TestParseSendFundsEvent(t *testing.T) { assert.Equal(t, store.ConfirmationFast, event.ConfirmationType) // Unmarshal EventData - var utx common.UniversalTx + var utx common.InboundObservation require.NoError(t, json.Unmarshal(event.EventData, &utx)) assert.Equal(t, chainID, utx.SourceChain) @@ -344,7 +344,7 @@ func TestParseSendFundsEvent(t *testing.T) { data := buildSendFundsPayload(s, r, tok, 0, nil, rev, 0, nil, false) event := ParseEvent(wrapAsLog(data), sig, 1, 0, EventTypeSendFunds, chainID, logger) require.NotNil(t, event) - var utx common.UniversalTx + var utx common.InboundObservation require.NoError(t, json.Unmarshal(event.EventData, &utx)) assert.False(t, utx.FromCEA) }) @@ -355,7 +355,7 @@ func TestParseSendFundsEvent(t *testing.T) { data := buildSendFundsPayload(s, r, tok, 0, nil, rev, 0, nil, false) event := ParseEvent(wrapAsLog(data), sig, 1, 0, EventTypeSendFunds, chainID, logger) require.NotNil(t, event) - var utx common.UniversalTx + var utx common.InboundObservation require.NoError(t, json.Unmarshal(event.EventData, &utx)) assert.Empty(t, utx.RawPayload) assert.Empty(t, utx.VerificationData) @@ -368,7 +368,7 @@ func TestParseSendFundsEvent(t *testing.T) { data := buildSendFundsPayload(s, r, tok, maxU64, nil, rev, 0, nil, false) event := ParseEvent(wrapAsLog(data), sig, 1, 0, EventTypeSendFunds, chainID, logger) require.NotNil(t, event) - var utx common.UniversalTx + var utx common.InboundObservation require.NoError(t, json.Unmarshal(event.EventData, &utx)) assert.Equal(t, "18446744073709551615", utx.Amount) }) @@ -423,7 +423,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { assert.Equal(t, store.StatusPending, event.Status) assert.Equal(t, store.ConfirmationStandard, event.ConfirmationType) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Equal(t, "0x"+hex.EncodeToString(txID[:]), outbound.TxID) assert.Equal(t, "0x"+hex.EncodeToString(utxID[:]), outbound.UniversalTxID) @@ -439,7 +439,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 1, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Equal(t, solana.PublicKeyFromBytes(token[:]).String(), outbound.Pc20WrapperAddress) }) @@ -450,7 +450,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 1, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Empty(t, outbound.Pc20WrapperAddress) }) @@ -461,7 +461,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 1, 0, EventTypeRevertUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Empty(t, outbound.Pc20WrapperAddress) assert.Equal(t, "7777", outbound.GasFeeUsed) @@ -473,7 +473,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 1, 0, EventTypeFundsRescued, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Empty(t, outbound.Pc20WrapperAddress) assert.Equal(t, "3333", outbound.GasFeeUsed) @@ -514,7 +514,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 100, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Contains(t, outbound.TxID, "0x1111") assert.Contains(t, outbound.UniversalTxID, "0x2222") @@ -536,7 +536,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 100, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Equal(t, "0x"+hex.EncodeToString(txID[:]), outbound.TxID) assert.Equal(t, "0x"+hex.EncodeToString(utxID[:]), outbound.UniversalTxID) @@ -549,7 +549,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 100, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Equal(t, "0", outbound.GasFeeUsed) }) @@ -560,7 +560,7 @@ func TestParseOutboundObservationEvent(t *testing.T) { event := ParseEvent(wrapAsLog(data), signature, 100, 0, EventTypeFinalizeUniversalTx, chainID, logger) require.NotNil(t, event) - var outbound common.OutboundEvent + var outbound common.OutboundObservation require.NoError(t, json.Unmarshal(event.EventData, &outbound)) assert.Equal(t, "18446744073709551615", outbound.GasFeeUsed) }) From 3afc24c293a0e413d39b4df4c592c910be609daa Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 4 Aug 2026 12:33:14 +0530 Subject: [PATCH 21/54] add: web2 read handler --- .../externalchains/common/types.go | 12 +- .../externalchains/web2/read_envelope.go | 140 ++++++++ .../externalchains/web2/read_executor.go | 324 +++++++++++++++++ .../externalchains/web2/read_executor_test.go | 330 ++++++++++++++++++ 4 files changed, 800 insertions(+), 6 deletions(-) create mode 100644 universalClient/externalchains/web2/read_envelope.go create mode 100644 universalClient/externalchains/web2/read_executor.go create mode 100644 universalClient/externalchains/web2/read_executor_test.go diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index 54a26cee..0dfc59be 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -9,12 +9,6 @@ import ( uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) -// ReadRequestHandler executes a read request on one destination chain. -// Consumed by the push watcher's read processor. -type ReadRequestHandler interface { - ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) -} - // EncodeUint256Result canonically encodes a balance/amount as abi.encode(uint256) // so read results are byte-identical across validators and decodable by the // requesting contract. The bounds check guards against a malicious RPC value @@ -119,3 +113,9 @@ type TxBuilder interface { // BroadcastFundMigrationTx assembles and broadcasts a signed fund migration transaction. BroadcastFundMigrationTx(ctx context.Context, req *UnsignedSigningReq, data *FundMigrationData, signature []byte) (string, error) } + +// ReadRequestHandler executes a read request on one destination chain. +// Consumed by the push watcher's read processor. +type ReadRequestHandler interface { + ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) +} diff --git a/universalClient/externalchains/web2/read_envelope.go b/universalClient/externalchains/web2/read_envelope.go new file mode 100644 index 00000000..b56d1048 --- /dev/null +++ b/universalClient/externalchains/web2/read_envelope.go @@ -0,0 +1,140 @@ +package web2 + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +// web2Method mirrors the Web2QueryEnvelope method enum from the read spec. +type web2Method uint8 + +const ( + web2MethodGet web2Method = 0 + web2MethodPost web2Method = 1 +) + +// extractValueType mirrors the Web2Extract valueType enum. +type extractValueType uint8 + +const ( + valueTypeUint256 extractValueType = 0 + valueTypeInt256 extractValueType = 1 + valueTypeBool extractValueType = 2 + valueTypeString extractValueType = 3 + valueTypeBytes extractValueType = 4 +) + +// extractMode mirrors the Web2Extract mode enum. +type extractMode uint8 + +// modeIdentical is the only supported aggregation mode: quorum on identical +// result bytes. More modes (e.g. median) need core-side aggregation first. +const modeIdentical extractMode = 0 + +// web2Extract is one declared field to pull out of the JSON response. +type web2Extract struct { + Path string // JSONPath into the response, e.g. "$.data.price" + ValueType extractValueType + Mode extractMode + Decimals uint8 // numeric JSON scaled by 10^decimals before encoding +} + +// web2QueryEnvelope is the decoded abi.encode(Web2QueryEnvelope) query. +type web2QueryEnvelope struct { + Method web2Method + URL string + Headers []byte // canonical JSON object of header name -> value + Body []byte // POST only + TimeoutMs uint64 + Extract []web2Extract +} + +var web2EnvelopeArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "method", Type: "uint8"}, + {Name: "url", Type: "string"}, + {Name: "headers", Type: "bytes"}, + {Name: "body", Type: "bytes"}, + {Name: "timeoutMs", Type: "uint64"}, + {Name: "extract", Type: "tuple[]", Components: []abi.ArgumentMarshaling{ + {Name: "path", Type: "string"}, + {Name: "valueType", Type: "uint8"}, + {Name: "mode", Type: "uint8"}, + {Name: "decimals", Type: "uint8"}, + }}, +}}) + +func mustReadArgs(marshalings ...abi.ArgumentMarshaling) abi.Arguments { + args := make(abi.Arguments, 0, len(marshalings)) + for i, m := range marshalings { + if m.Name == "" { + m.Name = fmt.Sprintf("arg%d", i) + } + typ, err := abi.NewType(m.Type, "", m.Components) + if err != nil { + panic(fmt.Sprintf("web2: invalid abi type %q: %v", m.Type, err)) + } + args = append(args, abi.Argument{Name: m.Name, Type: typ}) + } + return args +} + +type rawWeb2Extract struct { + Path string + ValueType uint8 + Mode uint8 + Decimals uint8 +} + +type rawWeb2Envelope struct { + Method uint8 + Url string + Headers []byte + Body []byte + TimeoutMs uint64 + Extract []rawWeb2Extract +} + +// decodeWeb2QueryEnvelope decodes ReadSpec.query for web2 destinations. +func decodeWeb2QueryEnvelope(query []byte) (*web2QueryEnvelope, error) { + vals, err := web2EnvelopeArgs.Unpack(query) + if err != nil { + return nil, fmt.Errorf("failed to unpack Web2QueryEnvelope: %w", err) + } + raw := *abi.ConvertType(vals[0], new(rawWeb2Envelope)).(*rawWeb2Envelope) + + env := &web2QueryEnvelope{ + Method: web2Method(raw.Method), + URL: raw.Url, + Headers: raw.Headers, + Body: raw.Body, + TimeoutMs: raw.TimeoutMs, + } + for _, e := range raw.Extract { + env.Extract = append(env.Extract, web2Extract{ + Path: e.Path, + ValueType: extractValueType(e.ValueType), + Mode: extractMode(e.Mode), + Decimals: e.Decimals, + }) + } + + if env.Method > web2MethodPost { + return nil, fmt.Errorf("unknown web2 method %d", env.Method) + } + if len(env.Extract) == 0 { + return nil, fmt.Errorf("envelope has no extract entries") + } + if len(env.Extract) > maxExtractEntries { + return nil, fmt.Errorf("envelope has %d extract entries, max %d", len(env.Extract), maxExtractEntries) + } + for _, e := range env.Extract { + if e.ValueType > valueTypeBytes { + return nil, fmt.Errorf("unknown extract value type %d", e.ValueType) + } + if e.Mode != modeIdentical { + return nil, fmt.Errorf("unsupported extract mode %d, only IDENTICAL", e.Mode) + } + } + return env, nil +} diff --git a/universalClient/externalchains/web2/read_executor.go b/universalClient/externalchains/web2/read_executor.go new file mode 100644 index 00000000..f5d92448 --- /dev/null +++ b/universalClient/externalchains/web2/read_executor.go @@ -0,0 +1,324 @@ +// Package web2 executes web2 (HTTP) read requests: it fetches the declared +// endpoint, extracts the declared JSON fields, and canonically encodes them so +// read results are byte-identical across validators. +package web2 + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "net/http" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/rs/zerolog" + + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +const ( + // DestinationPrefix identifies web2 read destinations, e.g. "web2:https". + DestinationPrefix = "web2:" + + maxResponseBytes = 64 * 1024 + maxExtractEntries = 16 + defaultTimeout = 5 * time.Second + maxTimeout = 15 * time.Second +) + +// Executor implements common.ReadRequestHandler for web2 destinations. +type Executor struct { + httpClient *http.Client + logger zerolog.Logger + // allowInsecureURL disables the https-only rule (tests only) + allowInsecureURL bool +} + +// NewExecutor creates a web2 read executor. +func NewExecutor(logger zerolog.Logger) *Executor { + return &Executor{ + httpClient: &http.Client{Timeout: maxTimeout}, + logger: logger.With().Str("component", "web2_read_executor").Logger(), + } +} + +// ExecuteRead fetches the endpoint declared in the envelope, extracts the +// declared fields, and abi-encodes them in extract order. Deterministic +// failures (bad envelope, non-JSON response, missing path, 4xx) are votable +// ERROR observations; transport failures and 5xx are transient errors. +func (e *Executor) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { + env, err := decodeWeb2QueryEnvelope(req.Query) + if err != nil { + return uread.NewErrorResult(err), nil + } + + if err := e.validateEnvelope(env); err != nil { + return uread.NewErrorResult(err), nil + } + + body, errResult, err := e.fetch(ctx, env) + if err != nil { + return nil, err // transient + } + if errResult != nil { + return errResult, nil + } + + resultData, err := extractAndEncode(body, env.Extract) + if err != nil { + return uread.NewErrorResult(err), nil + } + + // web2 has no block height or hash; the ballot covers result data only + return &uread.ReadResult{ + Status: uread.ReadStatusSuccess, + ResultData: resultData, + }, nil +} + +// validateEnvelope enforces the v1 request constraints. +func (e *Executor) validateEnvelope(env *web2QueryEnvelope) error { + if !e.allowInsecureURL && !strings.HasPrefix(env.URL, "https://") { + return fmt.Errorf("url must be https") + } + if env.Method == web2MethodGet && len(env.Body) > 0 { + return fmt.Errorf("GET request must not have a body") + } + return nil +} + +// fetch performs the HTTP request. Returns (body, nil, nil) on success, +// (nil, errorResult, nil) on deterministic failure, (nil, nil, err) on +// transient failure. +func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, *uread.ReadResult, error) { + timeout := defaultTimeout + if env.TimeoutMs > 0 { + timeout = min(time.Duration(env.TimeoutMs)*time.Millisecond, maxTimeout) + } + reqCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + method := http.MethodGet + var reqBody io.Reader + if env.Method == web2MethodPost { + method = http.MethodPost + reqBody = bytes.NewReader(env.Body) + } + + httpReq, err := http.NewRequestWithContext(reqCtx, method, env.URL, reqBody) + if err != nil { + return nil, uread.NewErrorResult(fmt.Errorf("invalid request: %w", err)), nil + } + + if len(env.Headers) > 0 { + var headers map[string]string + if err := json.Unmarshal(env.Headers, &headers); err != nil { + return nil, uread.NewErrorResult(fmt.Errorf("invalid headers encoding: %w", err)), nil + } + for name, value := range headers { + httpReq.Header.Set(name, value) + } + } + + resp, err := e.httpClient.Do(httpReq) + if err != nil { + return nil, nil, fmt.Errorf("request failed: %w", err) // transient + } + defer func() { _ = resp.Body.Close() }() + + // 4xx is a deterministic answer from the endpoint; 5xx is the endpoint + // having a bad moment + if resp.StatusCode >= 500 { + return nil, nil, fmt.Errorf("endpoint returned status %d", resp.StatusCode) + } + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, uread.NewErrorResult(fmt.Errorf("endpoint returned status %d", resp.StatusCode)), nil + } + + body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1)) + if err != nil { + return nil, nil, fmt.Errorf("failed to read response: %w", err) // transient + } + if len(body) > maxResponseBytes { + return nil, uread.NewErrorResult(fmt.Errorf("response exceeds %d bytes", maxResponseBytes)), nil + } + + return body, nil, nil +} + +// extractAndEncode applies each extract spec to the JSON response and +// abi-encodes the values in extract order. +func extractAndEncode(body []byte, extracts []web2Extract) ([]byte, error) { + decoder := json.NewDecoder(bytes.NewReader(body)) + decoder.UseNumber() + var root any + if err := decoder.Decode(&root); err != nil { + return nil, fmt.Errorf("response is not valid JSON: %w", err) + } + + args := make(abi.Arguments, 0, len(extracts)) + values := make([]any, 0, len(extracts)) + for _, ex := range extracts { + raw, err := resolveJSONPath(root, ex.Path) + if err != nil { + return nil, err + } + + value, abiType, err := convertValue(raw, ex) + if err != nil { + return nil, fmt.Errorf("path %s: %w", ex.Path, err) + } + args = append(args, abi.Argument{Name: "v", Type: abiType}) + values = append(values, value) + } + + encoded, err := args.Pack(values...) + if err != nil { + return nil, fmt.Errorf("failed to encode result: %w", err) + } + return encoded, nil +} + +var ( + abiUint256, _ = abi.NewType("uint256", "", nil) + abiInt256, _ = abi.NewType("int256", "", nil) + abiBool, _ = abi.NewType("bool", "", nil) + abiString, _ = abi.NewType("string", "", nil) + abiBytes, _ = abi.NewType("bytes", "", nil) +) + +// convertValue converts a JSON value to the declared abi value. +func convertValue(raw any, ex web2Extract) (any, abi.Type, error) { + switch ex.ValueType { + case valueTypeUint256, valueTypeInt256: + num, err := scaledInteger(raw, ex.Decimals) + if err != nil { + return nil, abi.Type{}, err + } + if ex.ValueType == valueTypeUint256 { + if num.Sign() < 0 || num.BitLen() > 256 { + return nil, abi.Type{}, fmt.Errorf("value out of uint256 range") + } + return num, abiUint256, nil + } + if num.BitLen() > 255 { + return nil, abi.Type{}, fmt.Errorf("value out of int256 range") + } + return num, abiInt256, nil + + case valueTypeBool: + b, ok := raw.(bool) + if !ok { + return nil, abi.Type{}, fmt.Errorf("expected bool, got %T", raw) + } + return b, abiBool, nil + + case valueTypeString: + s, ok := raw.(string) + if !ok { + return nil, abi.Type{}, fmt.Errorf("expected string, got %T", raw) + } + return s, abiString, nil + + case valueTypeBytes: + s, ok := raw.(string) + if !ok || !strings.HasPrefix(s, "0x") { + return nil, abi.Type{}, fmt.Errorf("expected 0x-prefixed hex string") + } + decoded, err := hex.DecodeString(strings.TrimPrefix(s, "0x")) + if err != nil { + return nil, abi.Type{}, fmt.Errorf("invalid hex: %w", err) + } + return decoded, abiBytes, nil + + default: + return nil, abi.Type{}, fmt.Errorf("unknown value type %d", ex.ValueType) + } +} + +// scaledInteger parses a JSON number (or numeric string), scales it by +// 10^decimals, and truncates to an integer. big.Rat keeps float-formatted +// JSON exact (e.g. "3512.4471" with 8 decimals -> 351244710000). +func scaledInteger(raw any, decimals uint8) (*big.Int, error) { + var numStr string + switch v := raw.(type) { + case json.Number: + numStr = v.String() + case string: + numStr = v + default: + return nil, fmt.Errorf("expected number, got %T", raw) + } + + rat, ok := new(big.Rat).SetString(numStr) + if !ok { + return nil, fmt.Errorf("invalid number %q", numStr) + } + + scale := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil) + rat.Mul(rat, new(big.Rat).SetInt(scale)) + + return new(big.Int).Quo(rat.Num(), rat.Denom()), nil +} + +// resolveJSONPath resolves a minimal JSONPath subset: "$" root, dot fields and +// array indexes, e.g. "$.data.items[0].price". +func resolveJSONPath(root any, path string) (any, error) { + if !strings.HasPrefix(path, "$") { + return nil, fmt.Errorf("path %s must start with $", path) + } + + current := root + rest := strings.TrimPrefix(path, "$") + for _, segment := range strings.Split(rest, ".") { + if segment == "" { + continue + } + + field := segment + var indexes []int + for strings.HasSuffix(field, "]") { + open := strings.LastIndex(field, "[") + if open < 0 { + return nil, fmt.Errorf("path %s has malformed index in %q", path, segment) + } + idx, err := strconv.Atoi(field[open+1 : len(field)-1]) + if err != nil || idx < 0 { + return nil, fmt.Errorf("path %s has invalid index in %q", path, segment) + } + indexes = append([]int{idx}, indexes...) + field = field[:open] + } + + if field != "" { + obj, ok := current.(map[string]any) + if !ok { + return nil, fmt.Errorf("path %s: %q is not an object", path, field) + } + value, ok := obj[field] + if !ok { + return nil, fmt.Errorf("path %s: field %q not found", path, field) + } + current = value + } + + for _, idx := range indexes { + arr, ok := current.([]any) + if !ok { + return nil, fmt.Errorf("path %s: indexing into non-array", path) + } + if idx >= len(arr) { + return nil, fmt.Errorf("path %s: index %d out of range", path, idx) + } + current = arr[idx] + } + } + + return current, nil +} diff --git a/universalClient/externalchains/web2/read_executor_test.go b/universalClient/externalchains/web2/read_executor_test.go new file mode 100644 index 00000000..0ee27d02 --- /dev/null +++ b/universalClient/externalchains/web2/read_executor_test.go @@ -0,0 +1,330 @@ +package web2 + +import ( + "context" + "encoding/json" + "math/big" + "net/http" + "net/http/httptest" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/universalClient/uread" +) + +func packEnvelope(t *testing.T, env rawWeb2Envelope) []byte { + t.Helper() + data, err := web2EnvelopeArgs.Pack(env) + require.NoError(t, err) + return data +} + +func extractSpec(path string, valueType extractValueType, decimals uint8) rawWeb2Extract { + return rawWeb2Extract{Path: path, ValueType: uint8(valueType), Mode: uint8(modeIdentical), Decimals: decimals} +} + +func newTestExecutor(t *testing.T, handler http.HandlerFunc) (*Executor, string) { + t.Helper() + srv := httptest.NewServer(handler) + t.Cleanup(srv.Close) + + e := NewExecutor(zerolog.Nop()) + e.allowInsecureURL = true // httptest serves plain http + return e, srv.URL +} + +func jsonHandler(t *testing.T, wantMethod string, response any) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, wantMethod, r.Method) + require.NoError(t, json.NewEncoder(w).Encode(response)) + } +} + +func web2Request(t *testing.T, env rawWeb2Envelope) *uread.ReadRequest { + t.Helper() + return &uread.ReadRequest{ + RequestID: "0xreq1", + DestinationChain: "web2:https", + Query: packEnvelope(t, env), + } +} + +func TestExecuteRead_GetIdenticalFields(t *testing.T) { + e, url := newTestExecutor(t, jsonHandler(t, http.MethodGet, map[string]any{ + "status": "FINAL", + "winner": "TeamA", + "score": map[string]any{"a": 3, "b": 1}, + })) + + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{ + extractSpec("$.status", valueTypeString, 0), + extractSpec("$.winner", valueTypeString, 0), + extractSpec("$.score.a", valueTypeUint256, 0), + }, + }) + + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + require.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Zero(t, result.ObservedBlockHeight) + + stringTy, _ := abi.NewType("string", "", nil) + uintTy, _ := abi.NewType("uint256", "", nil) + args := abi.Arguments{{Type: stringTy}, {Type: stringTy}, {Type: uintTy}} + vals, err := args.Unpack(result.ResultData) + require.NoError(t, err) + assert.Equal(t, "FINAL", vals[0]) + assert.Equal(t, "TeamA", vals[1]) + assert.Equal(t, big.NewInt(3), vals[2]) +} + +func TestExecuteRead_DecimalScaling(t *testing.T) { + e, url := newTestExecutor(t, jsonHandler(t, http.MethodGet, map[string]any{ + "price": 3512.4471, + })) + + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.price", valueTypeUint256, 8)}, + }) + + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + require.Equal(t, uread.ReadStatusSuccess, result.Status) + + assert.Equal(t, big.NewInt(351244710000), new(big.Int).SetBytes(result.ResultData)) +} + +func TestExecuteRead_PostBody(t *testing.T) { + e, url := newTestExecutor(t, func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method) + assert.Equal(t, "application/json", r.Header.Get("Content-Type")) + var body map[string]string + require.NoError(t, json.NewDecoder(r.Body).Decode(&body)) + assert.Equal(t, "{ token { decimals } }", body["query"]) + require.NoError(t, json.NewEncoder(w).Encode(map[string]any{ + "data": map[string]any{"token": map[string]any{"decimals": 18}}, + })) + }) + + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodPost), + Url: url, + Headers: []byte(`{"content-type":"application/json"}`), + Body: []byte(`{"query":"{ token { decimals } }"}`), + Extract: []rawWeb2Extract{extractSpec("$.data.token.decimals", valueTypeUint256, 0)}, + }) + + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + require.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, big.NewInt(18), new(big.Int).SetBytes(result.ResultData)) +} + +func TestExecuteRead_ArrayIndexPath(t *testing.T) { + e, url := newTestExecutor(t, jsonHandler(t, http.MethodGet, map[string]any{ + "items": []any{map[string]any{"ok": true}, map[string]any{"ok": false}}, + })) + + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.items[1].ok", valueTypeBool, 0)}, + }) + + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + require.Equal(t, uread.ReadStatusSuccess, result.Status) + + boolTy, _ := abi.NewType("bool", "", nil) + vals, err := abi.Arguments{{Type: boolTy}}.Unpack(result.ResultData) + require.NoError(t, err) + assert.Equal(t, false, vals[0]) +} + +func TestExecuteRead_VotableErrors(t *testing.T) { + t.Run("invalid envelope", func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + result, err := e.ExecuteRead(context.Background(), &uread.ReadRequest{Query: []byte{0x01}}) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("non-https url", func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: "http://insecure.example.com", + Extract: []rawWeb2Extract{extractSpec("$.x", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("non-identical mode not supported", func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: "https://api.example.com", + Extract: []rawWeb2Extract{ + {Path: "$.price", ValueType: uint8(valueTypeUint256), Mode: 1, Decimals: 8}, + }, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("GET with body", func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: "https://api.example.com", + Body: []byte("nope"), + Extract: []rawWeb2Extract{extractSpec("$.x", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("missing path", func(t *testing.T) { + e, url := newTestExecutor(t, jsonHandler(t, http.MethodGet, map[string]any{"a": 1})) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.missing", valueTypeUint256, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("type mismatch", func(t *testing.T) { + e, url := newTestExecutor(t, jsonHandler(t, http.MethodGet, map[string]any{"a": "text"})) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeBool, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("non-JSON response", func(t *testing.T) { + e, url := newTestExecutor(t, func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("not json")) + }) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + + t.Run("404 status", func(t *testing.T) { + e, url := newTestExecutor(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + }) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) + assert.Equal(t, uread.ReadStatusError, result.Status) + }) +} + +func TestExecuteRead_TransientErrors(t *testing.T) { + t.Run("500 status", func(t *testing.T) { + e, url := newTestExecutor(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + }) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.Error(t, err) + assert.Nil(t, result) + }) + + t.Run("unreachable endpoint", func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + e.allowInsecureURL = true + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: "http://127.0.0.1:1", + TimeoutMs: 500, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.Error(t, err) + assert.Nil(t, result) + }) +} + +func TestScaledInteger(t *testing.T) { + cases := []struct { + in string + decimals uint8 + want string + }{ + {"3512.4471", 8, "351244710000"}, + {"100", 0, "100"}, + {"0.5", 2, "50"}, + {"1.999", 0, "1"}, // truncates + {"-2.5", 1, "-25"}, + } + for _, tc := range cases { + got, err := scaledInteger(json.Number(tc.in), tc.decimals) + require.NoError(t, err, tc.in) + assert.Equal(t, tc.want, got.String(), tc.in) + } + + _, err := scaledInteger(json.Number("not-a-number"), 0) + assert.Error(t, err) + _, err = scaledInteger(true, 0) + assert.Error(t, err) +} + +func TestDecodeWeb2QueryEnvelope_Invalid(t *testing.T) { + t.Run("garbage bytes", func(t *testing.T) { + _, err := decodeWeb2QueryEnvelope([]byte{0x01, 0x02}) + assert.Error(t, err) + }) + + t.Run("no extract entries", func(t *testing.T) { + data, err := web2EnvelopeArgs.Pack(rawWeb2Envelope{Method: 0, Url: "https://x"}) + require.NoError(t, err) + _, err = decodeWeb2QueryEnvelope(data) + assert.Error(t, err) + }) + + t.Run("unknown method", func(t *testing.T) { + data, err := web2EnvelopeArgs.Pack(rawWeb2Envelope{ + Method: 9, + Url: "https://x", + Extract: []rawWeb2Extract{{Path: "$.a"}}, + }) + require.NoError(t, err) + _, err = decodeWeb2QueryEnvelope(data) + assert.Error(t, err) + }) +} From 6d6f773e532b5192f23f92a9c3276f9e9c8d380a Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 4 Aug 2026 12:47:11 +0530 Subject: [PATCH 22/54] fix: harden reads --- .../externalchains/web2/read_executor.go | 102 +++++++++++++++++- .../externalchains/web2/read_executor_test.go | 70 ++++++++++++ 2 files changed, 168 insertions(+), 4 deletions(-) diff --git a/universalClient/externalchains/web2/read_executor.go b/universalClient/externalchains/web2/read_executor.go index f5d92448..f8063fd5 100644 --- a/universalClient/externalchains/web2/read_executor.go +++ b/universalClient/externalchains/web2/read_executor.go @@ -8,9 +8,11 @@ import ( "context" "encoding/hex" "encoding/json" + "errors" "fmt" "io" "math/big" + "net" "net/http" "strconv" "strings" @@ -30,8 +32,21 @@ const ( maxExtractEntries = 16 defaultTimeout = 5 * time.Second maxTimeout = 15 * time.Second + maxRedirects = 5 ) +// errBlockedRequest marks a request rejected by the SSRF guard (private/internal +// address, disallowed redirect). It is deterministic: every honest validator +// rejects the same envelope identically, so it becomes a votable ERROR rather +// than a transient retry. +var errBlockedRequest = errors.New("request blocked by ssrf guard") + +// TODO(core): a web2 read makes every validator fetch an attacker-chosen URL, +// so the fee is the only thing pricing that outbound work. The fee must NEVER +// be fully refunded on failure or no-quorum: a full refund lets an attacker +// drive the whole validator set at any endpoint for only tx gas (griefing / +// reflected load). Charge for execution regardless of read outcome. + // Executor implements common.ReadRequestHandler for web2 destinations. type Executor struct { httpClient *http.Client @@ -40,12 +55,86 @@ type Executor struct { allowInsecureURL bool } -// NewExecutor creates a web2 read executor. +// NewExecutor creates a web2 read executor. Its HTTP client dials through an +// SSRF guard that blocks private/internal addresses on the initial request and +// on every redirect hop, and only connects to the exact IP it vetted (so DNS +// rebinding cannot swap in an internal address between check and dial). func NewExecutor(logger zerolog.Logger) *Executor { - return &Executor{ - httpClient: &http.Client{Timeout: maxTimeout}, - logger: logger.With().Str("component", "web2_read_executor").Logger(), + e := &Executor{ + logger: logger.With().Str("component", "web2_read_executor").Logger(), + } + e.httpClient = &http.Client{ + Timeout: maxTimeout, + Transport: &http.Transport{DialContext: e.dialContext}, + CheckRedirect: e.checkRedirect, + } + return e +} + +// dialContext resolves the target host and refuses any non-public address, then +// dials the vetted IP directly. Tests set allowInsecureURL to reach httptest +// servers on loopback. +func (e *Executor) dialContext(ctx context.Context, network, addr string) (net.Conn, error) { + if e.allowInsecureURL { + return (&net.Dialer{}).DialContext(ctx, network, addr) + } + + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + ips, err := net.DefaultResolver.LookupIPAddr(ctx, host) + if err != nil { + return nil, err + } + if len(ips) == 0 { + return nil, fmt.Errorf("no addresses for %s", host) + } + for _, ip := range ips { + if isDisallowedIP(ip.IP) { + return nil, fmt.Errorf("%w: %s resolves to non-public address %s", errBlockedRequest, host, ip.IP) + } } + + dialer := &net.Dialer{} + var lastErr error + for _, ip := range ips { + conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ip.IP.String(), port)) + if err != nil { + lastErr = err + continue + } + return conn, nil + } + return nil, lastErr +} + +// checkRedirect keeps redirects https-only and bounded. The dialer still vets +// every hop's address; this only rejects scheme downgrades and redirect loops. +func (e *Executor) checkRedirect(req *http.Request, via []*http.Request) error { + if len(via) >= maxRedirects { + return fmt.Errorf("%w: too many redirects", errBlockedRequest) + } + if !e.allowInsecureURL && req.URL.Scheme != "https" { + return fmt.Errorf("%w: redirect to non-https url", errBlockedRequest) + } + return nil +} + +// isDisallowedIP reports whether an IP is one the guard must never connect to: +// loopback, private (RFC1918 / ULA), link-local (incl. 169.254.169.254 cloud +// metadata), carrier-grade NAT, multicast, or the unspecified address. +func isDisallowedIP(ip net.IP) bool { + if ip.IsLoopback() || ip.IsPrivate() || ip.IsUnspecified() || + ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsMulticast() { + return true + } + // Carrier-grade NAT 100.64.0.0/10 (RFC 6598) is not covered by IsPrivate. + if v4 := ip.To4(); v4 != nil && v4[0] == 100 && v4[1]&0xc0 == 64 { + return true + } + return false } // ExecuteRead fetches the endpoint declared in the envelope, extracts the @@ -128,6 +217,11 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * resp, err := e.httpClient.Do(httpReq) if err != nil { + // A guard rejection is the same for every validator: votable ERROR. + // Any other transport error may be transient. + if errors.Is(err, errBlockedRequest) { + return nil, uread.NewErrorResult(fmt.Errorf("request blocked: %w", err)), nil + } return nil, nil, fmt.Errorf("request failed: %w", err) // transient } defer func() { _ = resp.Body.Close() }() diff --git a/universalClient/externalchains/web2/read_executor_test.go b/universalClient/externalchains/web2/read_executor_test.go index 0ee27d02..d13c8022 100644 --- a/universalClient/externalchains/web2/read_executor_test.go +++ b/universalClient/externalchains/web2/read_executor_test.go @@ -3,7 +3,9 @@ package web2 import ( "context" "encoding/json" + "errors" "math/big" + "net" "net/http" "net/http/httptest" "testing" @@ -280,6 +282,74 @@ func TestExecuteRead_TransientErrors(t *testing.T) { }) } +func TestExecuteRead_SSRFGuard(t *testing.T) { + // guard is active only when allowInsecureURL is false + blocked := []string{ + "https://127.0.0.1/x", + "https://[::1]/x", + "https://169.254.169.254/latest/meta-data/", + "https://10.0.0.1/x", + "https://192.168.1.1/x", + "https://172.16.0.1/x", + "https://100.64.0.1/x", + "https://0.0.0.0/x", + } + for _, target := range blocked { + t.Run(target, func(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: target, + TimeoutMs: 1000, + Extract: []rawWeb2Extract{extractSpec("$.x", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.NoError(t, err) // deterministic, not transient + assert.Equal(t, uread.ReadStatusError, result.Status) + }) + } +} + +func TestCheckRedirect(t *testing.T) { + e := NewExecutor(zerolog.Nop()) + + mustReq := func(rawURL string) *http.Request { + r, err := http.NewRequest(http.MethodGet, rawURL, nil) + require.NoError(t, err) + return r + } + + // https redirect within the hop budget is allowed + assert.NoError(t, e.checkRedirect(mustReq("https://example.com/next"), make([]*http.Request, 1))) + + // scheme downgrade to http is blocked + err := e.checkRedirect(mustReq("http://example.com/next"), make([]*http.Request, 1)) + require.Error(t, err) + assert.True(t, errors.Is(err, errBlockedRequest)) + + // too many redirects is blocked + err = e.checkRedirect(mustReq("https://example.com/next"), make([]*http.Request, maxRedirects)) + require.Error(t, err) + assert.True(t, errors.Is(err, errBlockedRequest)) +} + +func TestIsDisallowedIP(t *testing.T) { + disallowed := []string{ + "127.0.0.1", "::1", "10.1.2.3", "172.16.5.5", "192.168.0.1", + "169.254.169.254", "100.64.0.1", "0.0.0.0", "fe80::1", "fc00::1", "224.0.0.1", + // IPv4-mapped IPv6 must not slip past the v4-range checks + "::ffff:127.0.0.1", "::ffff:169.254.169.254", "::ffff:10.0.0.1", + } + for _, s := range disallowed { + assert.True(t, isDisallowedIP(net.ParseIP(s)), "%s should be blocked", s) + } + + allowed := []string{"8.8.8.8", "1.1.1.1", "93.184.216.34", "2606:2800:220:1::1", "100.63.255.255", "100.128.0.1"} + for _, s := range allowed { + assert.False(t, isDisallowedIP(net.ParseIP(s)), "%s should be allowed", s) + } +} + func TestScaledInteger(t *testing.T) { cases := []struct { in string From ab74a70bd30083e718780a6518be64e97415d836 Mon Sep 17 00:00:00 2001 From: aman035 Date: Tue, 4 Aug 2026 12:47:56 +0530 Subject: [PATCH 23/54] plug web2 read executor --- universalClient/pushwatcher/client.go | 3 +- .../pushwatcher/read_event_processor.go | 51 ++++++++++++------- .../pushwatcher/read_event_processor_test.go | 39 +++++++++++++- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/universalClient/pushwatcher/client.go b/universalClient/pushwatcher/client.go index 297246b2..875400f8 100644 --- a/universalClient/pushwatcher/client.go +++ b/universalClient/pushwatcher/client.go @@ -9,6 +9,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/externalchains/web2" "github.com/pushchain/push-chain-node/universalClient/pushcore" "github.com/pushchain/push-chain-node/universalClient/pushsigner" "github.com/pushchain/push-chain-node/universalClient/store" @@ -79,7 +80,7 @@ func NewClient( // READ_REQUEST events are executed on their destination chains (via // chainResolver) and the results voted back. if pushSigner != nil && chainResolver != nil { - readEventProcessor, err := NewReadEventProcessor(pushSigner, chainResolver, database, logger) + readEventProcessor, err := NewReadEventProcessor(pushSigner, chainResolver, web2.NewExecutor(logger), database, logger) if err != nil { return nil, fmt.Errorf("failed to create read event processor: %w", err) } diff --git a/universalClient/pushwatcher/read_event_processor.go b/universalClient/pushwatcher/read_event_processor.go index 1544c91e..9d99edd7 100644 --- a/universalClient/pushwatcher/read_event_processor.go +++ b/universalClient/pushwatcher/read_event_processor.go @@ -3,9 +3,12 @@ package pushwatcher import ( "context" "encoding/json" + "fmt" + "strings" "github.com/pushchain/push-chain-node/universalClient/db" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + "github.com/pushchain/push-chain-node/universalClient/externalchains/web2" "github.com/pushchain/push-chain-node/universalClient/store" "github.com/pushchain/push-chain-node/universalClient/uread" "github.com/rs/zerolog" @@ -29,16 +32,19 @@ type readVoter interface { // the event CONFIRMED for retry; corrupt events flip to REVERTED. Expiry is // core's job: expired requests leave the pending query. type ReadEventProcessor struct { - voter readVoter - resolver ChainResolver - chainStore *common.ChainStore - logger zerolog.Logger + voter readVoter + resolver ChainResolver + web2Handler common.ReadRequestHandler + chainStore *common.ChainStore + logger zerolog.Logger } // NewReadEventProcessor creates the handler for READ_REQUEST events. +// web2Handler serves web2 destinations; nil means web2 reads are not served. func NewReadEventProcessor( voter readVoter, resolver ChainResolver, + web2Handler common.ReadRequestHandler, database *db.DB, logger zerolog.Logger, ) (*ReadEventProcessor, error) { @@ -47,10 +53,11 @@ func NewReadEventProcessor( } return &ReadEventProcessor{ - voter: voter, - resolver: resolver, - chainStore: common.NewChainStore(database), - logger: logger.With().Str("component", "push_read_event_processor").Logger(), + voter: voter, + resolver: resolver, + web2Handler: web2Handler, + chainStore: common.NewChainStore(database), + logger: logger.With().Str("component", "push_read_event_processor").Logger(), }, nil } @@ -70,17 +77,10 @@ func (p *ReadEventProcessor) HandleEvent(ctx context.Context, event *store.Event log := p.logger.With().Str("request_id", req.RequestID).Logger() - destClient, err := p.resolver.GetClient(req.DestinationChain) + handler, err := p.resolveHandler(req.DestinationChain) if err != nil { // destination not served by this validator yet; retry next tick - log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("destination chain not served") - return nil - } - - handler, err := destClient.GetReadRequestHandler() - if err != nil { - // destination client not ready to serve reads yet; retry next tick - log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("read handler not available") + log.Debug().Err(err).Str("destination_chain", req.DestinationChain).Msg("destination not served") return nil } @@ -114,6 +114,23 @@ func (p *ReadEventProcessor) HandleEvent(ctx context.Context, event *store.Event return nil } +// resolveHandler returns the read handler for a destination: the web2 executor +// for web2 destinations, otherwise the destination chain client's handler. +func (p *ReadEventProcessor) resolveHandler(destination string) (common.ReadRequestHandler, error) { + if strings.HasPrefix(destination, web2.DestinationPrefix) { + if p.web2Handler == nil { + return nil, fmt.Errorf("web2 reads not served") + } + return p.web2Handler, nil + } + + destClient, err := p.resolver.GetClient(destination) + if err != nil { + return nil, err + } + return destClient.GetReadRequestHandler() +} + // isExpired reports whether the request's expiry Push chain height has been // reached, using the chain height persisted by the event listener. func (p *ReadEventProcessor) isExpired(event *store.Event) bool { diff --git a/universalClient/pushwatcher/read_event_processor_test.go b/universalClient/pushwatcher/read_event_processor_test.go index e1ad5bd6..f10cfcad 100644 --- a/universalClient/pushwatcher/read_event_processor_test.go +++ b/universalClient/pushwatcher/read_event_processor_test.go @@ -78,7 +78,7 @@ func testReadRequest() *uread.ReadRequest { func newTestReadEventProcessor(t *testing.T, voter readVoter, destClient common.ChainClient) (*ReadEventProcessor, *common.ChainStore) { t.Helper() database := newTestDB(t) - p, err := NewReadEventProcessor(voter, &fakeChainResolver{client: destClient}, database, zerolog.Nop()) + p, err := NewReadEventProcessor(voter, &fakeChainResolver{client: destClient}, nil, database, zerolog.Nop()) require.NoError(t, err) return p, common.NewChainStore(database) } @@ -213,3 +213,40 @@ func TestReadEventProcessor_NotExpiredProcessesNormally(t *testing.T) { require.Contains(t, voter.votes, req.RequestID) assertStatus(t, cs, event.EventID, store.StatusCompleted) } + + +func TestReadEventProcessor_Web2Dispatch(t *testing.T) { + req := testReadRequest() + req.DestinationChain = "web2:https" + result := &uread.ReadResult{Status: uread.ReadStatusSuccess, ResultData: []byte{0xbb}} + + t.Run("dispatches to web2 handler", func(t *testing.T) { + database := newTestDB(t) + voter := &fakeReadVoter{txHash: "VOTE_TX"} + web2Handler := &fakeDestClient{result: result} + p, err := NewReadEventProcessor(voter, &fakeChainResolver{}, web2Handler, database, zerolog.Nop()) + require.NoError(t, err) + cs := common.NewChainStore(database) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + require.Contains(t, voter.votes, req.RequestID) + assert.Equal(t, result, voter.votes[req.RequestID]) + assertStatus(t, cs, event.EventID, store.StatusCompleted) + }) + + t.Run("no web2 handler retries", func(t *testing.T) { + database := newTestDB(t) + voter := &fakeReadVoter{txHash: "VOTE_TX"} + p, err := NewReadEventProcessor(voter, &fakeChainResolver{}, nil, database, zerolog.Nop()) + require.NoError(t, err) + cs := common.NewChainStore(database) + event := seedReadRequest(t, cs, req) + + require.NoError(t, p.HandleEvent(context.Background(), event)) + + assert.Empty(t, voter.votes) + assertStatus(t, cs, event.EventID, store.StatusConfirmed) + }) +} From 803c37d080e64c0ecd90b3925f3e7387fcfc51c0 Mon Sep 17 00:00:00 2001 From: aman035 Date: Wed, 5 Aug 2026 17:00:45 +0530 Subject: [PATCH 24/54] remove: getERC20 bal - can be done by contract calls --- .../externalchains/evm/read_envelope.go | 15 ++------------ .../externalchains/evm/read_envelope_test.go | 8 -------- .../externalchains/evm/read_executor.go | 20 ------------------- .../externalchains/evm/read_executor_test.go | 17 ---------------- 4 files changed, 2 insertions(+), 58 deletions(-) diff --git a/universalClient/externalchains/evm/read_envelope.go b/universalClient/externalchains/evm/read_envelope.go index 8580ba0e..428ce66c 100644 --- a/universalClient/externalchains/evm/read_envelope.go +++ b/universalClient/externalchains/evm/read_envelope.go @@ -12,9 +12,8 @@ type evmQueryType uint8 const ( evmQueryAccountBalance evmQueryType = 0 - evmQueryERC20Balance evmQueryType = 1 - evmQueryContractCall evmQueryType = 2 - evmQueryStorageSlot evmQueryType = 3 + evmQueryContractCall evmQueryType = 1 + evmQueryStorageSlot evmQueryType = 2 ) // evmBlockRefType mirrors the EvmBlockRefType enum. Only AT_NUMBER exists in v1. @@ -41,7 +40,6 @@ var ( }}) addressArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}) - addressPairArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "address"}) addressBytesArgs = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "bytes"}) addressBytes32Args = mustReadArgs(abi.ArgumentMarshaling{Type: "address"}, abi.ArgumentMarshaling{Type: "bytes32"}) ) @@ -102,15 +100,6 @@ func decodeAccountBalancePayload(payload []byte) (ethcommon.Address, error) { return vals[0].(ethcommon.Address), nil } -// decodeERC20BalancePayload decodes abi.encode(address token, address owner). -func decodeERC20BalancePayload(payload []byte) (token, owner ethcommon.Address, err error) { - vals, err := addressPairArgs.Unpack(payload) - if err != nil { - return ethcommon.Address{}, ethcommon.Address{}, fmt.Errorf("failed to unpack ERC20Balance payload: %w", err) - } - return vals[0].(ethcommon.Address), vals[1].(ethcommon.Address), nil -} - // decodeContractCallPayload decodes abi.encode(address target, bytes callData). func decodeContractCallPayload(payload []byte) (ethcommon.Address, []byte, error) { vals, err := addressBytesArgs.Unpack(payload) diff --git a/universalClient/externalchains/evm/read_envelope_test.go b/universalClient/externalchains/evm/read_envelope_test.go index af04832a..5d07e7d4 100644 --- a/universalClient/externalchains/evm/read_envelope_test.go +++ b/universalClient/externalchains/evm/read_envelope_test.go @@ -53,14 +53,6 @@ func TestDecodeEvmQueryEnvelope_Invalid(t *testing.T) { func TestDecodeEvmPayloads(t *testing.T) { token := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") - owner := ethcommon.HexToAddress("0x3333333333333333333333333333333333333333") - - erc20Payload, err := addressPairArgs.Pack(token, owner) - require.NoError(t, err) - gotToken, gotOwner, err := decodeERC20BalancePayload(erc20Payload) - require.NoError(t, err) - assert.Equal(t, token, gotToken) - assert.Equal(t, owner, gotOwner) callData := []byte{0xde, 0xad, 0xbe, 0xef} callPayload, err := addressBytesArgs.Pack(token, callData) diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go index 78fa0d7b..046e2911 100644 --- a/universalClient/externalchains/evm/read_executor.go +++ b/universalClient/externalchains/evm/read_executor.go @@ -5,15 +5,10 @@ import ( "fmt" "math/big" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/uread" ) -// balanceOfSelector is the 4-byte selector for balanceOf(address). -var balanceOfSelector = []byte{0x70, 0xa0, 0x82, 0x31} - // ExecuteRead implements common.ChainReader for EVM chains. // All validators must produce byte-identical results, so every query runs at the // height pinned in the request; execution is gated until that height has @@ -55,21 +50,6 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea } resultData, err = common.EncodeUint256Result(balance) - case evmQueryERC20Balance: - token, owner, decErr := decodeERC20BalancePayload(env.Payload) - if decErr != nil { - return uread.NewErrorResult(decErr), nil - } - callData := append(append([]byte{}, balanceOfSelector...), ethcommon.LeftPadBytes(owner.Bytes(), 32)...) - ret, rpcErr := c.rpcClient.CallContract(ctx, token, callData, blockNum) - if rpcErr != nil { - return nil, rpcErr - } - if len(ret) < 32 { - return uread.NewErrorResult(fmt.Errorf("balanceOf returned %d bytes", len(ret))), nil - } - resultData, err = common.EncodeUint256Result(new(big.Int).SetBytes(ret[:32])) - case evmQueryContractCall: target, callData, decErr := decodeContractCallPayload(env.Payload) if decErr != nil { diff --git a/universalClient/externalchains/evm/read_executor_test.go b/universalClient/externalchains/evm/read_executor_test.go index 37c97491..17efb17c 100644 --- a/universalClient/externalchains/evm/read_executor_test.go +++ b/universalClient/externalchains/evm/read_executor_test.go @@ -119,23 +119,6 @@ func TestExecuteRead_AccountBalance(t *testing.T) { assert.Len(t, result.ObservedBlockHash, 32) } -func TestExecuteRead_ERC20Balance(t *testing.T) { - token := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") - owner := ethcommon.HexToAddress("0x3333333333333333333333333333333333333333") - payload, err := addressPairArgs.Pack(token, owner) - require.NoError(t, err) - - client := newReadTestClient(t, map[string]any{ - "eth_getBlockByNumber": fakeHeader(100), - "eth_call": "0x" + fmt.Sprintf("%064x", 42), - }, nil) - - result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryERC20Balance), 0, payload)) - require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) - assert.Equal(t, big.NewInt(42), new(big.Int).SetBytes(result.ResultData)) -} - func TestExecuteRead_ContractCall(t *testing.T) { target := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") payload, err := addressBytesArgs.Pack(target, []byte{0xde, 0xad}) From d2e033cecd382bc23a2af42f77574b45f2d6bd0c Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 09:19:20 +0530 Subject: [PATCH 25/54] fix(proto): stop protocgen deleting compat/orm-api compat/orm-api/module matched the module-dir scan, so it was moved into api/ and removed, breaking the go.mod replace and failing go mod tidy. --- scripts/protocgen.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 551766d3..f7c0e512 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -28,7 +28,11 @@ rm -rf github.com # Copy files over for dep injection rm -rf api && mkdir api -custom_modules=$(find . -name 'module' -type d -not -path "./proto/*" -not -path "./.cache/*") +# NOTE: exclude ./compat/* — compat/orm-api/module is a vendored compatibility shim, not a +# generated proto module. Without this it is matched here, moved into ./api/ and then deleted by +# the `rm -rf $module` below, which breaks the `cosmossdk.io/api/cosmos/orm => ./compat/orm-api` +# replace in go.mod and fails the `go mod tidy` at the end of `make proto-gen`. +custom_modules=$(find . -name 'module' -type d -not -path "./proto/*" -not -path "./.cache/*" -not -path "./compat/*") # get the 1 up directory (so ./cosmos/mint/module becomes ./cosmos/mint) # remove the relative path starter from base namespaces. so ./cosmos/mint becomes cosmos/mint From b9c966f3b2e122a160d29d291a9c7b3549ead9e3 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 09:19:37 +0530 Subject: [PATCH 26/54] feat(ucallback): scaffold module Generated with spawn, ORM scaffolding removed to match the other modules which all use collections. Genesis moved to keeper/genesis.go. --- api/ucallback/module/v1/module.pulsar.go | 503 ++++++++++ api/ucallback/v1/genesis.pulsar.go | 1054 +++++++++++++++++++++ api/ucallback/v1/query.pulsar.go | 999 ++++++++++++++++++++ api/ucallback/v1/query_grpc.pb.go | 111 +++ api/ucallback/v1/tx.pulsar.go | 1088 ++++++++++++++++++++++ api/ucallback/v1/tx_grpc.pb.go | 115 +++ app/app.go | 24 +- proto/ucallback/module/v1/module.proto | 13 + proto/ucallback/v1/genesis.proto | 22 + proto/ucallback/v1/query.proto | 24 + proto/ucallback/v1/tx.proto | 40 + x/ucallback/README.md | 3 + x/ucallback/autocli.go | 31 + x/ucallback/client/cli/query.go | 50 + x/ucallback/client/cli/tx.go | 71 ++ x/ucallback/depinject.go | 63 ++ x/ucallback/keeper/genesis.go | 28 + x/ucallback/keeper/genesis_test.go | 22 + x/ucallback/keeper/keeper.go | 64 ++ x/ucallback/keeper/keeper_test.go | 148 +++ x/ucallback/keeper/msg_server.go | 29 + x/ucallback/keeper/msg_server_test.go | 56 ++ x/ucallback/keeper/query_server.go | 30 + x/ucallback/module.go | 150 +++ x/ucallback/types/codec.go | 35 + x/ucallback/types/genesis.go | 19 + x/ucallback/types/genesis.pb.go | 511 ++++++++++ x/ucallback/types/genesis_test.go | 38 + x/ucallback/types/keys.go | 18 + x/ucallback/types/msgs.go | 49 + x/ucallback/types/params.go | 29 + x/ucallback/types/query.pb.go | 540 +++++++++++ x/ucallback/types/query.pb.gw.go | 153 +++ x/ucallback/types/tx.pb.go | 602 ++++++++++++ 34 files changed, 6727 insertions(+), 5 deletions(-) create mode 100644 api/ucallback/module/v1/module.pulsar.go create mode 100644 api/ucallback/v1/genesis.pulsar.go create mode 100644 api/ucallback/v1/query.pulsar.go create mode 100644 api/ucallback/v1/query_grpc.pb.go create mode 100644 api/ucallback/v1/tx.pulsar.go create mode 100644 api/ucallback/v1/tx_grpc.pb.go create mode 100755 proto/ucallback/module/v1/module.proto create mode 100755 proto/ucallback/v1/genesis.proto create mode 100755 proto/ucallback/v1/query.proto create mode 100755 proto/ucallback/v1/tx.proto create mode 100755 x/ucallback/README.md create mode 100755 x/ucallback/autocli.go create mode 100755 x/ucallback/client/cli/query.go create mode 100755 x/ucallback/client/cli/tx.go create mode 100755 x/ucallback/depinject.go create mode 100644 x/ucallback/keeper/genesis.go create mode 100755 x/ucallback/keeper/genesis_test.go create mode 100755 x/ucallback/keeper/keeper.go create mode 100755 x/ucallback/keeper/keeper_test.go create mode 100755 x/ucallback/keeper/msg_server.go create mode 100755 x/ucallback/keeper/msg_server_test.go create mode 100755 x/ucallback/keeper/query_server.go create mode 100755 x/ucallback/module.go create mode 100755 x/ucallback/types/codec.go create mode 100755 x/ucallback/types/genesis.go create mode 100644 x/ucallback/types/genesis.pb.go create mode 100755 x/ucallback/types/genesis_test.go create mode 100755 x/ucallback/types/keys.go create mode 100755 x/ucallback/types/msgs.go create mode 100755 x/ucallback/types/params.go create mode 100644 x/ucallback/types/query.pb.go create mode 100644 x/ucallback/types/query.pb.gw.go create mode 100644 x/ucallback/types/tx.pb.go diff --git a/api/ucallback/module/v1/module.pulsar.go b/api/ucallback/module/v1/module.pulsar.go new file mode 100644 index 00000000..311612d9 --- /dev/null +++ b/api/ucallback/module/v1/module.pulsar.go @@ -0,0 +1,503 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package modulev1 + +import ( + _ "cosmossdk.io/api/cosmos/app/v1alpha1" + fmt "fmt" + runtime "github.com/cosmos/cosmos-proto/runtime" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_Module protoreflect.MessageDescriptor +) + +func init() { + file_ucallback_module_v1_module_proto_init() + md_Module = File_ucallback_module_v1_module_proto.Messages().ByName("Module") +} + +var _ protoreflect.Message = (*fastReflection_Module)(nil) + +type fastReflection_Module Module + +func (x *Module) ProtoReflect() protoreflect.Message { + return (*fastReflection_Module)(x) +} + +func (x *Module) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_module_v1_module_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_Module_messageType fastReflection_Module_messageType +var _ protoreflect.MessageType = fastReflection_Module_messageType{} + +type fastReflection_Module_messageType struct{} + +func (x fastReflection_Module_messageType) Zero() protoreflect.Message { + return (*fastReflection_Module)(nil) +} +func (x fastReflection_Module_messageType) New() protoreflect.Message { + return new(fastReflection_Module) +} +func (x fastReflection_Module_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_Module +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_Module) Descriptor() protoreflect.MessageDescriptor { + return md_Module +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_Module) Type() protoreflect.MessageType { + return _fastReflection_Module_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_Module) New() protoreflect.Message { + return new(fastReflection_Module) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_Module) Interface() protoreflect.ProtoMessage { + return (*Module)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_Module) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_Module) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Module) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_Module) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Module) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Module) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_Module) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.module.v1.Module")) + } + panic(fmt.Errorf("message ucallback.module.v1.Module does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_Module) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.module.v1.Module", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_Module) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Module) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_Module) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_Module) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*Module) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*Module) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*Module) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: Module: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: Module: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: ucallback/module/v1/module.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Module is the app config object of the module. +// Learn more: https://docs.cosmos.network/main/building-modules/depinject +type Module struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Module) Reset() { + *x = Module{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_module_v1_module_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Module) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Module) ProtoMessage() {} + +// Deprecated: Use Module.ProtoReflect.Descriptor instead. +func (*Module) Descriptor() ([]byte, []int) { + return file_ucallback_module_v1_module_proto_rawDescGZIP(), []int{0} +} + +var File_ucallback_module_v1_module_proto protoreflect.FileDescriptor + +var file_ucallback_module_v1_module_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x13, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x20, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, + 0x61, 0x70, 0x70, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x06, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x3a, 0x2c, 0xba, 0xc0, 0x96, 0xda, 0x01, 0x26, 0x0a, 0x24, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, + 0x65, 0x42, 0xdb, 0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x45, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, + 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x4d, 0x58, 0xaa, 0x02, 0x13, 0x55, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x13, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1f, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x5c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ucallback_module_v1_module_proto_rawDescOnce sync.Once + file_ucallback_module_v1_module_proto_rawDescData = file_ucallback_module_v1_module_proto_rawDesc +) + +func file_ucallback_module_v1_module_proto_rawDescGZIP() []byte { + file_ucallback_module_v1_module_proto_rawDescOnce.Do(func() { + file_ucallback_module_v1_module_proto_rawDescData = protoimpl.X.CompressGZIP(file_ucallback_module_v1_module_proto_rawDescData) + }) + return file_ucallback_module_v1_module_proto_rawDescData +} + +var file_ucallback_module_v1_module_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_ucallback_module_v1_module_proto_goTypes = []interface{}{ + (*Module)(nil), // 0: ucallback.module.v1.Module +} +var file_ucallback_module_v1_module_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_ucallback_module_v1_module_proto_init() } +func file_ucallback_module_v1_module_proto_init() { + if File_ucallback_module_v1_module_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_ucallback_module_v1_module_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Module); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ucallback_module_v1_module_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ucallback_module_v1_module_proto_goTypes, + DependencyIndexes: file_ucallback_module_v1_module_proto_depIdxs, + MessageInfos: file_ucallback_module_v1_module_proto_msgTypes, + }.Build() + File_ucallback_module_v1_module_proto = out.File + file_ucallback_module_v1_module_proto_rawDesc = nil + file_ucallback_module_v1_module_proto_goTypes = nil + file_ucallback_module_v1_module_proto_depIdxs = nil +} diff --git a/api/ucallback/v1/genesis.pulsar.go b/api/ucallback/v1/genesis.pulsar.go new file mode 100644 index 00000000..e05e130a --- /dev/null +++ b/api/ucallback/v1/genesis.pulsar.go @@ -0,0 +1,1054 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package ucallbackv1 + +import ( + _ "cosmossdk.io/api/amino" + fmt "fmt" + runtime "github.com/cosmos/cosmos-proto/runtime" + _ "github.com/cosmos/gogoproto/gogoproto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_GenesisState protoreflect.MessageDescriptor + fd_GenesisState_params protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_genesis_proto_init() + md_GenesisState = File_ucallback_v1_genesis_proto.Messages().ByName("GenesisState") + fd_GenesisState_params = md_GenesisState.Fields().ByName("params") +} + +var _ protoreflect.Message = (*fastReflection_GenesisState)(nil) + +type fastReflection_GenesisState GenesisState + +func (x *GenesisState) ProtoReflect() protoreflect.Message { + return (*fastReflection_GenesisState)(x) +} + +func (x *GenesisState) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_genesis_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_GenesisState_messageType fastReflection_GenesisState_messageType +var _ protoreflect.MessageType = fastReflection_GenesisState_messageType{} + +type fastReflection_GenesisState_messageType struct{} + +func (x fastReflection_GenesisState_messageType) Zero() protoreflect.Message { + return (*fastReflection_GenesisState)(nil) +} +func (x fastReflection_GenesisState_messageType) New() protoreflect.Message { + return new(fastReflection_GenesisState) +} +func (x fastReflection_GenesisState_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_GenesisState +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_GenesisState) Descriptor() protoreflect.MessageDescriptor { + return md_GenesisState +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_GenesisState) Type() protoreflect.MessageType { + return _fastReflection_GenesisState_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_GenesisState) New() protoreflect.Message { + return new(fastReflection_GenesisState) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_GenesisState) Interface() protoreflect.ProtoMessage { + return (*GenesisState)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_GenesisState) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Params != nil { + value := protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + if !f(fd_GenesisState_params, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_GenesisState) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.GenesisState.params": + return x.Params != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_GenesisState) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.GenesisState.params": + x.Params = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_GenesisState) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.GenesisState.params": + value := x.Params + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_GenesisState) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.GenesisState.params": + x.Params = value.Message().Interface().(*Params) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_GenesisState) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.GenesisState.params": + if x.Params == nil { + x.Params = new(Params) + } + return protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_GenesisState) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.GenesisState.params": + m := new(Params) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) + } + panic(fmt.Errorf("message ucallback.v1.GenesisState does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_GenesisState) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.GenesisState", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_GenesisState) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_GenesisState) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_GenesisState) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_GenesisState) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*GenesisState) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Params != nil { + l = options.Size(x.Params) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*GenesisState) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Params != nil { + encoded, err := options.Marshal(x.Params) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*GenesisState) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Params == nil { + x.Params = &Params{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Params); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_Params protoreflect.MessageDescriptor + fd_Params_some_value protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_genesis_proto_init() + md_Params = File_ucallback_v1_genesis_proto.Messages().ByName("Params") + fd_Params_some_value = md_Params.Fields().ByName("some_value") +} + +var _ protoreflect.Message = (*fastReflection_Params)(nil) + +type fastReflection_Params Params + +func (x *Params) ProtoReflect() protoreflect.Message { + return (*fastReflection_Params)(x) +} + +func (x *Params) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_Params_messageType fastReflection_Params_messageType +var _ protoreflect.MessageType = fastReflection_Params_messageType{} + +type fastReflection_Params_messageType struct{} + +func (x fastReflection_Params_messageType) Zero() protoreflect.Message { + return (*fastReflection_Params)(nil) +} +func (x fastReflection_Params_messageType) New() protoreflect.Message { + return new(fastReflection_Params) +} +func (x fastReflection_Params_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_Params +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_Params) Descriptor() protoreflect.MessageDescriptor { + return md_Params +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_Params) Type() protoreflect.MessageType { + return _fastReflection_Params_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_Params) New() protoreflect.Message { + return new(fastReflection_Params) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_Params) Interface() protoreflect.ProtoMessage { + return (*Params)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.SomeValue != false { + value := protoreflect.ValueOfBool(x.SomeValue) + if !f(fd_Params_some_value, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.Params.some_value": + return x.SomeValue != false + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.Params.some_value": + x.SomeValue = false + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.Params.some_value": + value := x.SomeValue + return protoreflect.ValueOfBool(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Params) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.Params.some_value": + x.SomeValue = value.Bool() + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.Params.some_value": + panic(fmt.Errorf("field some_value of message ucallback.v1.Params is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_Params) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.Params.some_value": + return protoreflect.ValueOfBool(false) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.Params")) + } + panic(fmt.Errorf("message ucallback.v1.Params does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_Params) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.Params", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_Params) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_Params) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_Params) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*Params) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.SomeValue { + n += 2 + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*Params) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.SomeValue { + i-- + if x.SomeValue { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*Params) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SomeValue", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + x.SomeValue = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: ucallback/v1/genesis.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// GenesisState defines the module genesis state +type GenesisState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Params defines all the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (x *GenesisState) Reset() { + *x = GenesisState{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_genesis_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenesisState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenesisState) ProtoMessage() {} + +// Deprecated: Use GenesisState.ProtoReflect.Descriptor instead. +func (*GenesisState) Descriptor() ([]byte, []int) { + return file_ucallback_v1_genesis_proto_rawDescGZIP(), []int{0} +} + +func (x *GenesisState) GetParams() *Params { + if x != nil { + return x.Params + } + return nil +} + +// Params defines the set of module parameters. +type Params struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SomeValue bool `protobuf:"varint,2,opt,name=some_value,json=someValue,proto3" json:"some_value,omitempty"` +} + +func (x *Params) Reset() { + *x = Params{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Params) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Params) ProtoMessage() {} + +// Deprecated: Use Params.ProtoReflect.Descriptor instead. +func (*Params) Descriptor() ([]byte, []int) { + return file_ucallback_v1_genesis_proto_rawDescGZIP(), []int{1} +} + +func (x *Params) GetSomeValue() bool { + if x != nil { + return x.SomeValue + } + return false +} + +var File_ucallback_v1_genesis_proto protoreflect.FileDescriptor + +var file_ucallback_v1_genesis_proto_rawDesc = []byte{ + 0x0a, 0x1a, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, + 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x6f, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x1d, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x10, 0x75, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, + 0xb4, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, + 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ucallback_v1_genesis_proto_rawDescOnce sync.Once + file_ucallback_v1_genesis_proto_rawDescData = file_ucallback_v1_genesis_proto_rawDesc +) + +func file_ucallback_v1_genesis_proto_rawDescGZIP() []byte { + file_ucallback_v1_genesis_proto_rawDescOnce.Do(func() { + file_ucallback_v1_genesis_proto_rawDescData = protoimpl.X.CompressGZIP(file_ucallback_v1_genesis_proto_rawDescData) + }) + return file_ucallback_v1_genesis_proto_rawDescData +} + +var file_ucallback_v1_genesis_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_genesis_proto_goTypes = []interface{}{ + (*GenesisState)(nil), // 0: ucallback.v1.GenesisState + (*Params)(nil), // 1: ucallback.v1.Params +} +var file_ucallback_v1_genesis_proto_depIdxs = []int32{ + 1, // 0: ucallback.v1.GenesisState.params:type_name -> ucallback.v1.Params + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ucallback_v1_genesis_proto_init() } +func file_ucallback_v1_genesis_proto_init() { + if File_ucallback_v1_genesis_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_ucallback_v1_genesis_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenesisState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_genesis_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Params); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ucallback_v1_genesis_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ucallback_v1_genesis_proto_goTypes, + DependencyIndexes: file_ucallback_v1_genesis_proto_depIdxs, + MessageInfos: file_ucallback_v1_genesis_proto_msgTypes, + }.Build() + File_ucallback_v1_genesis_proto = out.File + file_ucallback_v1_genesis_proto_rawDesc = nil + file_ucallback_v1_genesis_proto_goTypes = nil + file_ucallback_v1_genesis_proto_depIdxs = nil +} diff --git a/api/ucallback/v1/query.pulsar.go b/api/ucallback/v1/query.pulsar.go new file mode 100644 index 00000000..3dbf7787 --- /dev/null +++ b/api/ucallback/v1/query.pulsar.go @@ -0,0 +1,999 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package ucallbackv1 + +import ( + fmt "fmt" + runtime "github.com/cosmos/cosmos-proto/runtime" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_QueryParamsRequest protoreflect.MessageDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryParamsRequest = File_ucallback_v1_query_proto.Messages().ByName("QueryParamsRequest") +} + +var _ protoreflect.Message = (*fastReflection_QueryParamsRequest)(nil) + +type fastReflection_QueryParamsRequest QueryParamsRequest + +func (x *QueryParamsRequest) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryParamsRequest)(x) +} + +func (x *QueryParamsRequest) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryParamsRequest_messageType fastReflection_QueryParamsRequest_messageType +var _ protoreflect.MessageType = fastReflection_QueryParamsRequest_messageType{} + +type fastReflection_QueryParamsRequest_messageType struct{} + +func (x fastReflection_QueryParamsRequest_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryParamsRequest)(nil) +} +func (x fastReflection_QueryParamsRequest_messageType) New() protoreflect.Message { + return new(fastReflection_QueryParamsRequest) +} +func (x fastReflection_QueryParamsRequest_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryParamsRequest +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryParamsRequest) Descriptor() protoreflect.MessageDescriptor { + return md_QueryParamsRequest +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryParamsRequest) Type() protoreflect.MessageType { + return _fastReflection_QueryParamsRequest_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryParamsRequest) New() protoreflect.Message { + return new(fastReflection_QueryParamsRequest) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryParamsRequest) Interface() protoreflect.ProtoMessage { + return (*QueryParamsRequest)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryParamsRequest) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryParamsRequest) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsRequest) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryParamsRequest) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsRequest) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsRequest) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryParamsRequest) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsRequest does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryParamsRequest) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryParamsRequest", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryParamsRequest) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsRequest) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryParamsRequest) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryParamsRequest) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryParamsRequest) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryParamsRequest) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryParamsRequest) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_QueryParamsResponse protoreflect.MessageDescriptor + fd_QueryParamsResponse_params protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryParamsResponse = File_ucallback_v1_query_proto.Messages().ByName("QueryParamsResponse") + fd_QueryParamsResponse_params = md_QueryParamsResponse.Fields().ByName("params") +} + +var _ protoreflect.Message = (*fastReflection_QueryParamsResponse)(nil) + +type fastReflection_QueryParamsResponse QueryParamsResponse + +func (x *QueryParamsResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryParamsResponse)(x) +} + +func (x *QueryParamsResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryParamsResponse_messageType fastReflection_QueryParamsResponse_messageType +var _ protoreflect.MessageType = fastReflection_QueryParamsResponse_messageType{} + +type fastReflection_QueryParamsResponse_messageType struct{} + +func (x fastReflection_QueryParamsResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryParamsResponse)(nil) +} +func (x fastReflection_QueryParamsResponse_messageType) New() protoreflect.Message { + return new(fastReflection_QueryParamsResponse) +} +func (x fastReflection_QueryParamsResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryParamsResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryParamsResponse) Descriptor() protoreflect.MessageDescriptor { + return md_QueryParamsResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryParamsResponse) Type() protoreflect.MessageType { + return _fastReflection_QueryParamsResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryParamsResponse) New() protoreflect.Message { + return new(fastReflection_QueryParamsResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryParamsResponse) Interface() protoreflect.ProtoMessage { + return (*QueryParamsResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryParamsResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Params != nil { + value := protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + if !f(fd_QueryParamsResponse_params, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryParamsResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + return x.Params != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + x.Params = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryParamsResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + value := x.Params + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + x.Params = value.Message().Interface().(*Params) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + if x.Params == nil { + x.Params = new(Params) + } + return protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryParamsResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryParamsResponse.params": + m := new(Params) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryParamsResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryParamsResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryParamsResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryParamsResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryParamsResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryParamsResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryParamsResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryParamsResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Params != nil { + l = options.Size(x.Params) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryParamsResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Params != nil { + encoded, err := options.Marshal(x.Params) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryParamsResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Params == nil { + x.Params = &Params{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Params); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: ucallback/v1/query.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *QueryParamsRequest) Reset() { + *x = QueryParamsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryParamsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryParamsRequest) ProtoMessage() {} + +// Deprecated: Use QueryParamsRequest.ProtoReflect.Descriptor instead. +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{0} +} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // params defines the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (x *QueryParamsResponse) Reset() { + *x = QueryParamsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryParamsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryParamsResponse) ProtoMessage() {} + +// Deprecated: Use QueryParamsResponse.ProtoReflect.Descriptor instead. +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{1} +} + +func (x *QueryParamsResponse) GetParams() *Params { + if x != nil { + return x.Params + } + return nil +} + +var File_ucallback_v1_query_proto protoreflect.FileDescriptor + +var file_ucallback_v1_query_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x14, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2c, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x32, 0x74, 0x0a, + 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x20, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, + 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, + 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, + 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, + 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ucallback_v1_query_proto_rawDescOnce sync.Once + file_ucallback_v1_query_proto_rawDescData = file_ucallback_v1_query_proto_rawDesc +) + +func file_ucallback_v1_query_proto_rawDescGZIP() []byte { + file_ucallback_v1_query_proto_rawDescOnce.Do(func() { + file_ucallback_v1_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_ucallback_v1_query_proto_rawDescData) + }) + return file_ucallback_v1_query_proto_rawDescData +} + +var file_ucallback_v1_query_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_query_proto_goTypes = []interface{}{ + (*QueryParamsRequest)(nil), // 0: ucallback.v1.QueryParamsRequest + (*QueryParamsResponse)(nil), // 1: ucallback.v1.QueryParamsResponse + (*Params)(nil), // 2: ucallback.v1.Params +} +var file_ucallback_v1_query_proto_depIdxs = []int32{ + 2, // 0: ucallback.v1.QueryParamsResponse.params:type_name -> ucallback.v1.Params + 0, // 1: ucallback.v1.Query.Params:input_type -> ucallback.v1.QueryParamsRequest + 1, // 2: ucallback.v1.Query.Params:output_type -> ucallback.v1.QueryParamsResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ucallback_v1_query_proto_init() } +func file_ucallback_v1_query_proto_init() { + if File_ucallback_v1_query_proto != nil { + return + } + file_ucallback_v1_genesis_proto_init() + if !protoimpl.UnsafeEnabled { + file_ucallback_v1_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryParamsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryParamsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ucallback_v1_query_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ucallback_v1_query_proto_goTypes, + DependencyIndexes: file_ucallback_v1_query_proto_depIdxs, + MessageInfos: file_ucallback_v1_query_proto_msgTypes, + }.Build() + File_ucallback_v1_query_proto = out.File + file_ucallback_v1_query_proto_rawDesc = nil + file_ucallback_v1_query_proto_goTypes = nil + file_ucallback_v1_query_proto_depIdxs = nil +} diff --git a/api/ucallback/v1/query_grpc.pb.go b/api/ucallback/v1/query_grpc.pb.go new file mode 100644 index 00000000..b6deab65 --- /dev/null +++ b/api/ucallback/v1/query_grpc.pb.go @@ -0,0 +1,111 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: ucallback/v1/query.proto + +package ucallbackv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Query_Params_FullMethodName = "/ucallback.v1.Query/Params" +) + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type QueryClient interface { + // Params queries all parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc.ClientConnInterface +} + +func NewQueryClient(cc grpc.ClientConnInterface) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +// All implementations must embed UnimplementedQueryServer +// for forward compatibility +type QueryServer interface { + // Params queries all parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + mustEmbedUnimplementedQueryServer() +} + +// UnimplementedQueryServer must be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {} + +// UnsafeQueryServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to QueryServer will +// result in compilation errors. +type UnsafeQueryServer interface { + mustEmbedUnimplementedQueryServer() +} + +func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) { + s.RegisterService(&Query_ServiceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Query_Params_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Query_ServiceDesc is the grpc.ServiceDesc for Query service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Query_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ucallback.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ucallback/v1/query.proto", +} diff --git a/api/ucallback/v1/tx.pulsar.go b/api/ucallback/v1/tx.pulsar.go new file mode 100644 index 00000000..0f28dd7b --- /dev/null +++ b/api/ucallback/v1/tx.pulsar.go @@ -0,0 +1,1088 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package ucallbackv1 + +import ( + _ "cosmossdk.io/api/cosmos/msg/v1" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + runtime "github.com/cosmos/cosmos-proto/runtime" + _ "github.com/cosmos/gogoproto/gogoproto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_MsgUpdateParams protoreflect.MessageDescriptor + fd_MsgUpdateParams_authority protoreflect.FieldDescriptor + fd_MsgUpdateParams_params protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_tx_proto_init() + md_MsgUpdateParams = File_ucallback_v1_tx_proto.Messages().ByName("MsgUpdateParams") + fd_MsgUpdateParams_authority = md_MsgUpdateParams.Fields().ByName("authority") + fd_MsgUpdateParams_params = md_MsgUpdateParams.Fields().ByName("params") +} + +var _ protoreflect.Message = (*fastReflection_MsgUpdateParams)(nil) + +type fastReflection_MsgUpdateParams MsgUpdateParams + +func (x *MsgUpdateParams) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgUpdateParams)(x) +} + +func (x *MsgUpdateParams) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_tx_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgUpdateParams_messageType fastReflection_MsgUpdateParams_messageType +var _ protoreflect.MessageType = fastReflection_MsgUpdateParams_messageType{} + +type fastReflection_MsgUpdateParams_messageType struct{} + +func (x fastReflection_MsgUpdateParams_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgUpdateParams)(nil) +} +func (x fastReflection_MsgUpdateParams_messageType) New() protoreflect.Message { + return new(fastReflection_MsgUpdateParams) +} +func (x fastReflection_MsgUpdateParams_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgUpdateParams +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgUpdateParams) Descriptor() protoreflect.MessageDescriptor { + return md_MsgUpdateParams +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgUpdateParams) Type() protoreflect.MessageType { + return _fastReflection_MsgUpdateParams_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgUpdateParams) New() protoreflect.Message { + return new(fastReflection_MsgUpdateParams) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgUpdateParams) Interface() protoreflect.ProtoMessage { + return (*MsgUpdateParams)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgUpdateParams) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Authority != "" { + value := protoreflect.ValueOfString(x.Authority) + if !f(fd_MsgUpdateParams_authority, value) { + return + } + } + if x.Params != nil { + value := protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + if !f(fd_MsgUpdateParams_params, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgUpdateParams) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.MsgUpdateParams.authority": + return x.Authority != "" + case "ucallback.v1.MsgUpdateParams.params": + return x.Params != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParams) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.MsgUpdateParams.authority": + x.Authority = "" + case "ucallback.v1.MsgUpdateParams.params": + x.Params = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgUpdateParams) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.MsgUpdateParams.authority": + value := x.Authority + return protoreflect.ValueOfString(value) + case "ucallback.v1.MsgUpdateParams.params": + value := x.Params + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParams) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.MsgUpdateParams.authority": + x.Authority = value.Interface().(string) + case "ucallback.v1.MsgUpdateParams.params": + x.Params = value.Message().Interface().(*Params) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParams) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgUpdateParams.params": + if x.Params == nil { + x.Params = new(Params) + } + return protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + case "ucallback.v1.MsgUpdateParams.authority": + panic(fmt.Errorf("field authority of message ucallback.v1.MsgUpdateParams is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgUpdateParams) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgUpdateParams.authority": + return protoreflect.ValueOfString("") + case "ucallback.v1.MsgUpdateParams.params": + m := new(Params) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParams")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParams does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgUpdateParams) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.MsgUpdateParams", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgUpdateParams) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParams) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgUpdateParams) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgUpdateParams) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgUpdateParams) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Authority) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Params != nil { + l = options.Size(x.Params) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgUpdateParams) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Params != nil { + encoded, err := options.Marshal(x.Params) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + if len(x.Authority) > 0 { + i -= len(x.Authority) + copy(dAtA[i:], x.Authority) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Authority))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgUpdateParams) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Params == nil { + x.Params = &Params{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Params); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_MsgUpdateParamsResponse protoreflect.MessageDescriptor +) + +func init() { + file_ucallback_v1_tx_proto_init() + md_MsgUpdateParamsResponse = File_ucallback_v1_tx_proto.Messages().ByName("MsgUpdateParamsResponse") +} + +var _ protoreflect.Message = (*fastReflection_MsgUpdateParamsResponse)(nil) + +type fastReflection_MsgUpdateParamsResponse MsgUpdateParamsResponse + +func (x *MsgUpdateParamsResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgUpdateParamsResponse)(x) +} + +func (x *MsgUpdateParamsResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_tx_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgUpdateParamsResponse_messageType fastReflection_MsgUpdateParamsResponse_messageType +var _ protoreflect.MessageType = fastReflection_MsgUpdateParamsResponse_messageType{} + +type fastReflection_MsgUpdateParamsResponse_messageType struct{} + +func (x fastReflection_MsgUpdateParamsResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgUpdateParamsResponse)(nil) +} +func (x fastReflection_MsgUpdateParamsResponse_messageType) New() protoreflect.Message { + return new(fastReflection_MsgUpdateParamsResponse) +} +func (x fastReflection_MsgUpdateParamsResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgUpdateParamsResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgUpdateParamsResponse) Descriptor() protoreflect.MessageDescriptor { + return md_MsgUpdateParamsResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgUpdateParamsResponse) Type() protoreflect.MessageType { + return _fastReflection_MsgUpdateParamsResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgUpdateParamsResponse) New() protoreflect.Message { + return new(fastReflection_MsgUpdateParamsResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgUpdateParamsResponse) Interface() protoreflect.ProtoMessage { + return (*MsgUpdateParamsResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgUpdateParamsResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgUpdateParamsResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParamsResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgUpdateParamsResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParamsResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParamsResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgUpdateParamsResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgUpdateParamsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgUpdateParamsResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgUpdateParamsResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.MsgUpdateParamsResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgUpdateParamsResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgUpdateParamsResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgUpdateParamsResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgUpdateParamsResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgUpdateParamsResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgUpdateParamsResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgUpdateParamsResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: ucallback/v1/tx.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // authority is the address of the governance account. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the parameters to update. + // + // NOTE: All parameters must be supplied. + Params *Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params,omitempty"` +} + +func (x *MsgUpdateParams) Reset() { + *x = MsgUpdateParams{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_tx_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgUpdateParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgUpdateParams) ProtoMessage() {} + +// Deprecated: Use MsgUpdateParams.ProtoReflect.Descriptor instead. +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return file_ucallback_v1_tx_proto_rawDescGZIP(), []int{0} +} + +func (x *MsgUpdateParams) GetAuthority() string { + if x != nil { + return x.Authority + } + return "" +} + +func (x *MsgUpdateParams) GetParams() *Params { + if x != nil { + return x.Params + } + return nil +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParamsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *MsgUpdateParamsResponse) Reset() { + *x = MsgUpdateParamsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_tx_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgUpdateParamsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgUpdateParamsResponse) ProtoMessage() {} + +// Deprecated: Use MsgUpdateParamsResponse.ProtoReflect.Descriptor instead. +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_tx_proto_rawDescGZIP(), []int{1} +} + +var File_ucallback_v1_tx_proto protoreflect.FileDescriptor + +var file_ucallback_v1_tx_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, + 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, + 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x0f, + 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x36, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, + 0xde, 0x1f, 0x00, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x0e, 0x82, 0xe7, 0xb0, + 0x2a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x4d, + 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x62, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x54, 0x0a, + 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1d, 0x2e, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x25, 0x2e, 0x75, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x1a, 0x05, 0x80, 0xe7, 0xb0, 0x2a, 0x01, 0x42, 0xaf, 0x01, 0x0a, 0x10, 0x63, + 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, + 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, + 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, + 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, + 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ucallback_v1_tx_proto_rawDescOnce sync.Once + file_ucallback_v1_tx_proto_rawDescData = file_ucallback_v1_tx_proto_rawDesc +) + +func file_ucallback_v1_tx_proto_rawDescGZIP() []byte { + file_ucallback_v1_tx_proto_rawDescOnce.Do(func() { + file_ucallback_v1_tx_proto_rawDescData = protoimpl.X.CompressGZIP(file_ucallback_v1_tx_proto_rawDescData) + }) + return file_ucallback_v1_tx_proto_rawDescData +} + +var file_ucallback_v1_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_tx_proto_goTypes = []interface{}{ + (*MsgUpdateParams)(nil), // 0: ucallback.v1.MsgUpdateParams + (*MsgUpdateParamsResponse)(nil), // 1: ucallback.v1.MsgUpdateParamsResponse + (*Params)(nil), // 2: ucallback.v1.Params +} +var file_ucallback_v1_tx_proto_depIdxs = []int32{ + 2, // 0: ucallback.v1.MsgUpdateParams.params:type_name -> ucallback.v1.Params + 0, // 1: ucallback.v1.Msg.UpdateParams:input_type -> ucallback.v1.MsgUpdateParams + 1, // 2: ucallback.v1.Msg.UpdateParams:output_type -> ucallback.v1.MsgUpdateParamsResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ucallback_v1_tx_proto_init() } +func file_ucallback_v1_tx_proto_init() { + if File_ucallback_v1_tx_proto != nil { + return + } + file_ucallback_v1_genesis_proto_init() + if !protoimpl.UnsafeEnabled { + file_ucallback_v1_tx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgUpdateParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_tx_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgUpdateParamsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ucallback_v1_tx_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ucallback_v1_tx_proto_goTypes, + DependencyIndexes: file_ucallback_v1_tx_proto_depIdxs, + MessageInfos: file_ucallback_v1_tx_proto_msgTypes, + }.Build() + File_ucallback_v1_tx_proto = out.File + file_ucallback_v1_tx_proto_rawDesc = nil + file_ucallback_v1_tx_proto_goTypes = nil + file_ucallback_v1_tx_proto_depIdxs = nil +} diff --git a/api/ucallback/v1/tx_grpc.pb.go b/api/ucallback/v1/tx_grpc.pb.go new file mode 100644 index 00000000..6834c819 --- /dev/null +++ b/api/ucallback/v1/tx_grpc.pb.go @@ -0,0 +1,115 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: ucallback/v1/tx.proto + +package ucallbackv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Msg_UpdateParams_FullMethodName = "/ucallback.v1.Msg/UpdateParams" +) + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type MsgClient interface { + // UpdateParams defines a governance operation for updating the parameters. + // + // Since: cosmos-sdk 0.47 + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc.ClientConnInterface +} + +func NewMsgClient(cc grpc.ClientConnInterface) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +// All implementations must embed UnimplementedMsgServer +// for forward compatibility +type MsgServer interface { + // UpdateParams defines a governance operation for updating the parameters. + // + // Since: cosmos-sdk 0.47 + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) + mustEmbedUnimplementedMsgServer() +} + +// UnimplementedMsgServer must be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (UnimplementedMsgServer) UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} +func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {} + +// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to MsgServer will +// result in compilation errors. +type UnsafeMsgServer interface { + mustEmbedUnimplementedMsgServer() +} + +func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) { + s.RegisterService(&Msg_ServiceDesc, srv) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Msg_UpdateParams_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +// Msg_ServiceDesc is the grpc.ServiceDesc for Msg service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Msg_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ucallback.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ucallback/v1/tx.proto", +} diff --git a/app/app.go b/app/app.go index 2d0d904d..fe626098 100644 --- a/app/app.go +++ b/app/app.go @@ -10,7 +10,6 @@ import ( "sort" "sync" "time" - autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" "cosmossdk.io/client/v2/autocli" @@ -119,7 +118,6 @@ import ( feemarketkeeper "github.com/cosmos/evm/x/feemarket/keeper" feemarkettypes "github.com/cosmos/evm/x/feemarket/types" "github.com/cosmos/evm/x/vm" - // _ "github.com/ethereum/go-ethereum/core/tracers/js" // _ "github.com/ethereum/go-ethereum/core/tracers/native" evmkeeper "github.com/cosmos/evm/x/vm/keeper" @@ -153,12 +151,10 @@ import ( ibcexported "github.com/cosmos/ibc-go/v10/modules/core/exported" ibckeeper "github.com/cosmos/ibc-go/v10/modules/core/keeper" ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint" - // "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/common" cosmoscorevm "github.com/ethereum/go-ethereum/core/vm" chainante "github.com/pushchain/push-chain-node/app/ante" - usigverifierprecompile "github.com/pushchain/push-chain-node/precompiles/usigverifier" pushtypes "github.com/pushchain/push-chain-node/types" uexecutor "github.com/pushchain/push-chain-node/x/uexecutor" @@ -178,8 +174,10 @@ import ( tokenfactorybindings "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/bindings" tokenfactorykeeper "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/keeper" tokenfactorytypes "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/types" - ibccallbacks "github.com/cosmos/ibc-go/v10/modules/apps/callbacks" + ucallback "github.com/pushchain/push-chain-node/x/ucallback" + ucallbackkeeper "github.com/pushchain/push-chain-node/x/ucallback/keeper" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) const ( @@ -337,6 +335,7 @@ type ChainApp struct { UregistryKeeper uregistrykeeper.Keeper UvalidatorKeeper uvalidatorkeeper.Keeper UtssKeeper utsskeeper.Keeper + UcallbackKeeper ucallbackkeeper.Keeper // the module manager ModuleManager *module.Manager @@ -451,6 +450,7 @@ func NewChainApp( uregistrytypes.StoreKey, uvalidatortypes.StoreKey, utsstypes.StoreKey, + ucallbacktypes.StoreKey, ) tkeys := storetypes.NewTransientStoreKeys( @@ -684,6 +684,14 @@ func NewChainApp( // If evidence needs to be handled for the app, set routes in router here and seal app.EvidenceKeeper = *evidenceKeeper + // Create the ucallback Keeper + app.UcallbackKeeper = ucallbackkeeper.NewKeeper( + appCodec, + runtime.NewKVStoreService(keys[ucallbacktypes.StoreKey]), + logger, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.FeeMarketKeeper = feemarketkeeper.NewKeeper( appCodec, authtypes.NewModuleAddress(govtypes.ModuleName), @@ -1061,6 +1069,8 @@ func NewChainApp( uregistry.NewAppModule(appCodec, app.UregistryKeeper, app.EVMKeeper), uvalidator.NewAppModule(appCodec, app.UvalidatorKeeper, app.BankKeeper, app.AccountKeeper, app.DistrKeeper, app.StakingKeeper, app.SlashingKeeper, &app.UtssKeeper), utss.NewAppModule(appCodec, app.UtssKeeper, app.UvalidatorKeeper), + ucallback.NewAppModule(appCodec, app.UcallbackKeeper), + ) // BasicModuleManager defines the module BasicManager is in charge of setting up basic, @@ -1111,6 +1121,7 @@ func NewChainApp( uexecutortypes.ModuleName, uregistrytypes.ModuleName, utsstypes.ModuleName, + ucallbacktypes.ModuleName, ) app.ModuleManager.SetOrderEndBlockers( @@ -1134,6 +1145,7 @@ func NewChainApp( uregistrytypes.ModuleName, uvalidatortypes.ModuleName, utsstypes.ModuleName, + ucallbacktypes.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -1184,6 +1196,7 @@ func NewChainApp( uregistrytypes.ModuleName, uvalidatortypes.ModuleName, utsstypes.ModuleName, + ucallbacktypes.ModuleName, } app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) @@ -1663,6 +1676,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(uregistrytypes.ModuleName) paramsKeeper.Subspace(uvalidatortypes.ModuleName) paramsKeeper.Subspace(utsstypes.ModuleName) + paramsKeeper.Subspace(ucallbacktypes.ModuleName) return paramsKeeper } diff --git a/proto/ucallback/module/v1/module.proto b/proto/ucallback/module/v1/module.proto new file mode 100755 index 00000000..09ef40ac --- /dev/null +++ b/proto/ucallback/module/v1/module.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package ucallback.module.v1; + +import "cosmos/app/v1alpha1/module.proto"; + +// Module is the app config object of the module. +// Learn more: https://docs.cosmos.network/main/building-modules/depinject +message Module { + option (cosmos.app.v1alpha1.module) = { + go_import : "github.com/pushchain/push-chain-node" + }; +} \ No newline at end of file diff --git a/proto/ucallback/v1/genesis.proto b/proto/ucallback/v1/genesis.proto new file mode 100755 index 00000000..352700b6 --- /dev/null +++ b/proto/ucallback/v1/genesis.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package ucallback.v1; + +import "gogoproto/gogo.proto"; +import "amino/amino.proto"; + +option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; + +// GenesisState defines the module genesis state +message GenesisState { + // Params defines all the parameters of the module. + Params params = 1 [(gogoproto.nullable) = false]; +} + +// Params defines the set of module parameters. +message Params { + option (amino.name) = "ucallback/params"; + option (gogoproto.equal) = true; + option (gogoproto.goproto_stringer) = false; + + bool some_value = 2; +} \ No newline at end of file diff --git a/proto/ucallback/v1/query.proto b/proto/ucallback/v1/query.proto new file mode 100755 index 00000000..edd7cb94 --- /dev/null +++ b/proto/ucallback/v1/query.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package ucallback.v1; + +import "google/api/annotations.proto"; +import "ucallback/v1/genesis.proto"; + +option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; + +// Query provides defines the gRPC querier service. +service Query { + // Params queries all parameters of the module. + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/ucallback/v1/params"; + } +} + +// QueryParamsRequest is the request type for the Query/Params RPC method. +message QueryParamsRequest {} + +// QueryParamsResponse is the response type for the Query/Params RPC method. +message QueryParamsResponse { + // params defines the parameters of the module. + Params params = 1; +} diff --git a/proto/ucallback/v1/tx.proto b/proto/ucallback/v1/tx.proto new file mode 100755 index 00000000..cccc87d3 --- /dev/null +++ b/proto/ucallback/v1/tx.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; +package ucallback.v1; + +import "cosmos/msg/v1/msg.proto"; +import "ucallback/v1/genesis.proto"; +import "gogoproto/gogo.proto"; +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; + +// Msg defines the Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // UpdateParams defines a governance operation for updating the parameters. + // + // Since: cosmos-sdk 0.47 + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address of the governance account. + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // params defines the parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParamsResponse {} diff --git a/x/ucallback/README.md b/x/ucallback/README.md new file mode 100755 index 00000000..17c8d110 --- /dev/null +++ b/x/ucallback/README.md @@ -0,0 +1,3 @@ +# Example Module + +This is a module base generated with [`spawn`](https://github.com/rollchains/spawn). \ No newline at end of file diff --git a/x/ucallback/autocli.go b/x/ucallback/autocli.go new file mode 100755 index 00000000..13e9e7a6 --- /dev/null +++ b/x/ucallback/autocli.go @@ -0,0 +1,31 @@ +package module + +import ( + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + modulev1 "github.com/pushchain/push-chain-node/api/ucallback/v1" +) + +// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface. +func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { + return &autocliv1.ModuleOptions{ + Query: &autocliv1.ServiceCommandDescriptor{ + Service: modulev1.Query_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Params", + Use: "params", + Short: "Query the current consensus parameters", + }, + }, + }, + Tx: &autocliv1.ServiceCommandDescriptor{ + Service: modulev1.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "UpdateParams", + Skip: false, // set to true if authority gated + }, + }, + }, + } +} diff --git a/x/ucallback/client/cli/query.go b/x/ucallback/client/cli/query.go new file mode 100755 index 00000000..3ac724db --- /dev/null +++ b/x/ucallback/client/cli/query.go @@ -0,0 +1,50 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +// !NOTE: Must enable in module.go (disabled in favor of autocli.go) + +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for " + types.ModuleName, + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + queryCmd.AddCommand( + GetCmdParams(), + ) + return queryCmd +} + +func GetCmdParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Show all module params", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/ucallback/client/cli/tx.go b/x/ucallback/client/cli/tx.go new file mode 100755 index 00000000..6cdf69fd --- /dev/null +++ b/x/ucallback/client/cli/tx.go @@ -0,0 +1,71 @@ +package cli + +import ( + "strconv" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +// !NOTE: Must enable in module.go (disabled in favor of autocli.go) + +// NewTxCmd returns a root CLI command handler for certain modules +// transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: types.ModuleName + " subcommands.", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + MsgUpdateParams(), + ) + return txCmd +} + +// Returns a CLI command handler for registering a +// contract for the module. +func MsgUpdateParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "update-params [some-value]", + Short: "Update the params (must be submitted from the authority)", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + senderAddress := cliCtx.GetFromAddress() + + someValue, err := strconv.ParseBool(args[0]) + if err != nil { + return err + } + + msg := &types.MsgUpdateParams{ + Authority: senderAddress.String(), + Params: types.Params{ + SomeValue: someValue, + }, + } + + if err := msg.Validate(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(cliCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/ucallback/depinject.go b/x/ucallback/depinject.go new file mode 100755 index 00000000..53ce9a93 --- /dev/null +++ b/x/ucallback/depinject.go @@ -0,0 +1,63 @@ +package module + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/codec" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + + "cosmossdk.io/core/address" + "cosmossdk.io/core/appmodule" + "cosmossdk.io/core/store" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + + modulev1 "github.com/pushchain/push-chain-node/api/ucallback/module/v1" + "github.com/pushchain/push-chain-node/x/ucallback/keeper" +) + +var _ appmodule.AppModule = AppModule{} + +// IsOnePerModuleType implements the depinject.OnePerModuleType interface. +func (am AppModule) IsOnePerModuleType() {} + +// IsAppModule implements the appmodule.AppModule interface. +func (am AppModule) IsAppModule() {} + +func init() { + appmodule.Register( + &modulev1.Module{}, + appmodule.Provide(ProvideModule), + ) +} + +type ModuleInputs struct { + depinject.In + + Cdc codec.Codec + StoreService store.KVStoreService + AddressCodec address.Codec + + StakingKeeper stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper +} + +type ModuleOutputs struct { + depinject.Out + + Module appmodule.AppModule + Keeper keeper.Keeper +} + +func ProvideModule(in ModuleInputs) ModuleOutputs { + govAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() + + k := keeper.NewKeeper(in.Cdc, in.StoreService, log.NewLogger(os.Stderr), govAddr) + m := NewAppModule(in.Cdc, k) + + return ModuleOutputs{Module: m, Keeper: k, Out: depinject.Out{}} +} diff --git a/x/ucallback/keeper/genesis.go b/x/ucallback/keeper/genesis.go new file mode 100644 index 00000000..e2f1b519 --- /dev/null +++ b/x/ucallback/keeper/genesis.go @@ -0,0 +1,28 @@ +package keeper + +import ( + "context" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +// InitGenesis initializes the module's state from a genesis state. +func (k *Keeper) InitGenesis(ctx context.Context, data *types.GenesisState) error { + if err := data.Params.Validate(); err != nil { + return err + } + + return k.Params.Set(ctx, data.Params) +} + +// ExportGenesis exports the module's state to a genesis state. +func (k *Keeper) ExportGenesis(ctx context.Context) *types.GenesisState { + params, err := k.Params.Get(ctx) + if err != nil { + panic(err) + } + + return &types.GenesisState{ + Params: params, + } +} diff --git a/x/ucallback/keeper/genesis_test.go b/x/ucallback/keeper/genesis_test.go new file mode 100755 index 00000000..210cd36a --- /dev/null +++ b/x/ucallback/keeper/genesis_test.go @@ -0,0 +1,22 @@ +package keeper_test + +import ( + "testing" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + "github.com/stretchr/testify/require" +) + +func TestGenesis(t *testing.T) { + f := SetupTest(t) + + genesisState := &types.GenesisState{ + Params: types.DefaultParams(), + } + + f.k.InitGenesis(f.ctx, genesisState) + + got := f.k.ExportGenesis(f.ctx) + require.NotNil(t, got) + +} diff --git a/x/ucallback/keeper/keeper.go b/x/ucallback/keeper/keeper.go new file mode 100755 index 00000000..7062f0f4 --- /dev/null +++ b/x/ucallback/keeper/keeper.go @@ -0,0 +1,64 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "cosmossdk.io/collections" + storetypes "cosmossdk.io/core/store" + "cosmossdk.io/log" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + + logger log.Logger + + // state management + Schema collections.Schema + Params collections.Item[types.Params] + + authority string +} + +// NewKeeper creates a new Keeper instance +func NewKeeper( + cdc codec.BinaryCodec, + storeService storetypes.KVStoreService, + logger log.Logger, + authority string, +) Keeper { + logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) + + sb := collections.NewSchemaBuilder(storeService) + + if authority == "" { + authority = authtypes.NewModuleAddress(govtypes.ModuleName).String() + } + + k := Keeper{ + cdc: cdc, + logger: logger, + + Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), + + authority: authority, + } + + schema, err := sb.Build() + if err != nil { + panic(err) + } + + k.Schema = schema + + return k +} + +func (k Keeper) Logger() log.Logger { + return k.logger +} diff --git a/x/ucallback/keeper/keeper_test.go b/x/ucallback/keeper/keeper_test.go new file mode 100755 index 00000000..7c1d711a --- /dev/null +++ b/x/ucallback/keeper/keeper_test.go @@ -0,0 +1,148 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "cosmossdk.io/core/address" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdkaddress "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/integration" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/pushchain/push-chain-node/app" + module "github.com/pushchain/push-chain-node/x/ucallback" + "github.com/pushchain/push-chain-node/x/ucallback/keeper" + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +var maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + minttypes.ModuleName: {authtypes.Minter}, + govtypes.ModuleName: {authtypes.Burner}, +} + +type testFixture struct { + suite.Suite + + ctx sdk.Context + k keeper.Keeper + msgServer types.MsgServer + queryServer types.QueryServer + appModule *module.AppModule + + accountkeeper authkeeper.AccountKeeper + bankkeeper bankkeeper.BaseKeeper + stakingKeeper *stakingkeeper.Keeper + mintkeeper mintkeeper.Keeper + + addrs []sdk.AccAddress + govModAddr string +} + +func SetupTest(t *testing.T) *testFixture { + t.Helper() + f := new(testFixture) + + cfg := sdk.GetConfig() // do not seal, more set later + cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(app.Bech32PrefixValAddr, app.Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) + cfg.SetCoinType(app.CoinType) + + validatorAddressCodec := sdkaddress.NewBech32Codec(app.Bech32PrefixValAddr) + accountAddressCodec := sdkaddress.NewBech32Codec(app.Bech32PrefixAccAddr) + consensusAddressCodec := sdkaddress.NewBech32Codec(app.Bech32PrefixConsAddr) + + // Base setup + logger := log.NewTestLogger(t) + encCfg := moduletestutil.MakeTestEncodingConfig() + + f.govModAddr = authtypes.NewModuleAddress(govtypes.ModuleName).String() + f.addrs = simtestutil.CreateIncrementalAccounts(3) + + keys := storetypes.NewKVStoreKeys(authtypes.ModuleName, banktypes.ModuleName, stakingtypes.ModuleName, minttypes.ModuleName, types.ModuleName) + f.ctx = sdk.NewContext(integration.CreateMultiStore(keys, logger), cmtproto.Header{}, false, logger) + + // Register SDK modules. + registerBaseSDKModules(logger, f, encCfg, keys, accountAddressCodec, validatorAddressCodec, consensusAddressCodec) + + // Setup Keeper. + f.k = keeper.NewKeeper(encCfg.Codec, runtime.NewKVStoreService(keys[types.ModuleName]), logger, f.govModAddr) + f.msgServer = keeper.NewMsgServerImpl(f.k) + f.queryServer = keeper.NewQuerier(f.k) + f.appModule = module.NewAppModule(encCfg.Codec, f.k) + + return f +} + +func registerModuleInterfaces(encCfg moduletestutil.TestEncodingConfig) { + authtypes.RegisterInterfaces(encCfg.InterfaceRegistry) + stakingtypes.RegisterInterfaces(encCfg.InterfaceRegistry) + banktypes.RegisterInterfaces(encCfg.InterfaceRegistry) + minttypes.RegisterInterfaces(encCfg.InterfaceRegistry) + + types.RegisterInterfaces(encCfg.InterfaceRegistry) +} + +func registerBaseSDKModules( + logger log.Logger, + f *testFixture, + encCfg moduletestutil.TestEncodingConfig, + keys map[string]*storetypes.KVStoreKey, + ac address.Codec, + validator address.Codec, + consensus address.Codec, +) { + registerModuleInterfaces(encCfg) + + // Auth Keeper. + f.accountkeeper = authkeeper.NewAccountKeeper( + encCfg.Codec, runtime.NewKVStoreService(keys[authtypes.StoreKey]), + authtypes.ProtoBaseAccount, + maccPerms, + ac, app.Bech32PrefixAccAddr, + f.govModAddr, + ) + + // Bank Keeper. + f.bankkeeper = bankkeeper.NewBaseKeeper( + encCfg.Codec, runtime.NewKVStoreService(keys[banktypes.StoreKey]), + f.accountkeeper, + nil, + f.govModAddr, logger, + ) + + // Staking Keeper. + f.stakingKeeper = stakingkeeper.NewKeeper( + encCfg.Codec, runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), + f.accountkeeper, f.bankkeeper, f.govModAddr, + validator, + consensus, + ) + + // Mint Keeper. + f.mintkeeper = mintkeeper.NewKeeper( + encCfg.Codec, runtime.NewKVStoreService(keys[minttypes.StoreKey]), + f.stakingKeeper, f.accountkeeper, f.bankkeeper, + authtypes.FeeCollectorName, f.govModAddr, + ) +} diff --git a/x/ucallback/keeper/msg_server.go b/x/ucallback/keeper/msg_server.go new file mode 100755 index 00000000..57521b3c --- /dev/null +++ b/x/ucallback/keeper/msg_server.go @@ -0,0 +1,29 @@ +package keeper + +import ( + "context" + + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "cosmossdk.io/errors" + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +type msgServer struct { + k Keeper +} + +var _ types.MsgServer = msgServer{} + +// NewMsgServerImpl returns an implementation of the module MsgServer interface. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{k: keeper} +} + +func (ms msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if ms.k.authority != msg.Authority { + return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.k.authority, msg.Authority) + } + + return nil, ms.k.Params.Set(ctx, msg.Params) +} diff --git a/x/ucallback/keeper/msg_server_test.go b/x/ucallback/keeper/msg_server_test.go new file mode 100755 index 00000000..87fb2088 --- /dev/null +++ b/x/ucallback/keeper/msg_server_test.go @@ -0,0 +1,56 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +func TestParams(t *testing.T) { + f := SetupTest(t) + require := require.New(t) + + testCases := []struct { + name string + request *types.MsgUpdateParams + err bool + }{ + { + name: "fail; invalid authority", + request: &types.MsgUpdateParams{ + Authority: f.addrs[0].String(), + Params: types.DefaultParams(), + }, + err: true, + }, + { + name: "success", + request: &types.MsgUpdateParams{ + Authority: f.govModAddr, + Params: types.DefaultParams(), + }, + err: false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + _, err := f.msgServer.UpdateParams(f.ctx, tc.request) + + if tc.err { + require.Error(err) + } else { + require.NoError(err) + + r, err := f.queryServer.Params(f.ctx, &types.QueryParamsRequest{}) + require.NoError(err) + + require.EqualValues(&tc.request.Params, r.Params) + } + + }) + } +} diff --git a/x/ucallback/keeper/query_server.go b/x/ucallback/keeper/query_server.go new file mode 100755 index 00000000..498c2f03 --- /dev/null +++ b/x/ucallback/keeper/query_server.go @@ -0,0 +1,30 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +var _ types.QueryServer = Querier{} + +type Querier struct { + Keeper +} + +func NewQuerier(keeper Keeper) Querier { + return Querier{Keeper: keeper} +} + +func (k Querier) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + p, err := k.Keeper.Params.Get(ctx) + if err != nil { + return nil, err + } + + return &types.QueryParamsResponse{Params: &p}, nil +} diff --git a/x/ucallback/module.go b/x/ucallback/module.go new file mode 100755 index 00000000..c613f9ce --- /dev/null +++ b/x/ucallback/module.go @@ -0,0 +1,150 @@ +package module + +import ( + "context" + "encoding/json" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + abci "github.com/cometbft/cometbft/abci/types" + + "cosmossdk.io/client/v2/autocli" + errorsmod "cosmossdk.io/errors" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/pushchain/push-chain-node/x/ucallback/keeper" + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +const ( + // ConsensusVersion defines the current x/ucallback module consensus version. + ConsensusVersion = 1 +) + +var ( + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleGenesis = AppModule{} + _ module.AppModule = AppModule{} + + _ autocli.HasAutoCLIConfig = AppModule{} +) + +// AppModuleBasic defines the basic application module used by the wasm module. +type AppModuleBasic struct { + cdc codec.Codec +} + +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper +} + +// NewAppModule constructor +func NewAppModule( + cdc codec.Codec, + keeper keeper.Keeper, +) *AppModule { + return &AppModule{ + AppModuleBasic: AppModuleBasic{cdc: cdc}, + keeper: keeper, + } +} + +func (a AppModuleBasic) Name() string { + return types.ModuleName +} + +func (a AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(&types.GenesisState{ + Params: types.DefaultParams(), + }) +} + +func (a AppModuleBasic) ValidateGenesis(marshaler codec.JSONCodec, _ client.TxEncodingConfig, message json.RawMessage) error { + var data types.GenesisState + err := marshaler.UnmarshalJSON(message, &data) + if err != nil { + return err + } + if err := data.Params.Validate(); err != nil { + return errorsmod.Wrap(err, "params") + } + return nil +} + +func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) { +} + +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + if err != nil { + // same behavior as in cosmos-sdk + panic(err) + } +} + +// Disable in favor of autocli.go. If you wish to use these, it will override AutoCLI methods. +/* +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} +*/ + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +func (a AppModuleBasic) RegisterInterfaces(r codectypes.InterfaceRegistry) { + types.RegisterInterfaces(r) +} + +func (a AppModule) InitGenesis(ctx sdk.Context, marshaler codec.JSONCodec, message json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + marshaler.MustUnmarshalJSON(message, &genesisState) + + if err := a.keeper.Params.Set(ctx, genesisState.Params); err != nil { + panic(err) + } + + if err := a.keeper.InitGenesis(ctx, &genesisState); err != nil { + panic(err) + } + + return nil +} + +func (a AppModule) ExportGenesis(ctx sdk.Context, marshaler codec.JSONCodec) json.RawMessage { + genState := a.keeper.ExportGenesis(ctx) + return marshaler.MustMarshalJSON(genState) +} + +func (a AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +func (a AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +func (a AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(a.keeper)) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQuerier(a.keeper)) +} + +// ConsensusVersion is a sequence number for state-breaking change of the +// module. It should be incremented on each consensus-breaking change +// introduced by the module. To avoid wrong/empty versions, the initial version +// should be set to 1. +func (a AppModule) ConsensusVersion() uint64 { + return ConsensusVersion +} diff --git a/x/ucallback/types/codec.go b/x/ucallback/types/codec.go new file mode 100755 index 00000000..6d57a9e3 --- /dev/null +++ b/x/ucallback/types/codec.go @@ -0,0 +1,35 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +var ( + amino = codec.NewLegacyAmino() + AminoCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) +} + +// RegisterLegacyAminoCodec registers concrete types on the LegacyAmino codec +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgUpdateParams{}, ModuleName+"/MsgUpdateParams", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/ucallback/types/genesis.go b/x/ucallback/types/genesis.go new file mode 100755 index 00000000..97cad761 --- /dev/null +++ b/x/ucallback/types/genesis.go @@ -0,0 +1,19 @@ +package types + +// DefaultIndex is the default global index +const DefaultIndex uint64 = 1 + +// DefaultGenesis returns the default genesis state +func DefaultGenesis() *GenesisState { + return &GenesisState{ + + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + + return gs.Params.Validate() +} diff --git a/x/ucallback/types/genesis.pb.go b/x/ucallback/types/genesis.pb.go new file mode 100644 index 00000000..dff37178 --- /dev/null +++ b/x/ucallback/types/genesis.pb.go @@ -0,0 +1,511 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ucallback/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the module genesis state +type GenesisState struct { + // Params defines all the parameters of the module. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_13c1287495624272, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// Params defines the set of module parameters. +type Params struct { + SomeValue bool `protobuf:"varint,2,opt,name=some_value,json=someValue,proto3" json:"some_value,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_13c1287495624272, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetSomeValue() bool { + if m != nil { + return m.SomeValue + } + return false +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "ucallback.v1.GenesisState") + proto.RegisterType((*Params)(nil), "ucallback.v1.Params") +} + +func init() { proto.RegisterFile("ucallback/v1/genesis.proto", fileDescriptor_13c1287495624272) } + +var fileDescriptor_13c1287495624272 = []byte{ + // 257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2a, 0x4d, 0x4e, 0xcc, + 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x81, 0xcb, 0xe9, 0x95, 0x19, 0x4a, 0x89, 0xa4, + 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf4, 0x41, 0x2c, 0x88, 0x1a, 0x29, 0xc1, 0xc4, 0xdc, 0xcc, 0xbc, + 0x7c, 0x7d, 0x30, 0x09, 0x11, 0x52, 0x72, 0xe2, 0xe2, 0x71, 0x87, 0x98, 0x13, 0x5c, 0x92, 0x58, + 0x92, 0x2a, 0x64, 0xc4, 0xc5, 0x56, 0x90, 0x58, 0x94, 0x98, 0x5b, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, + 0xc1, 0x6d, 0x24, 0xa2, 0x87, 0x6c, 0xae, 0x5e, 0x00, 0x58, 0xce, 0x89, 0xe5, 0xc4, 0x3d, 0x79, + 0x86, 0x20, 0xa8, 0x4a, 0x25, 0x37, 0x2e, 0x36, 0x88, 0xb8, 0x90, 0x2c, 0x17, 0x57, 0x71, 0x7e, + 0x6e, 0x6a, 0x7c, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x27, + 0x48, 0x24, 0x0c, 0x24, 0x60, 0x25, 0x3b, 0x63, 0x81, 0x3c, 0xc3, 0x8b, 0x05, 0xf2, 0x8c, 0x5d, + 0xcf, 0x37, 0x68, 0x09, 0x20, 0x3c, 0x03, 0x31, 0xc7, 0x29, 0xe0, 0xc4, 0x23, 0x39, 0xc6, 0x0b, + 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, + 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xcc, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, + 0xf5, 0x0b, 0x4a, 0x8b, 0x33, 0x92, 0x33, 0x12, 0x33, 0xf3, 0xc0, 0x2c, 0x5d, 0x30, 0x53, 0x37, + 0x2f, 0x3f, 0x25, 0x55, 0xbf, 0x42, 0x1f, 0x61, 0x64, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, + 0xd8, 0x93, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd8, 0xe9, 0x3c, 0x8c, 0x39, 0x01, 0x00, + 0x00, +} + +func (this *Params) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Params) + if !ok { + that2, ok := that.(Params) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.SomeValue != that1.SomeValue { + return false + } + return true +} +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SomeValue { + i-- + if m.SomeValue { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SomeValue { + n += 2 + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SomeValue", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SomeValue = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ucallback/types/genesis_test.go b/x/ucallback/types/genesis_test.go new file mode 100755 index 00000000..4b2c37e4 --- /dev/null +++ b/x/ucallback/types/genesis_test.go @@ -0,0 +1,38 @@ +package types_test + +import ( + "testing" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + + "github.com/stretchr/testify/require" +) + +func TestGenesisState_Validate(t *testing.T) { + tests := []struct { + desc string + genState *types.GenesisState + valid bool + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + valid: true, + }, + { + desc: "valid genesis state", + genState: &types.GenesisState{}, + valid: true, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + if tc.valid { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/x/ucallback/types/keys.go b/x/ucallback/types/keys.go new file mode 100755 index 00000000..001e881e --- /dev/null +++ b/x/ucallback/types/keys.go @@ -0,0 +1,18 @@ +package types + +import ( + "cosmossdk.io/collections" +) + +var ( + // ParamsKey saves the current module params. + ParamsKey = collections.NewPrefix(0) +) + +const ( + ModuleName = "ucallback" + + StoreKey = ModuleName + + QuerierRoute = ModuleName +) diff --git a/x/ucallback/types/msgs.go b/x/ucallback/types/msgs.go new file mode 100755 index 00000000..360797d6 --- /dev/null +++ b/x/ucallback/types/msgs.go @@ -0,0 +1,49 @@ +package types + +import ( + "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ sdk.Msg = &MsgUpdateParams{} +) + +// NewMsgUpdateParams creates new instance of MsgUpdateParams +func NewMsgUpdateParams( + sender sdk.Address, + someValue bool, +) *MsgUpdateParams { + return &MsgUpdateParams{ + Authority: sender.String(), + Params: Params{ + SomeValue: someValue, + }, + } +} + +// Route returns the name of the module +func (msg MsgUpdateParams) Route() string { return ModuleName } + +// Type returns the the action +func (msg MsgUpdateParams) Type() string { return "update_params" } + +// GetSignBytes implements the LegacyMsg interface. +func (msg MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&msg)) +} + +// GetSigners returns the expected signers for a MsgUpdateParams message. +func (msg *MsgUpdateParams) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Authority) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check on the provided data. +func (msg *MsgUpdateParams) Validate() error { + if _, err := sdk.AccAddressFromBech32(msg.Authority); err != nil { + return errors.Wrap(err, "invalid authority address") + } + + return msg.Params.Validate() +} diff --git a/x/ucallback/types/params.go b/x/ucallback/types/params.go new file mode 100755 index 00000000..d9be77ae --- /dev/null +++ b/x/ucallback/types/params.go @@ -0,0 +1,29 @@ +package types + +import ( + "encoding/json" +) + +// DefaultParams returns default module parameters. +func DefaultParams() Params { + // TODO: + return Params{ + SomeValue: true, + } +} + +// Stringer method for Params. +func (p Params) String() string { + bz, err := json.Marshal(p) + if err != nil { + panic(err) + } + + return string(bz) +} + +// Validate does the sanity check on the params. +func (p Params) Validate() error { + // TODO: + return nil +} diff --git a/x/ucallback/types/query.pb.go b/x/ucallback/types/query.pb.go new file mode 100644 index 00000000..0f81b76c --- /dev/null +++ b/x/ucallback/types/query.pb.go @@ -0,0 +1,540 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ucallback/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params defines the parameters of the module. + Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() *Params { + if m != nil { + return m.Params + } + return nil +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "ucallback.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "ucallback.v1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("ucallback/v1/query.proto", fileDescriptor_a64b97cfcca36b9d) } + +var fileDescriptor_a64b97cfcca36b9d = []byte{ + // 265 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x28, 0x4d, 0x4e, 0xcc, + 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, + 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x81, 0xcb, 0xe8, 0x95, 0x19, 0x4a, 0xc9, 0xa4, 0xe7, 0xe7, + 0xa7, 0xe7, 0xa4, 0xea, 0x27, 0x16, 0x64, 0xea, 0x27, 0xe6, 0xe5, 0xe5, 0x97, 0x24, 0x96, 0x64, + 0xe6, 0xe7, 0x15, 0x43, 0xd4, 0x4a, 0x49, 0xa1, 0x98, 0x92, 0x9e, 0x9a, 0x97, 0x5a, 0x9c, 0x09, + 0x95, 0x53, 0x12, 0xe1, 0x12, 0x0a, 0x04, 0x19, 0x1b, 0x90, 0x58, 0x94, 0x98, 0x5b, 0x1c, 0x94, + 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0xa2, 0xe4, 0xcc, 0x25, 0x8c, 0x22, 0x5a, 0x5c, 0x90, 0x9f, 0x57, + 0x9c, 0x2a, 0xa4, 0xc3, 0xc5, 0x56, 0x00, 0x16, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x12, + 0xd1, 0x43, 0x76, 0x85, 0x1e, 0x54, 0x35, 0x54, 0x8d, 0x51, 0x09, 0x17, 0x2b, 0xd8, 0x10, 0xa1, + 0x6c, 0x2e, 0x36, 0x88, 0x94, 0x90, 0x02, 0xaa, 0x06, 0x4c, 0x9b, 0xa5, 0x14, 0xf1, 0xa8, 0x80, + 0xb8, 0x42, 0x49, 0xa6, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, 0x62, 0x42, 0x22, 0xfa, 0x28, 0xfe, 0x82, + 0xd8, 0xea, 0x14, 0x70, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, + 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x66, 0xe9, + 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x05, 0xa5, 0xc5, 0x19, 0xc9, 0x19, + 0x89, 0x99, 0x79, 0x60, 0x96, 0x2e, 0x98, 0xa9, 0x9b, 0x97, 0x9f, 0x92, 0xaa, 0x5f, 0x81, 0x64, + 0x6a, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0xa4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, + 0xff, 0x4b, 0x9f, 0xf7, 0xc2, 0x8d, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Params queries all parameters of the module. + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params queries all parameters of the module. + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ucallback.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ucallback/v1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Params != nil { + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Params != nil { + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Params == nil { + m.Params = &Params{} + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/ucallback/types/query.pb.gw.go b/x/ucallback/types/query.pb.gw.go new file mode 100644 index 00000000..423a5545 --- /dev/null +++ b/x/ucallback/types/query.pb.gw.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ucallback/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"ucallback", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/ucallback/types/tx.pb.go b/x/ucallback/types/tx.pb.go new file mode 100644 index 00000000..dc232bcd --- /dev/null +++ b/x/ucallback/types/tx.pb.go @@ -0,0 +1,602 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ucallback/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParams struct { + // authority is the address of the governance account. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_9cc90e16cf6966ee, []int{0} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9cc90e16cf6966ee, []int{1} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUpdateParams)(nil), "ucallback.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "ucallback.v1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("ucallback/v1/tx.proto", fileDescriptor_9cc90e16cf6966ee) } + +var fileDescriptor_9cc90e16cf6966ee = []byte{ + // 331 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x4d, 0x4e, 0xcc, + 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0xe2, 0x81, 0x0b, 0xeb, 0x95, 0x19, 0x4a, 0x89, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, + 0xeb, 0xe7, 0x16, 0xa7, 0x83, 0x54, 0xe5, 0x16, 0xa7, 0x43, 0x94, 0x49, 0x49, 0xa1, 0xe8, 0x4e, + 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0x86, 0xca, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x99, 0xfa, + 0x20, 0x16, 0x54, 0x54, 0x12, 0x62, 0x54, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x48, 0x29, 0xf5, 0x32, + 0x72, 0xf1, 0xfb, 0x16, 0xa7, 0x87, 0x16, 0xa4, 0x24, 0x96, 0xa4, 0x06, 0x24, 0x16, 0x25, 0xe6, + 0x16, 0x0b, 0x99, 0x71, 0x71, 0x26, 0x96, 0x96, 0x64, 0xe4, 0x17, 0x65, 0x96, 0x54, 0x4a, 0x30, + 0x2a, 0x30, 0x6a, 0x70, 0x3a, 0x49, 0x5c, 0xda, 0xa2, 0x2b, 0x02, 0xd5, 0xe8, 0x98, 0x92, 0x52, + 0x94, 0x5a, 0x5c, 0x1c, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x1e, 0x84, 0x50, 0x2a, 0x64, 0xc4, 0xc5, + 0x56, 0x00, 0x36, 0x41, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0xd9, 0x43, 0x7a, + 0x10, 0xd3, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xaa, 0xb4, 0xe2, 0x6b, 0x7a, 0xbe, + 0x41, 0x0b, 0x61, 0x86, 0x92, 0x24, 0x97, 0x38, 0x9a, 0x73, 0x82, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, + 0x8a, 0x53, 0x8d, 0x92, 0xb8, 0x98, 0x7d, 0x8b, 0xd3, 0x85, 0x42, 0xb8, 0x78, 0x50, 0x5c, 0x2b, + 0x8b, 0x6a, 0x0b, 0x9a, 0x6e, 0x29, 0x55, 0xbc, 0xd2, 0x30, 0xc3, 0xa5, 0x58, 0x1b, 0x9e, 0x6f, + 0xd0, 0x62, 0x74, 0x0a, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, + 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xb3, + 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0x82, 0xd2, 0xe2, 0x8c, 0xe4, + 0x8c, 0xc4, 0xcc, 0x3c, 0x30, 0x4b, 0x17, 0xcc, 0xd4, 0xcd, 0xcb, 0x4f, 0x49, 0xd5, 0xaf, 0xd0, + 0x47, 0x44, 0x4e, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0x9c, 0x8d, 0x01, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x0a, 0x23, 0xf0, 0x0e, 0xf4, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // UpdateParams defines a governance operation for updating the parameters. + // + // Since: cosmos-sdk 0.47 + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // UpdateParams defines a governance operation for updating the parameters. + // + // Since: cosmos-sdk 0.47 + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ucallback.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ucallback/v1/tx.proto", +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) From d0ad57209f1765298386f4c03515c0203db51a15 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 10:37:57 +0530 Subject: [PATCH 27/54] feat(ucallback): add read-state types UniversalRead, ReadRequest and ReadResult. ReadRequest mirrors the fields universalClient expects; ReadResult carries no error text so ERROR ballots converge. Aggregates reserved for v2 median support. --- api/ucallback/v1/types.pulsar.go | 3992 ++++++++++++++++++++++++++++++ proto/ucallback/v1/types.proto | 123 + x/ucallback/types/types.pb.go | 2280 +++++++++++++++++ 3 files changed, 6395 insertions(+) create mode 100644 api/ucallback/v1/types.pulsar.go create mode 100644 proto/ucallback/v1/types.proto create mode 100644 x/ucallback/types/types.pb.go diff --git a/api/ucallback/v1/types.pulsar.go b/api/ucallback/v1/types.pulsar.go new file mode 100644 index 00000000..f53b9d4d --- /dev/null +++ b/api/ucallback/v1/types.pulsar.go @@ -0,0 +1,3992 @@ +// Code generated by protoc-gen-go-pulsar. DO NOT EDIT. +package ucallbackv1 + +import ( + _ "cosmossdk.io/api/amino" + fmt "fmt" + runtime "github.com/cosmos/cosmos-proto/runtime" + _ "github.com/cosmos/gogoproto/gogoproto" + v1 "github.com/pushchain/push-chain-node/api/uexecutor/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + reflect "reflect" + sync "sync" +) + +var ( + md_ReadRequest protoreflect.MessageDescriptor + fd_ReadRequest_request_id protoreflect.FieldDescriptor + fd_ReadRequest_destination_chain protoreflect.FieldDescriptor + fd_ReadRequest_owner protoreflect.FieldDescriptor + fd_ReadRequest_query protoreflect.FieldDescriptor + fd_ReadRequest_min_confirmations protoreflect.FieldDescriptor + fd_ReadRequest_destination_block_height protoreflect.FieldDescriptor + fd_ReadRequest_expiry_block_height protoreflect.FieldDescriptor + fd_ReadRequest_created_at_height protoreflect.FieldDescriptor + fd_ReadRequest_callback_target protoreflect.FieldDescriptor + fd_ReadRequest_original_funder protoreflect.FieldDescriptor + fd_ReadRequest_fees_deposited protoreflect.FieldDescriptor + fd_ReadRequest_max_fee protoreflect.FieldDescriptor + fd_ReadRequest_requested_tx_hash protoreflect.FieldDescriptor + fd_ReadRequest_requested_log_index protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_types_proto_init() + md_ReadRequest = File_ucallback_v1_types_proto.Messages().ByName("ReadRequest") + fd_ReadRequest_request_id = md_ReadRequest.Fields().ByName("request_id") + fd_ReadRequest_destination_chain = md_ReadRequest.Fields().ByName("destination_chain") + fd_ReadRequest_owner = md_ReadRequest.Fields().ByName("owner") + fd_ReadRequest_query = md_ReadRequest.Fields().ByName("query") + fd_ReadRequest_min_confirmations = md_ReadRequest.Fields().ByName("min_confirmations") + fd_ReadRequest_destination_block_height = md_ReadRequest.Fields().ByName("destination_block_height") + fd_ReadRequest_expiry_block_height = md_ReadRequest.Fields().ByName("expiry_block_height") + fd_ReadRequest_created_at_height = md_ReadRequest.Fields().ByName("created_at_height") + fd_ReadRequest_callback_target = md_ReadRequest.Fields().ByName("callback_target") + fd_ReadRequest_original_funder = md_ReadRequest.Fields().ByName("original_funder") + fd_ReadRequest_fees_deposited = md_ReadRequest.Fields().ByName("fees_deposited") + fd_ReadRequest_max_fee = md_ReadRequest.Fields().ByName("max_fee") + fd_ReadRequest_requested_tx_hash = md_ReadRequest.Fields().ByName("requested_tx_hash") + fd_ReadRequest_requested_log_index = md_ReadRequest.Fields().ByName("requested_log_index") +} + +var _ protoreflect.Message = (*fastReflection_ReadRequest)(nil) + +type fastReflection_ReadRequest ReadRequest + +func (x *ReadRequest) ProtoReflect() protoreflect.Message { + return (*fastReflection_ReadRequest)(x) +} + +func (x *ReadRequest) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_types_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_ReadRequest_messageType fastReflection_ReadRequest_messageType +var _ protoreflect.MessageType = fastReflection_ReadRequest_messageType{} + +type fastReflection_ReadRequest_messageType struct{} + +func (x fastReflection_ReadRequest_messageType) Zero() protoreflect.Message { + return (*fastReflection_ReadRequest)(nil) +} +func (x fastReflection_ReadRequest_messageType) New() protoreflect.Message { + return new(fastReflection_ReadRequest) +} +func (x fastReflection_ReadRequest_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_ReadRequest +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_ReadRequest) Descriptor() protoreflect.MessageDescriptor { + return md_ReadRequest +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_ReadRequest) Type() protoreflect.MessageType { + return _fastReflection_ReadRequest_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_ReadRequest) New() protoreflect.Message { + return new(fastReflection_ReadRequest) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_ReadRequest) Interface() protoreflect.ProtoMessage { + return (*ReadRequest)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_ReadRequest) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.RequestId != "" { + value := protoreflect.ValueOfString(x.RequestId) + if !f(fd_ReadRequest_request_id, value) { + return + } + } + if x.DestinationChain != "" { + value := protoreflect.ValueOfString(x.DestinationChain) + if !f(fd_ReadRequest_destination_chain, value) { + return + } + } + if len(x.Owner) != 0 { + value := protoreflect.ValueOfBytes(x.Owner) + if !f(fd_ReadRequest_owner, value) { + return + } + } + if len(x.Query) != 0 { + value := protoreflect.ValueOfBytes(x.Query) + if !f(fd_ReadRequest_query, value) { + return + } + } + if x.MinConfirmations != uint32(0) { + value := protoreflect.ValueOfUint32(x.MinConfirmations) + if !f(fd_ReadRequest_min_confirmations, value) { + return + } + } + if x.DestinationBlockHeight != uint64(0) { + value := protoreflect.ValueOfUint64(x.DestinationBlockHeight) + if !f(fd_ReadRequest_destination_block_height, value) { + return + } + } + if x.ExpiryBlockHeight != uint64(0) { + value := protoreflect.ValueOfUint64(x.ExpiryBlockHeight) + if !f(fd_ReadRequest_expiry_block_height, value) { + return + } + } + if x.CreatedAtHeight != uint64(0) { + value := protoreflect.ValueOfUint64(x.CreatedAtHeight) + if !f(fd_ReadRequest_created_at_height, value) { + return + } + } + if x.CallbackTarget != "" { + value := protoreflect.ValueOfString(x.CallbackTarget) + if !f(fd_ReadRequest_callback_target, value) { + return + } + } + if x.OriginalFunder != "" { + value := protoreflect.ValueOfString(x.OriginalFunder) + if !f(fd_ReadRequest_original_funder, value) { + return + } + } + if x.FeesDeposited != "" { + value := protoreflect.ValueOfString(x.FeesDeposited) + if !f(fd_ReadRequest_fees_deposited, value) { + return + } + } + if x.MaxFee != "" { + value := protoreflect.ValueOfString(x.MaxFee) + if !f(fd_ReadRequest_max_fee, value) { + return + } + } + if x.RequestedTxHash != "" { + value := protoreflect.ValueOfString(x.RequestedTxHash) + if !f(fd_ReadRequest_requested_tx_hash, value) { + return + } + } + if x.RequestedLogIndex != uint64(0) { + value := protoreflect.ValueOfUint64(x.RequestedLogIndex) + if !f(fd_ReadRequest_requested_log_index, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_ReadRequest) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.ReadRequest.request_id": + return x.RequestId != "" + case "ucallback.v1.ReadRequest.destination_chain": + return x.DestinationChain != "" + case "ucallback.v1.ReadRequest.owner": + return len(x.Owner) != 0 + case "ucallback.v1.ReadRequest.query": + return len(x.Query) != 0 + case "ucallback.v1.ReadRequest.min_confirmations": + return x.MinConfirmations != uint32(0) + case "ucallback.v1.ReadRequest.destination_block_height": + return x.DestinationBlockHeight != uint64(0) + case "ucallback.v1.ReadRequest.expiry_block_height": + return x.ExpiryBlockHeight != uint64(0) + case "ucallback.v1.ReadRequest.created_at_height": + return x.CreatedAtHeight != uint64(0) + case "ucallback.v1.ReadRequest.callback_target": + return x.CallbackTarget != "" + case "ucallback.v1.ReadRequest.original_funder": + return x.OriginalFunder != "" + case "ucallback.v1.ReadRequest.fees_deposited": + return x.FeesDeposited != "" + case "ucallback.v1.ReadRequest.max_fee": + return x.MaxFee != "" + case "ucallback.v1.ReadRequest.requested_tx_hash": + return x.RequestedTxHash != "" + case "ucallback.v1.ReadRequest.requested_log_index": + return x.RequestedLogIndex != uint64(0) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadRequest) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.ReadRequest.request_id": + x.RequestId = "" + case "ucallback.v1.ReadRequest.destination_chain": + x.DestinationChain = "" + case "ucallback.v1.ReadRequest.owner": + x.Owner = nil + case "ucallback.v1.ReadRequest.query": + x.Query = nil + case "ucallback.v1.ReadRequest.min_confirmations": + x.MinConfirmations = uint32(0) + case "ucallback.v1.ReadRequest.destination_block_height": + x.DestinationBlockHeight = uint64(0) + case "ucallback.v1.ReadRequest.expiry_block_height": + x.ExpiryBlockHeight = uint64(0) + case "ucallback.v1.ReadRequest.created_at_height": + x.CreatedAtHeight = uint64(0) + case "ucallback.v1.ReadRequest.callback_target": + x.CallbackTarget = "" + case "ucallback.v1.ReadRequest.original_funder": + x.OriginalFunder = "" + case "ucallback.v1.ReadRequest.fees_deposited": + x.FeesDeposited = "" + case "ucallback.v1.ReadRequest.max_fee": + x.MaxFee = "" + case "ucallback.v1.ReadRequest.requested_tx_hash": + x.RequestedTxHash = "" + case "ucallback.v1.ReadRequest.requested_log_index": + x.RequestedLogIndex = uint64(0) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_ReadRequest) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.ReadRequest.request_id": + value := x.RequestId + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.destination_chain": + value := x.DestinationChain + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.owner": + value := x.Owner + return protoreflect.ValueOfBytes(value) + case "ucallback.v1.ReadRequest.query": + value := x.Query + return protoreflect.ValueOfBytes(value) + case "ucallback.v1.ReadRequest.min_confirmations": + value := x.MinConfirmations + return protoreflect.ValueOfUint32(value) + case "ucallback.v1.ReadRequest.destination_block_height": + value := x.DestinationBlockHeight + return protoreflect.ValueOfUint64(value) + case "ucallback.v1.ReadRequest.expiry_block_height": + value := x.ExpiryBlockHeight + return protoreflect.ValueOfUint64(value) + case "ucallback.v1.ReadRequest.created_at_height": + value := x.CreatedAtHeight + return protoreflect.ValueOfUint64(value) + case "ucallback.v1.ReadRequest.callback_target": + value := x.CallbackTarget + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.original_funder": + value := x.OriginalFunder + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.fees_deposited": + value := x.FeesDeposited + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.max_fee": + value := x.MaxFee + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.requested_tx_hash": + value := x.RequestedTxHash + return protoreflect.ValueOfString(value) + case "ucallback.v1.ReadRequest.requested_log_index": + value := x.RequestedLogIndex + return protoreflect.ValueOfUint64(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadRequest) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.ReadRequest.request_id": + x.RequestId = value.Interface().(string) + case "ucallback.v1.ReadRequest.destination_chain": + x.DestinationChain = value.Interface().(string) + case "ucallback.v1.ReadRequest.owner": + x.Owner = value.Bytes() + case "ucallback.v1.ReadRequest.query": + x.Query = value.Bytes() + case "ucallback.v1.ReadRequest.min_confirmations": + x.MinConfirmations = uint32(value.Uint()) + case "ucallback.v1.ReadRequest.destination_block_height": + x.DestinationBlockHeight = value.Uint() + case "ucallback.v1.ReadRequest.expiry_block_height": + x.ExpiryBlockHeight = value.Uint() + case "ucallback.v1.ReadRequest.created_at_height": + x.CreatedAtHeight = value.Uint() + case "ucallback.v1.ReadRequest.callback_target": + x.CallbackTarget = value.Interface().(string) + case "ucallback.v1.ReadRequest.original_funder": + x.OriginalFunder = value.Interface().(string) + case "ucallback.v1.ReadRequest.fees_deposited": + x.FeesDeposited = value.Interface().(string) + case "ucallback.v1.ReadRequest.max_fee": + x.MaxFee = value.Interface().(string) + case "ucallback.v1.ReadRequest.requested_tx_hash": + x.RequestedTxHash = value.Interface().(string) + case "ucallback.v1.ReadRequest.requested_log_index": + x.RequestedLogIndex = value.Uint() + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadRequest) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.ReadRequest.request_id": + panic(fmt.Errorf("field request_id of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.destination_chain": + panic(fmt.Errorf("field destination_chain of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.owner": + panic(fmt.Errorf("field owner of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.query": + panic(fmt.Errorf("field query of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.min_confirmations": + panic(fmt.Errorf("field min_confirmations of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.destination_block_height": + panic(fmt.Errorf("field destination_block_height of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.expiry_block_height": + panic(fmt.Errorf("field expiry_block_height of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.created_at_height": + panic(fmt.Errorf("field created_at_height of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.callback_target": + panic(fmt.Errorf("field callback_target of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.original_funder": + panic(fmt.Errorf("field original_funder of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.fees_deposited": + panic(fmt.Errorf("field fees_deposited of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.max_fee": + panic(fmt.Errorf("field max_fee of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.requested_tx_hash": + panic(fmt.Errorf("field requested_tx_hash of message ucallback.v1.ReadRequest is not mutable")) + case "ucallback.v1.ReadRequest.requested_log_index": + panic(fmt.Errorf("field requested_log_index of message ucallback.v1.ReadRequest is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_ReadRequest) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.ReadRequest.request_id": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.destination_chain": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.owner": + return protoreflect.ValueOfBytes(nil) + case "ucallback.v1.ReadRequest.query": + return protoreflect.ValueOfBytes(nil) + case "ucallback.v1.ReadRequest.min_confirmations": + return protoreflect.ValueOfUint32(uint32(0)) + case "ucallback.v1.ReadRequest.destination_block_height": + return protoreflect.ValueOfUint64(uint64(0)) + case "ucallback.v1.ReadRequest.expiry_block_height": + return protoreflect.ValueOfUint64(uint64(0)) + case "ucallback.v1.ReadRequest.created_at_height": + return protoreflect.ValueOfUint64(uint64(0)) + case "ucallback.v1.ReadRequest.callback_target": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.original_funder": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.fees_deposited": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.max_fee": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.requested_tx_hash": + return protoreflect.ValueOfString("") + case "ucallback.v1.ReadRequest.requested_log_index": + return protoreflect.ValueOfUint64(uint64(0)) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.ReadRequest does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_ReadRequest) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.ReadRequest", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_ReadRequest) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadRequest) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_ReadRequest) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_ReadRequest) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*ReadRequest) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.RequestId) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.DestinationChain) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Owner) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Query) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.MinConfirmations != 0 { + n += 1 + runtime.Sov(uint64(x.MinConfirmations)) + } + if x.DestinationBlockHeight != 0 { + n += 1 + runtime.Sov(uint64(x.DestinationBlockHeight)) + } + if x.ExpiryBlockHeight != 0 { + n += 1 + runtime.Sov(uint64(x.ExpiryBlockHeight)) + } + if x.CreatedAtHeight != 0 { + n += 1 + runtime.Sov(uint64(x.CreatedAtHeight)) + } + l = len(x.CallbackTarget) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.OriginalFunder) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.FeesDeposited) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.MaxFee) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.RequestedTxHash) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.RequestedLogIndex != 0 { + n += 1 + runtime.Sov(uint64(x.RequestedLogIndex)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*ReadRequest) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.RequestedLogIndex != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.RequestedLogIndex)) + i-- + dAtA[i] = 0x70 + } + if len(x.RequestedTxHash) > 0 { + i -= len(x.RequestedTxHash) + copy(dAtA[i:], x.RequestedTxHash) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.RequestedTxHash))) + i-- + dAtA[i] = 0x6a + } + if len(x.MaxFee) > 0 { + i -= len(x.MaxFee) + copy(dAtA[i:], x.MaxFee) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.MaxFee))) + i-- + dAtA[i] = 0x62 + } + if len(x.FeesDeposited) > 0 { + i -= len(x.FeesDeposited) + copy(dAtA[i:], x.FeesDeposited) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.FeesDeposited))) + i-- + dAtA[i] = 0x5a + } + if len(x.OriginalFunder) > 0 { + i -= len(x.OriginalFunder) + copy(dAtA[i:], x.OriginalFunder) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.OriginalFunder))) + i-- + dAtA[i] = 0x52 + } + if len(x.CallbackTarget) > 0 { + i -= len(x.CallbackTarget) + copy(dAtA[i:], x.CallbackTarget) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.CallbackTarget))) + i-- + dAtA[i] = 0x4a + } + if x.CreatedAtHeight != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.CreatedAtHeight)) + i-- + dAtA[i] = 0x40 + } + if x.ExpiryBlockHeight != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.ExpiryBlockHeight)) + i-- + dAtA[i] = 0x38 + } + if x.DestinationBlockHeight != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.DestinationBlockHeight)) + i-- + dAtA[i] = 0x30 + } + if x.MinConfirmations != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.MinConfirmations)) + i-- + dAtA[i] = 0x28 + } + if len(x.Query) > 0 { + i -= len(x.Query) + copy(dAtA[i:], x.Query) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Query))) + i-- + dAtA[i] = 0x22 + } + if len(x.Owner) > 0 { + i -= len(x.Owner) + copy(dAtA[i:], x.Owner) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Owner))) + i-- + dAtA[i] = 0x1a + } + if len(x.DestinationChain) > 0 { + i -= len(x.DestinationChain) + copy(dAtA[i:], x.DestinationChain) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.DestinationChain))) + i-- + dAtA[i] = 0x12 + } + if len(x.RequestId) > 0 { + i -= len(x.RequestId) + copy(dAtA[i:], x.RequestId) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.RequestId))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*ReadRequest) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: ReadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: ReadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field DestinationChain", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.DestinationChain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Owner = append(x.Owner[:0], dAtA[iNdEx:postIndex]...) + if x.Owner == nil { + x.Owner = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Query", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Query = append(x.Query[:0], dAtA[iNdEx:postIndex]...) + if x.Query == nil { + x.Query = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MinConfirmations", wireType) + } + x.MinConfirmations = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.MinConfirmations |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field DestinationBlockHeight", wireType) + } + x.DestinationBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.DestinationBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExpiryBlockHeight", wireType) + } + x.ExpiryBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.ExpiryBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field CreatedAtHeight", wireType) + } + x.CreatedAtHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.CreatedAtHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field CallbackTarget", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.CallbackTarget = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field OriginalFunder", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.OriginalFunder = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field FeesDeposited", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.FeesDeposited = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MaxFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.MaxFee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 13: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RequestedTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.RequestedTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RequestedLogIndex", wireType) + } + x.RequestedLogIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.RequestedLogIndex |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var _ protoreflect.List = (*_ReadResult_5_list)(nil) + +type _ReadResult_5_list struct { + list *[]*AggregateValue +} + +func (x *_ReadResult_5_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_ReadResult_5_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_ReadResult_5_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*AggregateValue) + (*x.list)[i] = concreteValue +} + +func (x *_ReadResult_5_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*AggregateValue) + *x.list = append(*x.list, concreteValue) +} + +func (x *_ReadResult_5_list) AppendMutable() protoreflect.Value { + v := new(AggregateValue) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_ReadResult_5_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_ReadResult_5_list) NewElement() protoreflect.Value { + v := new(AggregateValue) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_ReadResult_5_list) IsValid() bool { + return x.list != nil +} + +var ( + md_ReadResult protoreflect.MessageDescriptor + fd_ReadResult_status protoreflect.FieldDescriptor + fd_ReadResult_result_data protoreflect.FieldDescriptor + fd_ReadResult_observed_block_height protoreflect.FieldDescriptor + fd_ReadResult_observed_block_hash protoreflect.FieldDescriptor + fd_ReadResult_aggregates protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_types_proto_init() + md_ReadResult = File_ucallback_v1_types_proto.Messages().ByName("ReadResult") + fd_ReadResult_status = md_ReadResult.Fields().ByName("status") + fd_ReadResult_result_data = md_ReadResult.Fields().ByName("result_data") + fd_ReadResult_observed_block_height = md_ReadResult.Fields().ByName("observed_block_height") + fd_ReadResult_observed_block_hash = md_ReadResult.Fields().ByName("observed_block_hash") + fd_ReadResult_aggregates = md_ReadResult.Fields().ByName("aggregates") +} + +var _ protoreflect.Message = (*fastReflection_ReadResult)(nil) + +type fastReflection_ReadResult ReadResult + +func (x *ReadResult) ProtoReflect() protoreflect.Message { + return (*fastReflection_ReadResult)(x) +} + +func (x *ReadResult) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_types_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_ReadResult_messageType fastReflection_ReadResult_messageType +var _ protoreflect.MessageType = fastReflection_ReadResult_messageType{} + +type fastReflection_ReadResult_messageType struct{} + +func (x fastReflection_ReadResult_messageType) Zero() protoreflect.Message { + return (*fastReflection_ReadResult)(nil) +} +func (x fastReflection_ReadResult_messageType) New() protoreflect.Message { + return new(fastReflection_ReadResult) +} +func (x fastReflection_ReadResult_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_ReadResult +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_ReadResult) Descriptor() protoreflect.MessageDescriptor { + return md_ReadResult +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_ReadResult) Type() protoreflect.MessageType { + return _fastReflection_ReadResult_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_ReadResult) New() protoreflect.Message { + return new(fastReflection_ReadResult) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_ReadResult) Interface() protoreflect.ProtoMessage { + return (*ReadResult)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_ReadResult) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Status != 0 { + value := protoreflect.ValueOfEnum((protoreflect.EnumNumber)(x.Status)) + if !f(fd_ReadResult_status, value) { + return + } + } + if len(x.ResultData) != 0 { + value := protoreflect.ValueOfBytes(x.ResultData) + if !f(fd_ReadResult_result_data, value) { + return + } + } + if x.ObservedBlockHeight != uint64(0) { + value := protoreflect.ValueOfUint64(x.ObservedBlockHeight) + if !f(fd_ReadResult_observed_block_height, value) { + return + } + } + if len(x.ObservedBlockHash) != 0 { + value := protoreflect.ValueOfBytes(x.ObservedBlockHash) + if !f(fd_ReadResult_observed_block_hash, value) { + return + } + } + if len(x.Aggregates) != 0 { + value := protoreflect.ValueOfList(&_ReadResult_5_list{list: &x.Aggregates}) + if !f(fd_ReadResult_aggregates, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_ReadResult) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.ReadResult.status": + return x.Status != 0 + case "ucallback.v1.ReadResult.result_data": + return len(x.ResultData) != 0 + case "ucallback.v1.ReadResult.observed_block_height": + return x.ObservedBlockHeight != uint64(0) + case "ucallback.v1.ReadResult.observed_block_hash": + return len(x.ObservedBlockHash) != 0 + case "ucallback.v1.ReadResult.aggregates": + return len(x.Aggregates) != 0 + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadResult) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.ReadResult.status": + x.Status = 0 + case "ucallback.v1.ReadResult.result_data": + x.ResultData = nil + case "ucallback.v1.ReadResult.observed_block_height": + x.ObservedBlockHeight = uint64(0) + case "ucallback.v1.ReadResult.observed_block_hash": + x.ObservedBlockHash = nil + case "ucallback.v1.ReadResult.aggregates": + x.Aggregates = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_ReadResult) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.ReadResult.status": + value := x.Status + return protoreflect.ValueOfEnum((protoreflect.EnumNumber)(value)) + case "ucallback.v1.ReadResult.result_data": + value := x.ResultData + return protoreflect.ValueOfBytes(value) + case "ucallback.v1.ReadResult.observed_block_height": + value := x.ObservedBlockHeight + return protoreflect.ValueOfUint64(value) + case "ucallback.v1.ReadResult.observed_block_hash": + value := x.ObservedBlockHash + return protoreflect.ValueOfBytes(value) + case "ucallback.v1.ReadResult.aggregates": + if len(x.Aggregates) == 0 { + return protoreflect.ValueOfList(&_ReadResult_5_list{}) + } + listValue := &_ReadResult_5_list{list: &x.Aggregates} + return protoreflect.ValueOfList(listValue) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadResult) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.ReadResult.status": + x.Status = (ReadStatus)(value.Enum()) + case "ucallback.v1.ReadResult.result_data": + x.ResultData = value.Bytes() + case "ucallback.v1.ReadResult.observed_block_height": + x.ObservedBlockHeight = value.Uint() + case "ucallback.v1.ReadResult.observed_block_hash": + x.ObservedBlockHash = value.Bytes() + case "ucallback.v1.ReadResult.aggregates": + lv := value.List() + clv := lv.(*_ReadResult_5_list) + x.Aggregates = *clv.list + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadResult) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.ReadResult.aggregates": + if x.Aggregates == nil { + x.Aggregates = []*AggregateValue{} + } + value := &_ReadResult_5_list{list: &x.Aggregates} + return protoreflect.ValueOfList(value) + case "ucallback.v1.ReadResult.status": + panic(fmt.Errorf("field status of message ucallback.v1.ReadResult is not mutable")) + case "ucallback.v1.ReadResult.result_data": + panic(fmt.Errorf("field result_data of message ucallback.v1.ReadResult is not mutable")) + case "ucallback.v1.ReadResult.observed_block_height": + panic(fmt.Errorf("field observed_block_height of message ucallback.v1.ReadResult is not mutable")) + case "ucallback.v1.ReadResult.observed_block_hash": + panic(fmt.Errorf("field observed_block_hash of message ucallback.v1.ReadResult is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_ReadResult) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.ReadResult.status": + return protoreflect.ValueOfEnum(0) + case "ucallback.v1.ReadResult.result_data": + return protoreflect.ValueOfBytes(nil) + case "ucallback.v1.ReadResult.observed_block_height": + return protoreflect.ValueOfUint64(uint64(0)) + case "ucallback.v1.ReadResult.observed_block_hash": + return protoreflect.ValueOfBytes(nil) + case "ucallback.v1.ReadResult.aggregates": + list := []*AggregateValue{} + return protoreflect.ValueOfList(&_ReadResult_5_list{list: &list}) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.ReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.ReadResult does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_ReadResult) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.ReadResult", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_ReadResult) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_ReadResult) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_ReadResult) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_ReadResult) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*ReadResult) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Status != 0 { + n += 1 + runtime.Sov(uint64(x.Status)) + } + l = len(x.ResultData) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.ObservedBlockHeight != 0 { + n += 1 + runtime.Sov(uint64(x.ObservedBlockHeight)) + } + l = len(x.ObservedBlockHash) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if len(x.Aggregates) > 0 { + for _, e := range x.Aggregates { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*ReadResult) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.Aggregates) > 0 { + for iNdEx := len(x.Aggregates) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.Aggregates[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x2a + } + } + if len(x.ObservedBlockHash) > 0 { + i -= len(x.ObservedBlockHash) + copy(dAtA[i:], x.ObservedBlockHash) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.ObservedBlockHash))) + i-- + dAtA[i] = 0x22 + } + if x.ObservedBlockHeight != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.ObservedBlockHeight)) + i-- + dAtA[i] = 0x18 + } + if len(x.ResultData) > 0 { + i -= len(x.ResultData) + copy(dAtA[i:], x.ResultData) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.ResultData))) + i-- + dAtA[i] = 0x12 + } + if x.Status != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.Status)) + i-- + dAtA[i] = 0x8 + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*ReadResult) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: ReadResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: ReadResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + x.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.Status |= ReadStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ResultData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.ResultData = append(x.ResultData[:0], dAtA[iNdEx:postIndex]...) + if x.ResultData == nil { + x.ResultData = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ObservedBlockHeight", wireType) + } + x.ObservedBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.ObservedBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ObservedBlockHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.ObservedBlockHash = append(x.ObservedBlockHash[:0], dAtA[iNdEx:postIndex]...) + if x.ObservedBlockHash == nil { + x.ObservedBlockHash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Aggregates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Aggregates = append(x.Aggregates, &AggregateValue{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Aggregates[len(x.Aggregates)-1]); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_AggregateValue protoreflect.MessageDescriptor + fd_AggregateValue_extract_index protoreflect.FieldDescriptor + fd_AggregateValue_mode protoreflect.FieldDescriptor + fd_AggregateValue_value protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_types_proto_init() + md_AggregateValue = File_ucallback_v1_types_proto.Messages().ByName("AggregateValue") + fd_AggregateValue_extract_index = md_AggregateValue.Fields().ByName("extract_index") + fd_AggregateValue_mode = md_AggregateValue.Fields().ByName("mode") + fd_AggregateValue_value = md_AggregateValue.Fields().ByName("value") +} + +var _ protoreflect.Message = (*fastReflection_AggregateValue)(nil) + +type fastReflection_AggregateValue AggregateValue + +func (x *AggregateValue) ProtoReflect() protoreflect.Message { + return (*fastReflection_AggregateValue)(x) +} + +func (x *AggregateValue) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_types_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_AggregateValue_messageType fastReflection_AggregateValue_messageType +var _ protoreflect.MessageType = fastReflection_AggregateValue_messageType{} + +type fastReflection_AggregateValue_messageType struct{} + +func (x fastReflection_AggregateValue_messageType) Zero() protoreflect.Message { + return (*fastReflection_AggregateValue)(nil) +} +func (x fastReflection_AggregateValue_messageType) New() protoreflect.Message { + return new(fastReflection_AggregateValue) +} +func (x fastReflection_AggregateValue_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_AggregateValue +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_AggregateValue) Descriptor() protoreflect.MessageDescriptor { + return md_AggregateValue +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_AggregateValue) Type() protoreflect.MessageType { + return _fastReflection_AggregateValue_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_AggregateValue) New() protoreflect.Message { + return new(fastReflection_AggregateValue) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_AggregateValue) Interface() protoreflect.ProtoMessage { + return (*AggregateValue)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_AggregateValue) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.ExtractIndex != uint32(0) { + value := protoreflect.ValueOfUint32(x.ExtractIndex) + if !f(fd_AggregateValue_extract_index, value) { + return + } + } + if x.Mode != uint32(0) { + value := protoreflect.ValueOfUint32(x.Mode) + if !f(fd_AggregateValue_mode, value) { + return + } + } + if len(x.Value) != 0 { + value := protoreflect.ValueOfBytes(x.Value) + if !f(fd_AggregateValue_value, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_AggregateValue) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + return x.ExtractIndex != uint32(0) + case "ucallback.v1.AggregateValue.mode": + return x.Mode != uint32(0) + case "ucallback.v1.AggregateValue.value": + return len(x.Value) != 0 + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AggregateValue) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + x.ExtractIndex = uint32(0) + case "ucallback.v1.AggregateValue.mode": + x.Mode = uint32(0) + case "ucallback.v1.AggregateValue.value": + x.Value = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_AggregateValue) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + value := x.ExtractIndex + return protoreflect.ValueOfUint32(value) + case "ucallback.v1.AggregateValue.mode": + value := x.Mode + return protoreflect.ValueOfUint32(value) + case "ucallback.v1.AggregateValue.value": + value := x.Value + return protoreflect.ValueOfBytes(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AggregateValue) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + x.ExtractIndex = uint32(value.Uint()) + case "ucallback.v1.AggregateValue.mode": + x.Mode = uint32(value.Uint()) + case "ucallback.v1.AggregateValue.value": + x.Value = value.Bytes() + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AggregateValue) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + panic(fmt.Errorf("field extract_index of message ucallback.v1.AggregateValue is not mutable")) + case "ucallback.v1.AggregateValue.mode": + panic(fmt.Errorf("field mode of message ucallback.v1.AggregateValue is not mutable")) + case "ucallback.v1.AggregateValue.value": + panic(fmt.Errorf("field value of message ucallback.v1.AggregateValue is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_AggregateValue) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.AggregateValue.extract_index": + return protoreflect.ValueOfUint32(uint32(0)) + case "ucallback.v1.AggregateValue.mode": + return protoreflect.ValueOfUint32(uint32(0)) + case "ucallback.v1.AggregateValue.value": + return protoreflect.ValueOfBytes(nil) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.AggregateValue")) + } + panic(fmt.Errorf("message ucallback.v1.AggregateValue does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_AggregateValue) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.AggregateValue", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_AggregateValue) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_AggregateValue) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_AggregateValue) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_AggregateValue) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*AggregateValue) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.ExtractIndex != 0 { + n += 1 + runtime.Sov(uint64(x.ExtractIndex)) + } + if x.Mode != 0 { + n += 1 + runtime.Sov(uint64(x.Mode)) + } + l = len(x.Value) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*AggregateValue) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.Value) > 0 { + i -= len(x.Value) + copy(dAtA[i:], x.Value) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Value))) + i-- + dAtA[i] = 0x1a + } + if x.Mode != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.Mode)) + i-- + dAtA[i] = 0x10 + } + if x.ExtractIndex != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.ExtractIndex)) + i-- + dAtA[i] = 0x8 + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*AggregateValue) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: AggregateValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: AggregateValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ExtractIndex", wireType) + } + x.ExtractIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.ExtractIndex |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + x.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.Mode |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Value = append(x.Value[:0], dAtA[iNdEx:postIndex]...) + if x.Value == nil { + x.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var _ protoreflect.List = (*_UniversalRead_6_list)(nil) + +type _UniversalRead_6_list struct { + list *[]*v1.PCTx +} + +func (x *_UniversalRead_6_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_UniversalRead_6_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_UniversalRead_6_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*v1.PCTx) + (*x.list)[i] = concreteValue +} + +func (x *_UniversalRead_6_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*v1.PCTx) + *x.list = append(*x.list, concreteValue) +} + +func (x *_UniversalRead_6_list) AppendMutable() protoreflect.Value { + v := new(v1.PCTx) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_UniversalRead_6_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_UniversalRead_6_list) NewElement() protoreflect.Value { + v := new(v1.PCTx) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_UniversalRead_6_list) IsValid() bool { + return x.list != nil +} + +var ( + md_UniversalRead protoreflect.MessageDescriptor + fd_UniversalRead_id protoreflect.FieldDescriptor + fd_UniversalRead_request protoreflect.FieldDescriptor + fd_UniversalRead_result protoreflect.FieldDescriptor + fd_UniversalRead_status protoreflect.FieldDescriptor + fd_UniversalRead_ballot_key protoreflect.FieldDescriptor + fd_UniversalRead_pc_tx protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_types_proto_init() + md_UniversalRead = File_ucallback_v1_types_proto.Messages().ByName("UniversalRead") + fd_UniversalRead_id = md_UniversalRead.Fields().ByName("id") + fd_UniversalRead_request = md_UniversalRead.Fields().ByName("request") + fd_UniversalRead_result = md_UniversalRead.Fields().ByName("result") + fd_UniversalRead_status = md_UniversalRead.Fields().ByName("status") + fd_UniversalRead_ballot_key = md_UniversalRead.Fields().ByName("ballot_key") + fd_UniversalRead_pc_tx = md_UniversalRead.Fields().ByName("pc_tx") +} + +var _ protoreflect.Message = (*fastReflection_UniversalRead)(nil) + +type fastReflection_UniversalRead UniversalRead + +func (x *UniversalRead) ProtoReflect() protoreflect.Message { + return (*fastReflection_UniversalRead)(x) +} + +func (x *UniversalRead) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_types_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_UniversalRead_messageType fastReflection_UniversalRead_messageType +var _ protoreflect.MessageType = fastReflection_UniversalRead_messageType{} + +type fastReflection_UniversalRead_messageType struct{} + +func (x fastReflection_UniversalRead_messageType) Zero() protoreflect.Message { + return (*fastReflection_UniversalRead)(nil) +} +func (x fastReflection_UniversalRead_messageType) New() protoreflect.Message { + return new(fastReflection_UniversalRead) +} +func (x fastReflection_UniversalRead_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_UniversalRead +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_UniversalRead) Descriptor() protoreflect.MessageDescriptor { + return md_UniversalRead +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_UniversalRead) Type() protoreflect.MessageType { + return _fastReflection_UniversalRead_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_UniversalRead) New() protoreflect.Message { + return new(fastReflection_UniversalRead) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_UniversalRead) Interface() protoreflect.ProtoMessage { + return (*UniversalRead)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_UniversalRead) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Id != "" { + value := protoreflect.ValueOfString(x.Id) + if !f(fd_UniversalRead_id, value) { + return + } + } + if x.Request != nil { + value := protoreflect.ValueOfMessage(x.Request.ProtoReflect()) + if !f(fd_UniversalRead_request, value) { + return + } + } + if x.Result != nil { + value := protoreflect.ValueOfMessage(x.Result.ProtoReflect()) + if !f(fd_UniversalRead_result, value) { + return + } + } + if x.Status != 0 { + value := protoreflect.ValueOfEnum((protoreflect.EnumNumber)(x.Status)) + if !f(fd_UniversalRead_status, value) { + return + } + } + if x.BallotKey != "" { + value := protoreflect.ValueOfString(x.BallotKey) + if !f(fd_UniversalRead_ballot_key, value) { + return + } + } + if len(x.PcTx) != 0 { + value := protoreflect.ValueOfList(&_UniversalRead_6_list{list: &x.PcTx}) + if !f(fd_UniversalRead_pc_tx, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_UniversalRead) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.UniversalRead.id": + return x.Id != "" + case "ucallback.v1.UniversalRead.request": + return x.Request != nil + case "ucallback.v1.UniversalRead.result": + return x.Result != nil + case "ucallback.v1.UniversalRead.status": + return x.Status != 0 + case "ucallback.v1.UniversalRead.ballot_key": + return x.BallotKey != "" + case "ucallback.v1.UniversalRead.pc_tx": + return len(x.PcTx) != 0 + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalRead) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.UniversalRead.id": + x.Id = "" + case "ucallback.v1.UniversalRead.request": + x.Request = nil + case "ucallback.v1.UniversalRead.result": + x.Result = nil + case "ucallback.v1.UniversalRead.status": + x.Status = 0 + case "ucallback.v1.UniversalRead.ballot_key": + x.BallotKey = "" + case "ucallback.v1.UniversalRead.pc_tx": + x.PcTx = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_UniversalRead) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.UniversalRead.id": + value := x.Id + return protoreflect.ValueOfString(value) + case "ucallback.v1.UniversalRead.request": + value := x.Request + return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "ucallback.v1.UniversalRead.result": + value := x.Result + return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "ucallback.v1.UniversalRead.status": + value := x.Status + return protoreflect.ValueOfEnum((protoreflect.EnumNumber)(value)) + case "ucallback.v1.UniversalRead.ballot_key": + value := x.BallotKey + return protoreflect.ValueOfString(value) + case "ucallback.v1.UniversalRead.pc_tx": + if len(x.PcTx) == 0 { + return protoreflect.ValueOfList(&_UniversalRead_6_list{}) + } + listValue := &_UniversalRead_6_list{list: &x.PcTx} + return protoreflect.ValueOfList(listValue) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalRead) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.UniversalRead.id": + x.Id = value.Interface().(string) + case "ucallback.v1.UniversalRead.request": + x.Request = value.Message().Interface().(*ReadRequest) + case "ucallback.v1.UniversalRead.result": + x.Result = value.Message().Interface().(*ReadResult) + case "ucallback.v1.UniversalRead.status": + x.Status = (UniversalReadStatus)(value.Enum()) + case "ucallback.v1.UniversalRead.ballot_key": + x.BallotKey = value.Interface().(string) + case "ucallback.v1.UniversalRead.pc_tx": + lv := value.List() + clv := lv.(*_UniversalRead_6_list) + x.PcTx = *clv.list + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalRead) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.UniversalRead.request": + if x.Request == nil { + x.Request = new(ReadRequest) + } + return protoreflect.ValueOfMessage(x.Request.ProtoReflect()) + case "ucallback.v1.UniversalRead.result": + if x.Result == nil { + x.Result = new(ReadResult) + } + return protoreflect.ValueOfMessage(x.Result.ProtoReflect()) + case "ucallback.v1.UniversalRead.pc_tx": + if x.PcTx == nil { + x.PcTx = []*v1.PCTx{} + } + value := &_UniversalRead_6_list{list: &x.PcTx} + return protoreflect.ValueOfList(value) + case "ucallback.v1.UniversalRead.id": + panic(fmt.Errorf("field id of message ucallback.v1.UniversalRead is not mutable")) + case "ucallback.v1.UniversalRead.status": + panic(fmt.Errorf("field status of message ucallback.v1.UniversalRead is not mutable")) + case "ucallback.v1.UniversalRead.ballot_key": + panic(fmt.Errorf("field ballot_key of message ucallback.v1.UniversalRead is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_UniversalRead) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.UniversalRead.id": + return protoreflect.ValueOfString("") + case "ucallback.v1.UniversalRead.request": + m := new(ReadRequest) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "ucallback.v1.UniversalRead.result": + m := new(ReadResult) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "ucallback.v1.UniversalRead.status": + return protoreflect.ValueOfEnum(0) + case "ucallback.v1.UniversalRead.ballot_key": + return protoreflect.ValueOfString("") + case "ucallback.v1.UniversalRead.pc_tx": + list := []*v1.PCTx{} + return protoreflect.ValueOfList(&_UniversalRead_6_list{list: &list}) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalRead")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalRead does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_UniversalRead) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.UniversalRead", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_UniversalRead) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalRead) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_UniversalRead) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_UniversalRead) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*UniversalRead) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Id) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Request != nil { + l = options.Size(x.Request) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Result != nil { + l = options.Size(x.Result) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Status != 0 { + n += 1 + runtime.Sov(uint64(x.Status)) + } + l = len(x.BallotKey) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if len(x.PcTx) > 0 { + for _, e := range x.PcTx { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*UniversalRead) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.PcTx) > 0 { + for iNdEx := len(x.PcTx) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.PcTx[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x32 + } + } + if len(x.BallotKey) > 0 { + i -= len(x.BallotKey) + copy(dAtA[i:], x.BallotKey) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.BallotKey))) + i-- + dAtA[i] = 0x2a + } + if x.Status != 0 { + i = runtime.EncodeVarint(dAtA, i, uint64(x.Status)) + i-- + dAtA[i] = 0x20 + } + if x.Result != nil { + encoded, err := options.Marshal(x.Result) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x1a + } + if x.Request != nil { + encoded, err := options.Marshal(x.Request) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + if len(x.Id) > 0 { + i -= len(x.Id) + copy(dAtA[i:], x.Id) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Id))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*UniversalRead) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: UniversalRead: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: UniversalRead: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Request == nil { + x.Request = &ReadRequest{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Request); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Result == nil { + x.Result = &ReadResult{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Result); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + x.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + x.Status |= UniversalReadStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field BallotKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.BallotKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field PcTx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.PcTx = append(x.PcTx, &v1.PCTx{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.PcTx[len(x.PcTx)-1]); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.0 +// protoc (unknown) +// source: ucallback/v1/types.proto + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// ReadStatus is the outcome a universal validator observed for a read. +type ReadStatus int32 + +const ( + ReadStatus_READ_STATUS_UNSPECIFIED ReadStatus = 0 + ReadStatus_READ_STATUS_SUCCESS ReadStatus = 1 + ReadStatus_READ_STATUS_ERROR ReadStatus = 2 +) + +// Enum value maps for ReadStatus. +var ( + ReadStatus_name = map[int32]string{ + 0: "READ_STATUS_UNSPECIFIED", + 1: "READ_STATUS_SUCCESS", + 2: "READ_STATUS_ERROR", + } + ReadStatus_value = map[string]int32{ + "READ_STATUS_UNSPECIFIED": 0, + "READ_STATUS_SUCCESS": 1, + "READ_STATUS_ERROR": 2, + } +) + +func (x ReadStatus) Enum() *ReadStatus { + p := new(ReadStatus) + *p = x + return p +} + +func (x ReadStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ReadStatus) Descriptor() protoreflect.EnumDescriptor { + return file_ucallback_v1_types_proto_enumTypes[0].Descriptor() +} + +func (ReadStatus) Type() protoreflect.EnumType { + return &file_ucallback_v1_types_proto_enumTypes[0] +} + +func (x ReadStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ReadStatus.Descriptor instead. +func (ReadStatus) EnumDescriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{0} +} + +// UniversalReadStatus is the lifecycle state of a read request on Push Chain. +type UniversalReadStatus int32 + +const ( + UniversalReadStatus_UNIVERSAL_READ_STATUS_UNSPECIFIED UniversalReadStatus = 0 + UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING UniversalReadStatus = 1 // ingested, awaiting votes + UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING UniversalReadStatus = 2 // at least one vote, no quorum yet + UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED UniversalReadStatus = 3 // callback dispatched successfully + UniversalReadStatus_UNIVERSAL_READ_STATUS_EXPIRED UniversalReadStatus = 4 // expireExternalRead submitted + UniversalReadStatus_UNIVERSAL_READ_STATUS_FAILED UniversalReadStatus = 5 // quorum reached but the callback reverted +) + +// Enum value maps for UniversalReadStatus. +var ( + UniversalReadStatus_name = map[int32]string{ + 0: "UNIVERSAL_READ_STATUS_UNSPECIFIED", + 1: "UNIVERSAL_READ_STATUS_PENDING", + 2: "UNIVERSAL_READ_STATUS_VOTING", + 3: "UNIVERSAL_READ_STATUS_FULFILLED", + 4: "UNIVERSAL_READ_STATUS_EXPIRED", + 5: "UNIVERSAL_READ_STATUS_FAILED", + } + UniversalReadStatus_value = map[string]int32{ + "UNIVERSAL_READ_STATUS_UNSPECIFIED": 0, + "UNIVERSAL_READ_STATUS_PENDING": 1, + "UNIVERSAL_READ_STATUS_VOTING": 2, + "UNIVERSAL_READ_STATUS_FULFILLED": 3, + "UNIVERSAL_READ_STATUS_EXPIRED": 4, + "UNIVERSAL_READ_STATUS_FAILED": 5, + } +) + +func (x UniversalReadStatus) Enum() *UniversalReadStatus { + p := new(UniversalReadStatus) + *p = x + return p +} + +func (x UniversalReadStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (UniversalReadStatus) Descriptor() protoreflect.EnumDescriptor { + return file_ucallback_v1_types_proto_enumTypes[1].Descriptor() +} + +func (UniversalReadStatus) Type() protoreflect.EnumType { + return &file_ucallback_v1_types_proto_enumTypes[1] +} + +func (x UniversalReadStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use UniversalReadStatus.Descriptor instead. +func (UniversalReadStatus) EnumDescriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{1} +} + +// ReadRequest is one external read requested by an app on Push Chain. +// +// Every field is derived from the UniversalCallback.ReadRequested event, except +// created_at_height / requested_tx_hash / requested_log_index which come from the +// block the log was emitted in. +// +// NOTE: callbackGasLimit is deliberately absent. It is an argument to +// requestExternalReadSelf and is stored in the contract's _pending entry, but it is +// NOT emitted in ReadRequested. It has to be read back via getPendingRead(requestId) +// at fulfilment time, when the gas budget is actually needed. +type ReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` // uint256 requestId as 0x-prefixed hex + DestinationChain string `protobuf:"bytes,2,opt,name=destination_chain,json=destinationChain,proto3" json:"destination_chain,omitempty"` // CAIP-2, e.g. "eip155:1"; web2 uses "web2:https" + Owner []byte `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` // 20-byte address or 32-byte pubkey + Query []byte `protobuf:"bytes,4,opt,name=query,proto3" json:"query,omitempty"` // chain-specific envelope, abi.encode(...) + MinConfirmations uint32 `protobuf:"varint,5,opt,name=min_confirmations,json=minConfirmations,proto3" json:"min_confirmations,omitempty"` // uint16 on the contract; proto3 has no uint16 + DestinationBlockHeight uint64 `protobuf:"varint,6,opt,name=destination_block_height,json=destinationBlockHeight,proto3" json:"destination_block_height,omitempty"` // height on the destination chain; unused for web2 + ExpiryBlockHeight uint64 `protobuf:"varint,7,opt,name=expiry_block_height,json=expiryBlockHeight,proto3" json:"expiry_block_height,omitempty"` // Push Chain height at which the request expires + CreatedAtHeight uint64 `protobuf:"varint,8,opt,name=created_at_height,json=createdAtHeight,proto3" json:"created_at_height,omitempty"` // Push Chain height the request was observed at + // Bookkeeping — recorded for operators, not consumed by universal validators. + CallbackTarget string `protobuf:"bytes,9,opt,name=callback_target,json=callbackTarget,proto3" json:"callback_target,omitempty"` // the app contract the callback routes to + OriginalFunder string `protobuf:"bytes,10,opt,name=original_funder,json=originalFunder,proto3" json:"original_funder,omitempty"` // who paid the fee (the app, not the end user) + FeesDeposited string `protobuf:"bytes,11,opt,name=fees_deposited,json=feesDeposited,proto3" json:"fees_deposited,omitempty"` // uint256 as a decimal string + MaxFee string `protobuf:"bytes,12,opt,name=max_fee,json=maxFee,proto3" json:"max_fee,omitempty"` // uint256 as a decimal string + RequestedTxHash string `protobuf:"bytes,13,opt,name=requested_tx_hash,json=requestedTxHash,proto3" json:"requested_tx_hash,omitempty"` // Push Chain tx that emitted ReadRequested + RequestedLogIndex uint64 `protobuf:"varint,14,opt,name=requested_log_index,json=requestedLogIndex,proto3" json:"requested_log_index,omitempty"` // log index within that tx +} + +func (x *ReadRequest) Reset() { + *x = ReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_types_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadRequest) ProtoMessage() {} + +// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. +func (*ReadRequest) Descriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{0} +} + +func (x *ReadRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *ReadRequest) GetDestinationChain() string { + if x != nil { + return x.DestinationChain + } + return "" +} + +func (x *ReadRequest) GetOwner() []byte { + if x != nil { + return x.Owner + } + return nil +} + +func (x *ReadRequest) GetQuery() []byte { + if x != nil { + return x.Query + } + return nil +} + +func (x *ReadRequest) GetMinConfirmations() uint32 { + if x != nil { + return x.MinConfirmations + } + return 0 +} + +func (x *ReadRequest) GetDestinationBlockHeight() uint64 { + if x != nil { + return x.DestinationBlockHeight + } + return 0 +} + +func (x *ReadRequest) GetExpiryBlockHeight() uint64 { + if x != nil { + return x.ExpiryBlockHeight + } + return 0 +} + +func (x *ReadRequest) GetCreatedAtHeight() uint64 { + if x != nil { + return x.CreatedAtHeight + } + return 0 +} + +func (x *ReadRequest) GetCallbackTarget() string { + if x != nil { + return x.CallbackTarget + } + return "" +} + +func (x *ReadRequest) GetOriginalFunder() string { + if x != nil { + return x.OriginalFunder + } + return "" +} + +func (x *ReadRequest) GetFeesDeposited() string { + if x != nil { + return x.FeesDeposited + } + return "" +} + +func (x *ReadRequest) GetMaxFee() string { + if x != nil { + return x.MaxFee + } + return "" +} + +func (x *ReadRequest) GetRequestedTxHash() string { + if x != nil { + return x.RequestedTxHash + } + return "" +} + +func (x *ReadRequest) GetRequestedLogIndex() uint64 { + if x != nil { + return x.RequestedLogIndex + } + return 0 +} + +// ReadResult is the observation a universal validator votes on. +// +// Fields 1-4 are covered by the ballot key, so they must be byte-identical across +// validators for a ballot to converge. There is deliberately no error message field: +// local error text differs per node and would prevent agreement. +type ReadResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status ReadStatus `protobuf:"varint,1,opt,name=status,proto3,enum=ucallback.v1.ReadStatus" json:"status,omitempty"` + ResultData []byte `protobuf:"bytes,2,opt,name=result_data,json=resultData,proto3" json:"result_data,omitempty"` // ABI-encoded payload delivered to the app + ObservedBlockHeight uint64 `protobuf:"varint,3,opt,name=observed_block_height,json=observedBlockHeight,proto3" json:"observed_block_height,omitempty"` // block number (EVM) or slot (SVM); 0 for web2 + ObservedBlockHash []byte `protobuf:"bytes,4,opt,name=observed_block_hash,json=observedBlockHash,proto3" json:"observed_block_hash,omitempty"` // 32 bytes; empty when the chain cannot pin one + // v2 ONLY — always empty in v1. See AggregateValue. + Aggregates []*AggregateValue `protobuf:"bytes,5,rep,name=aggregates,proto3" json:"aggregates,omitempty"` +} + +func (x *ReadResult) Reset() { + *x = ReadResult{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_types_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResult) ProtoMessage() {} + +// Deprecated: Use ReadResult.ProtoReflect.Descriptor instead. +func (*ReadResult) Descriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{1} +} + +func (x *ReadResult) GetStatus() ReadStatus { + if x != nil { + return x.Status + } + return ReadStatus_READ_STATUS_UNSPECIFIED +} + +func (x *ReadResult) GetResultData() []byte { + if x != nil { + return x.ResultData + } + return nil +} + +func (x *ReadResult) GetObservedBlockHeight() uint64 { + if x != nil { + return x.ObservedBlockHeight + } + return 0 +} + +func (x *ReadResult) GetObservedBlockHash() []byte { + if x != nil { + return x.ObservedBlockHash + } + return nil +} + +func (x *ReadResult) GetAggregates() []*AggregateValue { + if x != nil { + return x.Aggregates + } + return nil +} + +// AggregateValue is one field the module combines across validators instead of +// requiring byte-equality on — a price, say, where honest nodes legitimately differ. +// +// NOT USED IN v1. The universal client rejects any extract mode other than IDENTICAL +// (externalchains/web2/read_envelope.go), so this list is always empty today. The field +// is reserved now because adding it later would change how ballots are keyed on a live +// chain, which is consensus-breaking. In v1 the ballot key covers all of fields 1-4; in +// v2 it must cover only fields 1-4 with `aggregates` EXCLUDED, so computing the key over +// "the identical subset" from the start keeps v2 purely additive. +// +// v2 ALSO REQUIRES REPLACING THE BALLOT MECHANISM, not just populating this field. +// Ballots today store a binary VoteResult{SUCCESS|FAILURE} against an ID that encodes the +// observation, so distinct observations produce distinct ballots and none reaches quorum +// when validators report different numbers. Ballots therefore cannot retain per-validator +// values, which is exactly what a median needs. v2 has to keep each validator's +// AggregateValue set in module state and reduce at quorum — the pattern x/uexecutor +// already uses for gas-price medians in keeper/chain_meta.go, which bypasses ballots for +// the same reason. +type AggregateValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExtractIndex uint32 `protobuf:"varint,1,opt,name=extract_index,json=extractIndex,proto3" json:"extract_index,omitempty"` // index into the query envelope's extract list + Mode uint32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"` // aggregation mode; only MEDIAN is planned + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` // big-endian uint256/int256 +} + +func (x *AggregateValue) Reset() { + *x = AggregateValue{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_types_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AggregateValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AggregateValue) ProtoMessage() {} + +// Deprecated: Use AggregateValue.ProtoReflect.Descriptor instead. +func (*AggregateValue) Descriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{2} +} + +func (x *AggregateValue) GetExtractIndex() uint32 { + if x != nil { + return x.ExtractIndex + } + return 0 +} + +func (x *AggregateValue) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *AggregateValue) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +// UniversalRead is the full lifecycle record of one read request. +// +// The read-side sibling of uexecutor's UniversalTx, but deliberately not the same +// shape: a read is triggered by a Push Chain event rather than an external inbound, +// performs no external write, and settles in exactly one Push Chain transaction. +type UniversalRead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // requestId, same value as request.request_id + Request *ReadRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Result *ReadResult `protobuf:"bytes,3,opt,name=result,proto3" json:"result,omitempty"` // set once the ballot finalises + Status UniversalReadStatus `protobuf:"varint,4,opt,name=status,proto3,enum=ucallback.v1.UniversalReadStatus" json:"status,omitempty"` + BallotKey string `protobuf:"bytes,5,opt,name=ballot_key,json=ballotKey,proto3" json:"ballot_key,omitempty"` + // Push Chain execution attempts — fulfilExternalCallback and expireExternalRead. + // Repeated because fulfilment can be retried and may be followed by an expiry. + PcTx []*v1.PCTx `protobuf:"bytes,6,rep,name=pc_tx,json=pcTx,proto3" json:"pc_tx,omitempty"` +} + +func (x *UniversalRead) Reset() { + *x = UniversalRead{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_types_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UniversalRead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UniversalRead) ProtoMessage() {} + +// Deprecated: Use UniversalRead.ProtoReflect.Descriptor instead. +func (*UniversalRead) Descriptor() ([]byte, []int) { + return file_ucallback_v1_types_proto_rawDescGZIP(), []int{3} +} + +func (x *UniversalRead) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UniversalRead) GetRequest() *ReadRequest { + if x != nil { + return x.Request + } + return nil +} + +func (x *UniversalRead) GetResult() *ReadResult { + if x != nil { + return x.Result + } + return nil +} + +func (x *UniversalRead) GetStatus() UniversalReadStatus { + if x != nil { + return x.Status + } + return UniversalReadStatus_UNIVERSAL_READ_STATUS_UNSPECIFIED +} + +func (x *UniversalRead) GetBallotKey() string { + if x != nil { + return x.BallotKey + } + return "" +} + +func (x *UniversalRead) GetPcTx() []*v1.PCTx { + if x != nil { + return x.PcTx + } + return nil +} + +var File_ucallback_v1_types_proto protoreflect.FileDescriptor + +var file_ucallback_v1_types_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, + 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x18, 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbc, 0x04, 0x0a, 0x0b, + 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, + 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x16, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, + 0x27, 0x0a, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, + 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x65, 0x65, 0x73, + 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x66, 0x65, 0x65, 0x73, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x78, + 0x48, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x11, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x6f, 0x67, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0xe8, 0xa0, 0x1f, 0x01, 0x22, 0x87, 0x02, 0x0a, 0x0a, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x15, + 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x6f, + 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x3c, 0x0a, 0x0a, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0a, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x73, 0x3a, 0x04, + 0xe8, 0xa0, 0x1f, 0x01, 0x22, 0x65, 0x0a, 0x0e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x65, + 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6d, + 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x04, 0xe8, 0xa0, 0x1f, 0x01, 0x22, 0xac, 0x02, 0x0a, 0x0d, + 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x39, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x27, + 0x0a, 0x05, 0x70, 0x63, 0x5f, 0x74, 0x78, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x75, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x43, 0x54, + 0x78, 0x52, 0x04, 0x70, 0x63, 0x54, 0x78, 0x3a, 0x21, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, + 0x2a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x75, 0x6e, 0x69, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x2a, 0x5f, 0x0a, 0x0a, 0x52, 0x65, + 0x61, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x44, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x15, + 0x0a, 0x11, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, + 0x52, 0x4f, 0x52, 0x10, 0x02, 0x1a, 0x04, 0xa8, 0xa4, 0x1e, 0x01, 0x2a, 0xf1, 0x01, 0x0a, 0x13, + 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x21, 0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, 0x4c, + 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x4e, + 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x20, 0x0a, + 0x1c, 0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x56, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, + 0x23, 0x0a, 0x1f, 0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x41, + 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x55, 0x4c, 0x46, 0x49, 0x4c, 0x4c, + 0x45, 0x44, 0x10, 0x03, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x4e, 0x49, 0x56, 0x45, 0x52, 0x53, 0x41, + 0x4c, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x58, + 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x4e, 0x49, 0x56, 0x45, + 0x52, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x1a, 0x04, 0xa8, 0xa4, 0x1e, 0x01, 0x42, + 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, + 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ucallback_v1_types_proto_rawDescOnce sync.Once + file_ucallback_v1_types_proto_rawDescData = file_ucallback_v1_types_proto_rawDesc +) + +func file_ucallback_v1_types_proto_rawDescGZIP() []byte { + file_ucallback_v1_types_proto_rawDescOnce.Do(func() { + file_ucallback_v1_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_ucallback_v1_types_proto_rawDescData) + }) + return file_ucallback_v1_types_proto_rawDescData +} + +var file_ucallback_v1_types_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_ucallback_v1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_ucallback_v1_types_proto_goTypes = []interface{}{ + (ReadStatus)(0), // 0: ucallback.v1.ReadStatus + (UniversalReadStatus)(0), // 1: ucallback.v1.UniversalReadStatus + (*ReadRequest)(nil), // 2: ucallback.v1.ReadRequest + (*ReadResult)(nil), // 3: ucallback.v1.ReadResult + (*AggregateValue)(nil), // 4: ucallback.v1.AggregateValue + (*UniversalRead)(nil), // 5: ucallback.v1.UniversalRead + (*v1.PCTx)(nil), // 6: uexecutor.v1.PCTx +} +var file_ucallback_v1_types_proto_depIdxs = []int32{ + 0, // 0: ucallback.v1.ReadResult.status:type_name -> ucallback.v1.ReadStatus + 4, // 1: ucallback.v1.ReadResult.aggregates:type_name -> ucallback.v1.AggregateValue + 2, // 2: ucallback.v1.UniversalRead.request:type_name -> ucallback.v1.ReadRequest + 3, // 3: ucallback.v1.UniversalRead.result:type_name -> ucallback.v1.ReadResult + 1, // 4: ucallback.v1.UniversalRead.status:type_name -> ucallback.v1.UniversalReadStatus + 6, // 5: ucallback.v1.UniversalRead.pc_tx:type_name -> uexecutor.v1.PCTx + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_ucallback_v1_types_proto_init() } +func file_ucallback_v1_types_proto_init() { + if File_ucallback_v1_types_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_ucallback_v1_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_types_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_types_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AggregateValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_types_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UniversalRead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ucallback_v1_types_proto_rawDesc, + NumEnums: 2, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ucallback_v1_types_proto_goTypes, + DependencyIndexes: file_ucallback_v1_types_proto_depIdxs, + EnumInfos: file_ucallback_v1_types_proto_enumTypes, + MessageInfos: file_ucallback_v1_types_proto_msgTypes, + }.Build() + File_ucallback_v1_types_proto = out.File + file_ucallback_v1_types_proto_rawDesc = nil + file_ucallback_v1_types_proto_goTypes = nil + file_ucallback_v1_types_proto_depIdxs = nil +} diff --git a/proto/ucallback/v1/types.proto b/proto/ucallback/v1/types.proto new file mode 100644 index 00000000..9c14a310 --- /dev/null +++ b/proto/ucallback/v1/types.proto @@ -0,0 +1,123 @@ +syntax = "proto3"; +package ucallback.v1; + +import "gogoproto/gogo.proto"; +import "amino/amino.proto"; +import "uexecutor/v1/types.proto"; + +option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; + +// ReadStatus is the outcome a universal validator observed for a read. +enum ReadStatus { + option (gogoproto.goproto_enum_stringer) = true; + + READ_STATUS_UNSPECIFIED = 0; + READ_STATUS_SUCCESS = 1; + READ_STATUS_ERROR = 2; +} + +// UniversalReadStatus is the lifecycle state of a read request on Push Chain. +enum UniversalReadStatus { + option (gogoproto.goproto_enum_stringer) = true; + + UNIVERSAL_READ_STATUS_UNSPECIFIED = 0; + UNIVERSAL_READ_STATUS_PENDING = 1; // ingested, awaiting votes + UNIVERSAL_READ_STATUS_VOTING = 2; // at least one vote, no quorum yet + UNIVERSAL_READ_STATUS_FULFILLED = 3; // callback dispatched successfully + UNIVERSAL_READ_STATUS_EXPIRED = 4; // expireExternalRead submitted + UNIVERSAL_READ_STATUS_FAILED = 5; // quorum reached but the callback reverted +} + +// ReadRequest is one external read requested by an app on Push Chain. +// +// Every field is derived from the UniversalCallback.ReadRequested event, except +// created_at_height / requested_tx_hash / requested_log_index which come from the +// block the log was emitted in. +// +// NOTE: callbackGasLimit is deliberately absent. It is an argument to +// requestExternalReadSelf and is stored in the contract's _pending entry, but it is +// NOT emitted in ReadRequested. It has to be read back via getPendingRead(requestId) +// at fulfilment time, when the gas budget is actually needed. +message ReadRequest { + option (gogoproto.equal) = true; + + string request_id = 1; // uint256 requestId as 0x-prefixed hex + string destination_chain = 2; // CAIP-2, e.g. "eip155:1"; web2 uses "web2:https" + bytes owner = 3; // 20-byte address or 32-byte pubkey + bytes query = 4; // chain-specific envelope, abi.encode(...) + uint32 min_confirmations = 5; // uint16 on the contract; proto3 has no uint16 + uint64 destination_block_height = 6; // height on the destination chain; unused for web2 + uint64 expiry_block_height = 7; // Push Chain height at which the request expires + uint64 created_at_height = 8; // Push Chain height the request was observed at + + // Bookkeeping — recorded for operators, not consumed by universal validators. + string callback_target = 9; // the app contract the callback routes to + string original_funder = 10; // who paid the fee (the app, not the end user) + string fees_deposited = 11; // uint256 as a decimal string + string max_fee = 12; // uint256 as a decimal string + string requested_tx_hash = 13; // Push Chain tx that emitted ReadRequested + uint64 requested_log_index = 14; // log index within that tx +} + +// ReadResult is the observation a universal validator votes on. +// +// Fields 1-4 are covered by the ballot key, so they must be byte-identical across +// validators for a ballot to converge. There is deliberately no error message field: +// local error text differs per node and would prevent agreement. +message ReadResult { + option (gogoproto.equal) = true; + + ReadStatus status = 1; + bytes result_data = 2; // ABI-encoded payload delivered to the app + uint64 observed_block_height = 3; // block number (EVM) or slot (SVM); 0 for web2 + bytes observed_block_hash = 4; // 32 bytes; empty when the chain cannot pin one + + // v2 ONLY — always empty in v1. See AggregateValue. + repeated AggregateValue aggregates = 5; +} + +// AggregateValue is one field the module combines across validators instead of +// requiring byte-equality on — a price, say, where honest nodes legitimately differ. +// +// NOT USED IN v1. The universal client rejects any extract mode other than IDENTICAL +// (externalchains/web2/read_envelope.go), so this list is always empty today. The field +// is reserved now because adding it later would change how ballots are keyed on a live +// chain, which is consensus-breaking. In v1 the ballot key covers all of fields 1-4; in +// v2 it must cover only fields 1-4 with `aggregates` EXCLUDED, so computing the key over +// "the identical subset" from the start keeps v2 purely additive. +// +// v2 ALSO REQUIRES REPLACING THE BALLOT MECHANISM, not just populating this field. +// Ballots today store a binary VoteResult{SUCCESS|FAILURE} against an ID that encodes the +// observation, so distinct observations produce distinct ballots and none reaches quorum +// when validators report different numbers. Ballots therefore cannot retain per-validator +// values, which is exactly what a median needs. v2 has to keep each validator's +// AggregateValue set in module state and reduce at quorum — the pattern x/uexecutor +// already uses for gas-price medians in keeper/chain_meta.go, which bypasses ballots for +// the same reason. +message AggregateValue { + option (gogoproto.equal) = true; + + uint32 extract_index = 1; // index into the query envelope's extract list + uint32 mode = 2; // aggregation mode; only MEDIAN is planned + bytes value = 3; // big-endian uint256/int256 +} + +// UniversalRead is the full lifecycle record of one read request. +// +// The read-side sibling of uexecutor's UniversalTx, but deliberately not the same +// shape: a read is triggered by a Push Chain event rather than an external inbound, +// performs no external write, and settles in exactly one Push Chain transaction. +message UniversalRead { + option (amino.name) = "ucallback/universal_read"; + option (gogoproto.equal) = true; + + string id = 1; // requestId, same value as request.request_id + ReadRequest request = 2; + ReadResult result = 3; // set once the ballot finalises + UniversalReadStatus status = 4; + string ballot_key = 5; + + // Push Chain execution attempts — fulfilExternalCallback and expireExternalRead. + // Repeated because fulfilment can be retried and may be followed by an expiry. + repeated uexecutor.v1.PCTx pc_tx = 6; +} diff --git a/x/ucallback/types/types.pb.go b/x/ucallback/types/types.pb.go new file mode 100644 index 00000000..1d039cb9 --- /dev/null +++ b/x/ucallback/types/types.pb.go @@ -0,0 +1,2280 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ucallback/v1/types.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + types "github.com/pushchain/push-chain-node/x/uexecutor/types" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// ReadStatus is the outcome a universal validator observed for a read. +type ReadStatus int32 + +const ( + ReadStatus_READ_STATUS_UNSPECIFIED ReadStatus = 0 + ReadStatus_READ_STATUS_SUCCESS ReadStatus = 1 + ReadStatus_READ_STATUS_ERROR ReadStatus = 2 +) + +var ReadStatus_name = map[int32]string{ + 0: "READ_STATUS_UNSPECIFIED", + 1: "READ_STATUS_SUCCESS", + 2: "READ_STATUS_ERROR", +} + +var ReadStatus_value = map[string]int32{ + "READ_STATUS_UNSPECIFIED": 0, + "READ_STATUS_SUCCESS": 1, + "READ_STATUS_ERROR": 2, +} + +func (x ReadStatus) String() string { + return proto.EnumName(ReadStatus_name, int32(x)) +} + +func (ReadStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{0} +} + +// UniversalReadStatus is the lifecycle state of a read request on Push Chain. +type UniversalReadStatus int32 + +const ( + UniversalReadStatus_UNIVERSAL_READ_STATUS_UNSPECIFIED UniversalReadStatus = 0 + UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING UniversalReadStatus = 1 + UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING UniversalReadStatus = 2 + UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED UniversalReadStatus = 3 + UniversalReadStatus_UNIVERSAL_READ_STATUS_EXPIRED UniversalReadStatus = 4 + UniversalReadStatus_UNIVERSAL_READ_STATUS_FAILED UniversalReadStatus = 5 +) + +var UniversalReadStatus_name = map[int32]string{ + 0: "UNIVERSAL_READ_STATUS_UNSPECIFIED", + 1: "UNIVERSAL_READ_STATUS_PENDING", + 2: "UNIVERSAL_READ_STATUS_VOTING", + 3: "UNIVERSAL_READ_STATUS_FULFILLED", + 4: "UNIVERSAL_READ_STATUS_EXPIRED", + 5: "UNIVERSAL_READ_STATUS_FAILED", +} + +var UniversalReadStatus_value = map[string]int32{ + "UNIVERSAL_READ_STATUS_UNSPECIFIED": 0, + "UNIVERSAL_READ_STATUS_PENDING": 1, + "UNIVERSAL_READ_STATUS_VOTING": 2, + "UNIVERSAL_READ_STATUS_FULFILLED": 3, + "UNIVERSAL_READ_STATUS_EXPIRED": 4, + "UNIVERSAL_READ_STATUS_FAILED": 5, +} + +func (x UniversalReadStatus) String() string { + return proto.EnumName(UniversalReadStatus_name, int32(x)) +} + +func (UniversalReadStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{1} +} + +// ReadRequest is one external read requested by an app on Push Chain. +// +// Every field is derived from the UniversalCallback.ReadRequested event, except +// created_at_height / requested_tx_hash / requested_log_index which come from the +// block the log was emitted in. +// +// NOTE: callbackGasLimit is deliberately absent. It is an argument to +// requestExternalReadSelf and is stored in the contract's _pending entry, but it is +// NOT emitted in ReadRequested. It has to be read back via getPendingRead(requestId) +// at fulfilment time, when the gas budget is actually needed. +type ReadRequest struct { + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + DestinationChain string `protobuf:"bytes,2,opt,name=destination_chain,json=destinationChain,proto3" json:"destination_chain,omitempty"` + Owner []byte `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` + Query []byte `protobuf:"bytes,4,opt,name=query,proto3" json:"query,omitempty"` + MinConfirmations uint32 `protobuf:"varint,5,opt,name=min_confirmations,json=minConfirmations,proto3" json:"min_confirmations,omitempty"` + DestinationBlockHeight uint64 `protobuf:"varint,6,opt,name=destination_block_height,json=destinationBlockHeight,proto3" json:"destination_block_height,omitempty"` + ExpiryBlockHeight uint64 `protobuf:"varint,7,opt,name=expiry_block_height,json=expiryBlockHeight,proto3" json:"expiry_block_height,omitempty"` + CreatedAtHeight uint64 `protobuf:"varint,8,opt,name=created_at_height,json=createdAtHeight,proto3" json:"created_at_height,omitempty"` + // Bookkeeping — recorded for operators, not consumed by universal validators. + CallbackTarget string `protobuf:"bytes,9,opt,name=callback_target,json=callbackTarget,proto3" json:"callback_target,omitempty"` + OriginalFunder string `protobuf:"bytes,10,opt,name=original_funder,json=originalFunder,proto3" json:"original_funder,omitempty"` + FeesDeposited string `protobuf:"bytes,11,opt,name=fees_deposited,json=feesDeposited,proto3" json:"fees_deposited,omitempty"` + MaxFee string `protobuf:"bytes,12,opt,name=max_fee,json=maxFee,proto3" json:"max_fee,omitempty"` + RequestedTxHash string `protobuf:"bytes,13,opt,name=requested_tx_hash,json=requestedTxHash,proto3" json:"requested_tx_hash,omitempty"` + RequestedLogIndex uint64 `protobuf:"varint,14,opt,name=requested_log_index,json=requestedLogIndex,proto3" json:"requested_log_index,omitempty"` +} + +func (m *ReadRequest) Reset() { *m = ReadRequest{} } +func (m *ReadRequest) String() string { return proto.CompactTextString(m) } +func (*ReadRequest) ProtoMessage() {} +func (*ReadRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{0} +} +func (m *ReadRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadRequest.Merge(m, src) +} +func (m *ReadRequest) XXX_Size() int { + return m.Size() +} +func (m *ReadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadRequest proto.InternalMessageInfo + +func (m *ReadRequest) GetRequestId() string { + if m != nil { + return m.RequestId + } + return "" +} + +func (m *ReadRequest) GetDestinationChain() string { + if m != nil { + return m.DestinationChain + } + return "" +} + +func (m *ReadRequest) GetOwner() []byte { + if m != nil { + return m.Owner + } + return nil +} + +func (m *ReadRequest) GetQuery() []byte { + if m != nil { + return m.Query + } + return nil +} + +func (m *ReadRequest) GetMinConfirmations() uint32 { + if m != nil { + return m.MinConfirmations + } + return 0 +} + +func (m *ReadRequest) GetDestinationBlockHeight() uint64 { + if m != nil { + return m.DestinationBlockHeight + } + return 0 +} + +func (m *ReadRequest) GetExpiryBlockHeight() uint64 { + if m != nil { + return m.ExpiryBlockHeight + } + return 0 +} + +func (m *ReadRequest) GetCreatedAtHeight() uint64 { + if m != nil { + return m.CreatedAtHeight + } + return 0 +} + +func (m *ReadRequest) GetCallbackTarget() string { + if m != nil { + return m.CallbackTarget + } + return "" +} + +func (m *ReadRequest) GetOriginalFunder() string { + if m != nil { + return m.OriginalFunder + } + return "" +} + +func (m *ReadRequest) GetFeesDeposited() string { + if m != nil { + return m.FeesDeposited + } + return "" +} + +func (m *ReadRequest) GetMaxFee() string { + if m != nil { + return m.MaxFee + } + return "" +} + +func (m *ReadRequest) GetRequestedTxHash() string { + if m != nil { + return m.RequestedTxHash + } + return "" +} + +func (m *ReadRequest) GetRequestedLogIndex() uint64 { + if m != nil { + return m.RequestedLogIndex + } + return 0 +} + +// ReadResult is the observation a universal validator votes on. +// +// Fields 1-4 are covered by the ballot key, so they must be byte-identical across +// validators for a ballot to converge. There is deliberately no error message field: +// local error text differs per node and would prevent agreement. +type ReadResult struct { + Status ReadStatus `protobuf:"varint,1,opt,name=status,proto3,enum=ucallback.v1.ReadStatus" json:"status,omitempty"` + ResultData []byte `protobuf:"bytes,2,opt,name=result_data,json=resultData,proto3" json:"result_data,omitempty"` + ObservedBlockHeight uint64 `protobuf:"varint,3,opt,name=observed_block_height,json=observedBlockHeight,proto3" json:"observed_block_height,omitempty"` + ObservedBlockHash []byte `protobuf:"bytes,4,opt,name=observed_block_hash,json=observedBlockHash,proto3" json:"observed_block_hash,omitempty"` + // v2 ONLY — always empty in v1. See AggregateValue. + Aggregates []*AggregateValue `protobuf:"bytes,5,rep,name=aggregates,proto3" json:"aggregates,omitempty"` +} + +func (m *ReadResult) Reset() { *m = ReadResult{} } +func (m *ReadResult) String() string { return proto.CompactTextString(m) } +func (*ReadResult) ProtoMessage() {} +func (*ReadResult) Descriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{1} +} +func (m *ReadResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReadResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReadResult.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReadResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadResult.Merge(m, src) +} +func (m *ReadResult) XXX_Size() int { + return m.Size() +} +func (m *ReadResult) XXX_DiscardUnknown() { + xxx_messageInfo_ReadResult.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadResult proto.InternalMessageInfo + +func (m *ReadResult) GetStatus() ReadStatus { + if m != nil { + return m.Status + } + return ReadStatus_READ_STATUS_UNSPECIFIED +} + +func (m *ReadResult) GetResultData() []byte { + if m != nil { + return m.ResultData + } + return nil +} + +func (m *ReadResult) GetObservedBlockHeight() uint64 { + if m != nil { + return m.ObservedBlockHeight + } + return 0 +} + +func (m *ReadResult) GetObservedBlockHash() []byte { + if m != nil { + return m.ObservedBlockHash + } + return nil +} + +func (m *ReadResult) GetAggregates() []*AggregateValue { + if m != nil { + return m.Aggregates + } + return nil +} + +// AggregateValue is one field the module combines across validators instead of +// requiring byte-equality on — a price, say, where honest nodes legitimately differ. +// +// NOT USED IN v1. The universal client rejects any extract mode other than IDENTICAL +// (externalchains/web2/read_envelope.go), so this list is always empty today. The field +// is reserved now because adding it later would change how ballots are keyed on a live +// chain, which is consensus-breaking. In v1 the ballot key covers all of fields 1-4; in +// v2 it must cover only fields 1-4 with `aggregates` EXCLUDED, so computing the key over +// "the identical subset" from the start keeps v2 purely additive. +// +// v2 ALSO REQUIRES REPLACING THE BALLOT MECHANISM, not just populating this field. +// Ballots today store a binary VoteResult{SUCCESS|FAILURE} against an ID that encodes the +// observation, so distinct observations produce distinct ballots and none reaches quorum +// when validators report different numbers. Ballots therefore cannot retain per-validator +// values, which is exactly what a median needs. v2 has to keep each validator's +// AggregateValue set in module state and reduce at quorum — the pattern x/uexecutor +// already uses for gas-price medians in keeper/chain_meta.go, which bypasses ballots for +// the same reason. +type AggregateValue struct { + ExtractIndex uint32 `protobuf:"varint,1,opt,name=extract_index,json=extractIndex,proto3" json:"extract_index,omitempty"` + Mode uint32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"` + Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *AggregateValue) Reset() { *m = AggregateValue{} } +func (m *AggregateValue) String() string { return proto.CompactTextString(m) } +func (*AggregateValue) ProtoMessage() {} +func (*AggregateValue) Descriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{2} +} +func (m *AggregateValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AggregateValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AggregateValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AggregateValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_AggregateValue.Merge(m, src) +} +func (m *AggregateValue) XXX_Size() int { + return m.Size() +} +func (m *AggregateValue) XXX_DiscardUnknown() { + xxx_messageInfo_AggregateValue.DiscardUnknown(m) +} + +var xxx_messageInfo_AggregateValue proto.InternalMessageInfo + +func (m *AggregateValue) GetExtractIndex() uint32 { + if m != nil { + return m.ExtractIndex + } + return 0 +} + +func (m *AggregateValue) GetMode() uint32 { + if m != nil { + return m.Mode + } + return 0 +} + +func (m *AggregateValue) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +// UniversalRead is the full lifecycle record of one read request. +// +// The read-side sibling of uexecutor's UniversalTx, but deliberately not the same +// shape: a read is triggered by a Push Chain event rather than an external inbound, +// performs no external write, and settles in exactly one Push Chain transaction. +type UniversalRead struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Request *ReadRequest `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Result *ReadResult `protobuf:"bytes,3,opt,name=result,proto3" json:"result,omitempty"` + Status UniversalReadStatus `protobuf:"varint,4,opt,name=status,proto3,enum=ucallback.v1.UniversalReadStatus" json:"status,omitempty"` + BallotKey string `protobuf:"bytes,5,opt,name=ballot_key,json=ballotKey,proto3" json:"ballot_key,omitempty"` + // Push Chain execution attempts — fulfilExternalCallback and expireExternalRead. + // Repeated because fulfilment can be retried and may be followed by an expiry. + PcTx []*types.PCTx `protobuf:"bytes,6,rep,name=pc_tx,json=pcTx,proto3" json:"pc_tx,omitempty"` +} + +func (m *UniversalRead) Reset() { *m = UniversalRead{} } +func (m *UniversalRead) String() string { return proto.CompactTextString(m) } +func (*UniversalRead) ProtoMessage() {} +func (*UniversalRead) Descriptor() ([]byte, []int) { + return fileDescriptor_bdb5182bd84a8426, []int{3} +} +func (m *UniversalRead) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UniversalRead) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UniversalRead.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UniversalRead) XXX_Merge(src proto.Message) { + xxx_messageInfo_UniversalRead.Merge(m, src) +} +func (m *UniversalRead) XXX_Size() int { + return m.Size() +} +func (m *UniversalRead) XXX_DiscardUnknown() { + xxx_messageInfo_UniversalRead.DiscardUnknown(m) +} + +var xxx_messageInfo_UniversalRead proto.InternalMessageInfo + +func (m *UniversalRead) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *UniversalRead) GetRequest() *ReadRequest { + if m != nil { + return m.Request + } + return nil +} + +func (m *UniversalRead) GetResult() *ReadResult { + if m != nil { + return m.Result + } + return nil +} + +func (m *UniversalRead) GetStatus() UniversalReadStatus { + if m != nil { + return m.Status + } + return UniversalReadStatus_UNIVERSAL_READ_STATUS_UNSPECIFIED +} + +func (m *UniversalRead) GetBallotKey() string { + if m != nil { + return m.BallotKey + } + return "" +} + +func (m *UniversalRead) GetPcTx() []*types.PCTx { + if m != nil { + return m.PcTx + } + return nil +} + +func init() { + proto.RegisterEnum("ucallback.v1.ReadStatus", ReadStatus_name, ReadStatus_value) + proto.RegisterEnum("ucallback.v1.UniversalReadStatus", UniversalReadStatus_name, UniversalReadStatus_value) + proto.RegisterType((*ReadRequest)(nil), "ucallback.v1.ReadRequest") + proto.RegisterType((*ReadResult)(nil), "ucallback.v1.ReadResult") + proto.RegisterType((*AggregateValue)(nil), "ucallback.v1.AggregateValue") + proto.RegisterType((*UniversalRead)(nil), "ucallback.v1.UniversalRead") +} + +func init() { proto.RegisterFile("ucallback/v1/types.proto", fileDescriptor_bdb5182bd84a8426) } + +var fileDescriptor_bdb5182bd84a8426 = []byte{ + // 939 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x55, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0x36, 0x65, 0xd9, 0xa9, 0x56, 0x3f, 0x96, 0xd6, 0x49, 0xcd, 0xba, 0xb1, 0x2c, 0x27, 0x08, + 0x22, 0xb8, 0x88, 0xd4, 0x38, 0x40, 0xd1, 0x06, 0xbd, 0x28, 0x12, 0xd5, 0x08, 0x15, 0x1c, 0x61, + 0x25, 0x19, 0x45, 0x2f, 0x8b, 0x15, 0x39, 0xa6, 0x08, 0x53, 0xa4, 0x42, 0x2e, 0x55, 0xea, 0x09, + 0x0a, 0xf4, 0xd4, 0x47, 0xe8, 0xa1, 0x87, 0x1e, 0x7a, 0xe8, 0x03, 0xf4, 0x01, 0x7a, 0xcc, 0xb1, + 0xc7, 0xc2, 0x3e, 0xb4, 0xd7, 0xbe, 0x41, 0xb1, 0x4b, 0x52, 0xa1, 0x5c, 0xf9, 0x22, 0x2c, 0xbf, + 0xef, 0xdb, 0x99, 0xd9, 0xf9, 0x66, 0xb5, 0x48, 0x0d, 0x74, 0x66, 0xdb, 0x13, 0xa6, 0x5f, 0x35, + 0x17, 0xcf, 0x9b, 0x7c, 0x39, 0x07, 0xbf, 0x31, 0xf7, 0x5c, 0xee, 0xe2, 0xc2, 0x8a, 0x69, 0x2c, + 0x9e, 0x1f, 0xde, 0x37, 0x5d, 0xd3, 0x95, 0x44, 0x53, 0xac, 0x22, 0xcd, 0x61, 0x85, 0xcd, 0x2c, + 0xc7, 0x6d, 0xca, 0xdf, 0x18, 0x52, 0x03, 0x08, 0x41, 0x0f, 0xb8, 0xeb, 0xdd, 0x0a, 0xf8, 0xe8, + 0xf7, 0x2c, 0xca, 0x13, 0x60, 0x06, 0x81, 0xb7, 0x01, 0xf8, 0x1c, 0x1f, 0x21, 0xe4, 0x45, 0x4b, + 0x6a, 0x19, 0xaa, 0x52, 0x53, 0xea, 0x39, 0x92, 0x8b, 0x91, 0x9e, 0x81, 0x3f, 0x41, 0x15, 0x03, + 0x7c, 0x6e, 0x39, 0x8c, 0x5b, 0xae, 0x43, 0xf5, 0x29, 0xb3, 0x1c, 0x35, 0x23, 0x55, 0xe5, 0x14, + 0xd1, 0x16, 0x38, 0xbe, 0x8f, 0x76, 0xdc, 0xef, 0x1c, 0xf0, 0xd4, 0xed, 0x9a, 0x52, 0x2f, 0x90, + 0xe8, 0x43, 0xa0, 0x6f, 0x03, 0xf0, 0x96, 0x6a, 0x36, 0x42, 0xe5, 0x87, 0x08, 0x3c, 0xb3, 0x1c, + 0xaa, 0xbb, 0xce, 0xa5, 0xe5, 0xcd, 0x64, 0x10, 0x5f, 0xdd, 0xa9, 0x29, 0xf5, 0x22, 0x29, 0xcf, + 0x2c, 0xa7, 0x9d, 0xc6, 0xf1, 0xe7, 0x48, 0x4d, 0x57, 0x31, 0xb1, 0x5d, 0xfd, 0x8a, 0x4e, 0xc1, + 0x32, 0xa7, 0x5c, 0xdd, 0xad, 0x29, 0xf5, 0x2c, 0xf9, 0x30, 0xc5, 0xbf, 0x12, 0xf4, 0x6b, 0xc9, + 0xe2, 0x06, 0xda, 0x87, 0x70, 0x6e, 0x79, 0xcb, 0xf5, 0x4d, 0xf7, 0xe4, 0xa6, 0x4a, 0x44, 0xa5, + 0xf5, 0xa7, 0xa8, 0xa2, 0x7b, 0xc0, 0x38, 0x18, 0x94, 0xf1, 0x44, 0xfd, 0x81, 0x54, 0xef, 0xc5, + 0x44, 0x8b, 0xc7, 0xda, 0xa7, 0x68, 0x2f, 0x31, 0x87, 0x72, 0xe6, 0x99, 0xc0, 0xd5, 0x9c, 0xec, + 0x4c, 0x29, 0x81, 0x47, 0x12, 0x15, 0x42, 0xd7, 0xb3, 0x4c, 0xcb, 0x61, 0x36, 0xbd, 0x0c, 0x1c, + 0x03, 0x3c, 0x15, 0x45, 0xc2, 0x04, 0xee, 0x4a, 0x14, 0x3f, 0x41, 0xa5, 0x4b, 0x00, 0x9f, 0x1a, + 0x30, 0x77, 0x7d, 0x8b, 0x83, 0xa1, 0xe6, 0xa5, 0xae, 0x28, 0xd0, 0x4e, 0x02, 0xe2, 0x03, 0x74, + 0x6f, 0xc6, 0x42, 0x7a, 0x09, 0xa0, 0x16, 0x24, 0xbf, 0x3b, 0x63, 0x61, 0x17, 0x40, 0x54, 0x1f, + 0x5b, 0x07, 0x06, 0xe5, 0x21, 0x9d, 0x32, 0x7f, 0xaa, 0x16, 0xa5, 0x64, 0x6f, 0x45, 0x8c, 0xc2, + 0xd7, 0xcc, 0x9f, 0x8a, 0xce, 0xbc, 0xd7, 0xda, 0xae, 0x49, 0x2d, 0xc7, 0x80, 0x50, 0x2d, 0x45, + 0x9d, 0x59, 0x51, 0x7d, 0xd7, 0xec, 0x09, 0xe2, 0x65, 0xf6, 0x9f, 0x9f, 0x8e, 0x95, 0x47, 0xdf, + 0x67, 0x10, 0x8a, 0xc6, 0xc7, 0x0f, 0x6c, 0x8e, 0x3f, 0x45, 0xbb, 0x3e, 0x67, 0x3c, 0xf0, 0xe5, + 0xe4, 0x94, 0xce, 0xd4, 0x46, 0x7a, 0x5e, 0x1b, 0x42, 0x39, 0x94, 0x3c, 0x89, 0x75, 0xf8, 0x18, + 0xe5, 0x3d, 0xb9, 0x97, 0x1a, 0x8c, 0x33, 0x39, 0x4a, 0x05, 0x82, 0x22, 0xa8, 0xc3, 0x38, 0xc3, + 0x67, 0xe8, 0x81, 0x3b, 0xf1, 0xc1, 0x5b, 0x80, 0xb1, 0xee, 0xd9, 0xb6, 0xac, 0x6c, 0x3f, 0x21, + 0x6f, 0xb9, 0x7c, 0x7b, 0x8f, 0x38, 0x79, 0x34, 0x70, 0x95, 0xf5, 0x1d, 0xe2, 0xec, 0x5f, 0x22, + 0xc4, 0x4c, 0xd3, 0x03, 0x93, 0x71, 0x10, 0x53, 0xb7, 0x5d, 0xcf, 0x9f, 0x3d, 0x5c, 0x2f, 0xbd, + 0x95, 0xf0, 0x17, 0xcc, 0x0e, 0x80, 0xa4, 0xf4, 0x71, 0x27, 0x00, 0x95, 0xd6, 0x35, 0xf8, 0x31, + 0x2a, 0x42, 0xc8, 0x3d, 0xa6, 0xf3, 0xb8, 0x97, 0x8a, 0x1c, 0xe7, 0x42, 0x0c, 0xca, 0x36, 0x62, + 0x8c, 0xb2, 0x33, 0xd7, 0x00, 0x79, 0xf0, 0x22, 0x91, 0x6b, 0x71, 0x43, 0x16, 0x22, 0x42, 0x72, + 0x6f, 0xe4, 0x47, 0x9c, 0xe6, 0xd7, 0x0c, 0x2a, 0x8e, 0x1d, 0x6b, 0x01, 0x9e, 0xcf, 0x6c, 0xd1, + 0x4f, 0x5c, 0x42, 0x99, 0xd5, 0x4d, 0xcd, 0x58, 0x06, 0x7e, 0x81, 0xee, 0xc5, 0x6e, 0xc9, 0xa0, + 0xf9, 0xb3, 0x8f, 0xfe, 0x6f, 0x42, 0x7c, 0xdb, 0x49, 0xa2, 0x14, 0xc6, 0x45, 0x3d, 0x97, 0x39, + 0xf3, 0x9b, 0x8c, 0x8b, 0x2c, 0x26, 0xb1, 0x0e, 0x7f, 0xb1, 0xb2, 0x3a, 0x2b, 0xad, 0x3e, 0x59, + 0xdf, 0xb1, 0x56, 0xe3, 0x2d, 0xcf, 0x8f, 0x10, 0x9a, 0x30, 0xdb, 0x76, 0x39, 0xbd, 0x82, 0xa5, + 0xbc, 0xe4, 0x39, 0x92, 0x8b, 0x90, 0xaf, 0x61, 0x89, 0x9f, 0xa2, 0x9d, 0xb9, 0x4e, 0x79, 0xa8, + 0xee, 0x4a, 0x23, 0x70, 0x63, 0xf5, 0xe7, 0x25, 0x02, 0x0f, 0xda, 0xa3, 0x90, 0x64, 0xe7, 0xfa, + 0x28, 0x7c, 0x79, 0x22, 0x3a, 0xf2, 0xc3, 0xdf, 0xbf, 0x9d, 0xa6, 0xfe, 0x2f, 0x83, 0x24, 0x2f, + 0xf5, 0x80, 0x19, 0xa7, 0x34, 0x1a, 0xcf, 0xa8, 0x00, 0xfc, 0x31, 0x3a, 0x20, 0x5a, 0xab, 0x43, + 0x87, 0xa3, 0xd6, 0x68, 0x3c, 0xa4, 0xe3, 0xf3, 0xe1, 0x40, 0x6b, 0xf7, 0xba, 0x3d, 0xad, 0x53, + 0xde, 0xc2, 0x07, 0x68, 0x3f, 0x4d, 0x0e, 0xc7, 0xed, 0xb6, 0x36, 0x1c, 0x96, 0x15, 0xfc, 0x00, + 0x55, 0xd2, 0x84, 0x46, 0xc8, 0x1b, 0x52, 0xce, 0x1c, 0x66, 0x7f, 0xf9, 0xb9, 0xaa, 0x9c, 0xfe, + 0xab, 0xa0, 0xfd, 0x0d, 0x67, 0xc5, 0x4f, 0xd0, 0xc9, 0xf8, 0xbc, 0x77, 0xa1, 0x91, 0x61, 0xab, + 0x4f, 0xef, 0x4e, 0x7a, 0x82, 0x8e, 0x36, 0xcb, 0x06, 0xda, 0x79, 0xa7, 0x77, 0xfe, 0x55, 0x59, + 0xc1, 0x35, 0xf4, 0x70, 0xb3, 0xe4, 0xe2, 0xcd, 0x48, 0x28, 0x32, 0xf8, 0x31, 0x3a, 0xde, 0xac, + 0xe8, 0x8e, 0xfb, 0xdd, 0x5e, 0xbf, 0xaf, 0x75, 0xca, 0xdb, 0x77, 0x67, 0xd2, 0xbe, 0x19, 0xf4, + 0x88, 0xd6, 0x29, 0x67, 0xef, 0xce, 0xd4, 0x6d, 0xf5, 0x44, 0x90, 0x9d, 0xe8, 0xcc, 0xaf, 0x06, + 0x7f, 0x5c, 0x57, 0x95, 0x77, 0xd7, 0x55, 0xe5, 0xaf, 0xeb, 0xaa, 0xf2, 0xe3, 0x4d, 0x75, 0xeb, + 0xdd, 0x4d, 0x75, 0xeb, 0xcf, 0x9b, 0xea, 0xd6, 0xb7, 0x9f, 0x99, 0x16, 0x9f, 0x06, 0x93, 0x86, + 0xee, 0xce, 0x9a, 0xf3, 0xc0, 0x9f, 0xca, 0xf7, 0x41, 0xae, 0x9e, 0xc9, 0xe5, 0x33, 0xc7, 0x35, + 0xa0, 0x19, 0x36, 0xdf, 0xfb, 0x25, 0xdf, 0xa2, 0xc9, 0xae, 0x7c, 0x8c, 0x5e, 0xfc, 0x17, 0x00, + 0x00, 0xff, 0xff, 0x80, 0x05, 0x37, 0x8d, 0xf9, 0x06, 0x00, 0x00, +} + +func (this *ReadRequest) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ReadRequest) + if !ok { + that2, ok := that.(ReadRequest) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.RequestId != that1.RequestId { + return false + } + if this.DestinationChain != that1.DestinationChain { + return false + } + if !bytes.Equal(this.Owner, that1.Owner) { + return false + } + if !bytes.Equal(this.Query, that1.Query) { + return false + } + if this.MinConfirmations != that1.MinConfirmations { + return false + } + if this.DestinationBlockHeight != that1.DestinationBlockHeight { + return false + } + if this.ExpiryBlockHeight != that1.ExpiryBlockHeight { + return false + } + if this.CreatedAtHeight != that1.CreatedAtHeight { + return false + } + if this.CallbackTarget != that1.CallbackTarget { + return false + } + if this.OriginalFunder != that1.OriginalFunder { + return false + } + if this.FeesDeposited != that1.FeesDeposited { + return false + } + if this.MaxFee != that1.MaxFee { + return false + } + if this.RequestedTxHash != that1.RequestedTxHash { + return false + } + if this.RequestedLogIndex != that1.RequestedLogIndex { + return false + } + return true +} +func (this *ReadResult) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*ReadResult) + if !ok { + that2, ok := that.(ReadResult) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Status != that1.Status { + return false + } + if !bytes.Equal(this.ResultData, that1.ResultData) { + return false + } + if this.ObservedBlockHeight != that1.ObservedBlockHeight { + return false + } + if !bytes.Equal(this.ObservedBlockHash, that1.ObservedBlockHash) { + return false + } + if len(this.Aggregates) != len(that1.Aggregates) { + return false + } + for i := range this.Aggregates { + if !this.Aggregates[i].Equal(that1.Aggregates[i]) { + return false + } + } + return true +} +func (this *AggregateValue) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*AggregateValue) + if !ok { + that2, ok := that.(AggregateValue) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.ExtractIndex != that1.ExtractIndex { + return false + } + if this.Mode != that1.Mode { + return false + } + if !bytes.Equal(this.Value, that1.Value) { + return false + } + return true +} +func (this *UniversalRead) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*UniversalRead) + if !ok { + that2, ok := that.(UniversalRead) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Id != that1.Id { + return false + } + if !this.Request.Equal(that1.Request) { + return false + } + if !this.Result.Equal(that1.Result) { + return false + } + if this.Status != that1.Status { + return false + } + if this.BallotKey != that1.BallotKey { + return false + } + if len(this.PcTx) != len(that1.PcTx) { + return false + } + for i := range this.PcTx { + if !this.PcTx[i].Equal(that1.PcTx[i]) { + return false + } + } + return true +} +func (m *ReadRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReadRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReadRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.RequestedLogIndex != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.RequestedLogIndex)) + i-- + dAtA[i] = 0x70 + } + if len(m.RequestedTxHash) > 0 { + i -= len(m.RequestedTxHash) + copy(dAtA[i:], m.RequestedTxHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.RequestedTxHash))) + i-- + dAtA[i] = 0x6a + } + if len(m.MaxFee) > 0 { + i -= len(m.MaxFee) + copy(dAtA[i:], m.MaxFee) + i = encodeVarintTypes(dAtA, i, uint64(len(m.MaxFee))) + i-- + dAtA[i] = 0x62 + } + if len(m.FeesDeposited) > 0 { + i -= len(m.FeesDeposited) + copy(dAtA[i:], m.FeesDeposited) + i = encodeVarintTypes(dAtA, i, uint64(len(m.FeesDeposited))) + i-- + dAtA[i] = 0x5a + } + if len(m.OriginalFunder) > 0 { + i -= len(m.OriginalFunder) + copy(dAtA[i:], m.OriginalFunder) + i = encodeVarintTypes(dAtA, i, uint64(len(m.OriginalFunder))) + i-- + dAtA[i] = 0x52 + } + if len(m.CallbackTarget) > 0 { + i -= len(m.CallbackTarget) + copy(dAtA[i:], m.CallbackTarget) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CallbackTarget))) + i-- + dAtA[i] = 0x4a + } + if m.CreatedAtHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.CreatedAtHeight)) + i-- + dAtA[i] = 0x40 + } + if m.ExpiryBlockHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ExpiryBlockHeight)) + i-- + dAtA[i] = 0x38 + } + if m.DestinationBlockHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.DestinationBlockHeight)) + i-- + dAtA[i] = 0x30 + } + if m.MinConfirmations != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.MinConfirmations)) + i-- + dAtA[i] = 0x28 + } + if len(m.Query) > 0 { + i -= len(m.Query) + copy(dAtA[i:], m.Query) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Query))) + i-- + dAtA[i] = 0x22 + } + if len(m.Owner) > 0 { + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Owner))) + i-- + dAtA[i] = 0x1a + } + if len(m.DestinationChain) > 0 { + i -= len(m.DestinationChain) + copy(dAtA[i:], m.DestinationChain) + i = encodeVarintTypes(dAtA, i, uint64(len(m.DestinationChain))) + i-- + dAtA[i] = 0x12 + } + if len(m.RequestId) > 0 { + i -= len(m.RequestId) + copy(dAtA[i:], m.RequestId) + i = encodeVarintTypes(dAtA, i, uint64(len(m.RequestId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ReadResult) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReadResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReadResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Aggregates) > 0 { + for iNdEx := len(m.Aggregates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Aggregates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.ObservedBlockHash) > 0 { + i -= len(m.ObservedBlockHash) + copy(dAtA[i:], m.ObservedBlockHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ObservedBlockHash))) + i-- + dAtA[i] = 0x22 + } + if m.ObservedBlockHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ObservedBlockHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.ResultData) > 0 { + i -= len(m.ResultData) + copy(dAtA[i:], m.ResultData) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ResultData))) + i-- + dAtA[i] = 0x12 + } + if m.Status != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *AggregateValue) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AggregateValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AggregateValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x1a + } + if m.Mode != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x10 + } + if m.ExtractIndex != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ExtractIndex)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *UniversalRead) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UniversalRead) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UniversalRead) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PcTx) > 0 { + for iNdEx := len(m.PcTx) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PcTx[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.BallotKey) > 0 { + i -= len(m.BallotKey) + copy(dAtA[i:], m.BallotKey) + i = encodeVarintTypes(dAtA, i, uint64(len(m.BallotKey))) + i-- + dAtA[i] = 0x2a + } + if m.Status != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.Result != nil { + { + size, err := m.Result.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Request != nil { + { + size, err := m.Request.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { + offset -= sovTypes(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *ReadRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RequestId) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.DestinationChain) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Owner) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.Query) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.MinConfirmations != 0 { + n += 1 + sovTypes(uint64(m.MinConfirmations)) + } + if m.DestinationBlockHeight != 0 { + n += 1 + sovTypes(uint64(m.DestinationBlockHeight)) + } + if m.ExpiryBlockHeight != 0 { + n += 1 + sovTypes(uint64(m.ExpiryBlockHeight)) + } + if m.CreatedAtHeight != 0 { + n += 1 + sovTypes(uint64(m.CreatedAtHeight)) + } + l = len(m.CallbackTarget) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.OriginalFunder) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.FeesDeposited) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.MaxFee) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.RequestedTxHash) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.RequestedLogIndex != 0 { + n += 1 + sovTypes(uint64(m.RequestedLogIndex)) + } + return n +} + +func (m *ReadResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Status != 0 { + n += 1 + sovTypes(uint64(m.Status)) + } + l = len(m.ResultData) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.ObservedBlockHeight != 0 { + n += 1 + sovTypes(uint64(m.ObservedBlockHeight)) + } + l = len(m.ObservedBlockHash) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if len(m.Aggregates) > 0 { + for _, e := range m.Aggregates { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + return n +} + +func (m *AggregateValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExtractIndex != 0 { + n += 1 + sovTypes(uint64(m.ExtractIndex)) + } + if m.Mode != 0 { + n += 1 + sovTypes(uint64(m.Mode)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *UniversalRead) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Request != nil { + l = m.Request.Size() + n += 1 + l + sovTypes(uint64(l)) + } + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovTypes(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovTypes(uint64(m.Status)) + } + l = len(m.BallotKey) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if len(m.PcTx) > 0 { + for _, e := range m.PcTx { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + return n +} + +func sovTypes(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTypes(x uint64) (n int) { + return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *ReadRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationChain", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DestinationChain = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Owner", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Owner = append(m.Owner[:0], dAtA[iNdEx:postIndex]...) + if m.Owner == nil { + m.Owner = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Query", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Query = append(m.Query[:0], dAtA[iNdEx:postIndex]...) + if m.Query == nil { + m.Query = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinConfirmations", wireType) + } + m.MinConfirmations = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinConfirmations |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationBlockHeight", wireType) + } + m.DestinationBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DestinationBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpiryBlockHeight", wireType) + } + m.ExpiryBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpiryBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAtHeight", wireType) + } + m.CreatedAtHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CreatedAtHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CallbackTarget", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CallbackTarget = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OriginalFunder", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.OriginalFunder = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeesDeposited", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeesDeposited = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxFee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MaxFee = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestedTxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestedTxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestedLogIndex", wireType) + } + m.RequestedLogIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RequestedLogIndex |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReadResult) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReadResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReadResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= ReadStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResultData", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResultData = append(m.ResultData[:0], dAtA[iNdEx:postIndex]...) + if m.ResultData == nil { + m.ResultData = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedBlockHeight", wireType) + } + m.ObservedBlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ObservedBlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObservedBlockHash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ObservedBlockHash = append(m.ObservedBlockHash[:0], dAtA[iNdEx:postIndex]...) + if m.ObservedBlockHash == nil { + m.ObservedBlockHash = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Aggregates", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Aggregates = append(m.Aggregates, &AggregateValue{}) + if err := m.Aggregates[len(m.Aggregates)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AggregateValue) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AggregateValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AggregateValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtractIndex", wireType) + } + m.ExtractIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExtractIndex |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UniversalRead) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UniversalRead: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UniversalRead: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Request == nil { + m.Request = &ReadRequest{} + } + if err := m.Request.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &ReadResult{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= UniversalReadStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BallotKey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BallotKey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PcTx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PcTx = append(m.PcTx, &types.PCTx{}) + if err := m.PcTx[len(m.PcTx)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTypes(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTypes + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTypes + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTypes + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTypes + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group") +) From b16c3b6e48adfb3df6d1f2f170caeb80f636640d Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:46:38 +0530 Subject: [PATCH 28/54] feat(ucallback): add universal reads to genesis state --- api/ucallback/v1/genesis.pulsar.go | 797 +++++++++++++++++++++++++++-- proto/ucallback/v1/genesis.proto | 15 + x/ucallback/types/genesis.pb.go | 327 +++++++++++- 3 files changed, 1088 insertions(+), 51 deletions(-) diff --git a/api/ucallback/v1/genesis.pulsar.go b/api/ucallback/v1/genesis.pulsar.go index e05e130a..18e19d49 100644 --- a/api/ucallback/v1/genesis.pulsar.go +++ b/api/ucallback/v1/genesis.pulsar.go @@ -14,15 +14,68 @@ import ( sync "sync" ) +var _ protoreflect.List = (*_GenesisState_2_list)(nil) + +type _GenesisState_2_list struct { + list *[]*UniversalReadEntry +} + +func (x *_GenesisState_2_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_GenesisState_2_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_GenesisState_2_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalReadEntry) + (*x.list)[i] = concreteValue +} + +func (x *_GenesisState_2_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalReadEntry) + *x.list = append(*x.list, concreteValue) +} + +func (x *_GenesisState_2_list) AppendMutable() protoreflect.Value { + v := new(UniversalReadEntry) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_GenesisState_2_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_GenesisState_2_list) NewElement() protoreflect.Value { + v := new(UniversalReadEntry) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_GenesisState_2_list) IsValid() bool { + return x.list != nil +} + var ( - md_GenesisState protoreflect.MessageDescriptor - fd_GenesisState_params protoreflect.FieldDescriptor + md_GenesisState protoreflect.MessageDescriptor + fd_GenesisState_params protoreflect.FieldDescriptor + fd_GenesisState_universal_reads protoreflect.FieldDescriptor ) func init() { file_ucallback_v1_genesis_proto_init() md_GenesisState = File_ucallback_v1_genesis_proto.Messages().ByName("GenesisState") fd_GenesisState_params = md_GenesisState.Fields().ByName("params") + fd_GenesisState_universal_reads = md_GenesisState.Fields().ByName("universal_reads") } var _ protoreflect.Message = (*fastReflection_GenesisState)(nil) @@ -96,6 +149,12 @@ func (x *fastReflection_GenesisState) Range(f func(protoreflect.FieldDescriptor, return } } + if len(x.UniversalReads) != 0 { + value := protoreflect.ValueOfList(&_GenesisState_2_list{list: &x.UniversalReads}) + if !f(fd_GenesisState_universal_reads, value) { + return + } + } } // Has reports whether a field is populated. @@ -113,6 +172,8 @@ func (x *fastReflection_GenesisState) Has(fd protoreflect.FieldDescriptor) bool switch fd.FullName() { case "ucallback.v1.GenesisState.params": return x.Params != nil + case "ucallback.v1.GenesisState.universal_reads": + return len(x.UniversalReads) != 0 default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -131,6 +192,8 @@ func (x *fastReflection_GenesisState) Clear(fd protoreflect.FieldDescriptor) { switch fd.FullName() { case "ucallback.v1.GenesisState.params": x.Params = nil + case "ucallback.v1.GenesisState.universal_reads": + x.UniversalReads = nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -150,6 +213,12 @@ func (x *fastReflection_GenesisState) Get(descriptor protoreflect.FieldDescripto case "ucallback.v1.GenesisState.params": value := x.Params return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "ucallback.v1.GenesisState.universal_reads": + if len(x.UniversalReads) == 0 { + return protoreflect.ValueOfList(&_GenesisState_2_list{}) + } + listValue := &_GenesisState_2_list{list: &x.UniversalReads} + return protoreflect.ValueOfList(listValue) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -172,6 +241,10 @@ func (x *fastReflection_GenesisState) Set(fd protoreflect.FieldDescriptor, value switch fd.FullName() { case "ucallback.v1.GenesisState.params": x.Params = value.Message().Interface().(*Params) + case "ucallback.v1.GenesisState.universal_reads": + lv := value.List() + clv := lv.(*_GenesisState_2_list) + x.UniversalReads = *clv.list default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -197,6 +270,12 @@ func (x *fastReflection_GenesisState) Mutable(fd protoreflect.FieldDescriptor) p x.Params = new(Params) } return protoreflect.ValueOfMessage(x.Params.ProtoReflect()) + case "ucallback.v1.GenesisState.universal_reads": + if x.UniversalReads == nil { + x.UniversalReads = []*UniversalReadEntry{} + } + value := &_GenesisState_2_list{list: &x.UniversalReads} + return protoreflect.ValueOfList(value) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -213,6 +292,9 @@ func (x *fastReflection_GenesisState) NewField(fd protoreflect.FieldDescriptor) case "ucallback.v1.GenesisState.params": m := new(Params) return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "ucallback.v1.GenesisState.universal_reads": + list := []*UniversalReadEntry{} + return protoreflect.ValueOfList(&_GenesisState_2_list{list: &list}) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.GenesisState")) @@ -286,6 +368,12 @@ func (x *fastReflection_GenesisState) ProtoMethods() *protoiface.Methods { l = options.Size(x.Params) n += 1 + l + runtime.Sov(uint64(l)) } + if len(x.UniversalReads) > 0 { + for _, e := range x.UniversalReads { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -315,6 +403,22 @@ func (x *fastReflection_GenesisState) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if len(x.UniversalReads) > 0 { + for iNdEx := len(x.UniversalReads) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.UniversalReads[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + } if x.Params != nil { encoded, err := options.Marshal(x.Params) if err != nil { @@ -414,6 +518,539 @@ func (x *fastReflection_GenesisState) ProtoMethods() *protoiface.Methods { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field UniversalReads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.UniversalReads = append(x.UniversalReads, &UniversalReadEntry{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.UniversalReads[len(x.UniversalReads)-1]); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_UniversalReadEntry protoreflect.MessageDescriptor + fd_UniversalReadEntry_key protoreflect.FieldDescriptor + fd_UniversalReadEntry_value protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_genesis_proto_init() + md_UniversalReadEntry = File_ucallback_v1_genesis_proto.Messages().ByName("UniversalReadEntry") + fd_UniversalReadEntry_key = md_UniversalReadEntry.Fields().ByName("key") + fd_UniversalReadEntry_value = md_UniversalReadEntry.Fields().ByName("value") +} + +var _ protoreflect.Message = (*fastReflection_UniversalReadEntry)(nil) + +type fastReflection_UniversalReadEntry UniversalReadEntry + +func (x *UniversalReadEntry) ProtoReflect() protoreflect.Message { + return (*fastReflection_UniversalReadEntry)(x) +} + +func (x *UniversalReadEntry) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_UniversalReadEntry_messageType fastReflection_UniversalReadEntry_messageType +var _ protoreflect.MessageType = fastReflection_UniversalReadEntry_messageType{} + +type fastReflection_UniversalReadEntry_messageType struct{} + +func (x fastReflection_UniversalReadEntry_messageType) Zero() protoreflect.Message { + return (*fastReflection_UniversalReadEntry)(nil) +} +func (x fastReflection_UniversalReadEntry_messageType) New() protoreflect.Message { + return new(fastReflection_UniversalReadEntry) +} +func (x fastReflection_UniversalReadEntry_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_UniversalReadEntry +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_UniversalReadEntry) Descriptor() protoreflect.MessageDescriptor { + return md_UniversalReadEntry +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_UniversalReadEntry) Type() protoreflect.MessageType { + return _fastReflection_UniversalReadEntry_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_UniversalReadEntry) New() protoreflect.Message { + return new(fastReflection_UniversalReadEntry) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_UniversalReadEntry) Interface() protoreflect.ProtoMessage { + return (*UniversalReadEntry)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_UniversalReadEntry) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Key != "" { + value := protoreflect.ValueOfString(x.Key) + if !f(fd_UniversalReadEntry_key, value) { + return + } + } + if x.Value != nil { + value := protoreflect.ValueOfMessage(x.Value.ProtoReflect()) + if !f(fd_UniversalReadEntry_value, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_UniversalReadEntry) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.UniversalReadEntry.key": + return x.Key != "" + case "ucallback.v1.UniversalReadEntry.value": + return x.Value != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalReadEntry) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.UniversalReadEntry.key": + x.Key = "" + case "ucallback.v1.UniversalReadEntry.value": + x.Value = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_UniversalReadEntry) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.UniversalReadEntry.key": + value := x.Key + return protoreflect.ValueOfString(value) + case "ucallback.v1.UniversalReadEntry.value": + value := x.Value + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalReadEntry) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.UniversalReadEntry.key": + x.Key = value.Interface().(string) + case "ucallback.v1.UniversalReadEntry.value": + x.Value = value.Message().Interface().(*UniversalRead) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalReadEntry) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.UniversalReadEntry.value": + if x.Value == nil { + x.Value = new(UniversalRead) + } + return protoreflect.ValueOfMessage(x.Value.ProtoReflect()) + case "ucallback.v1.UniversalReadEntry.key": + panic(fmt.Errorf("field key of message ucallback.v1.UniversalReadEntry is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_UniversalReadEntry) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.UniversalReadEntry.key": + return protoreflect.ValueOfString("") + case "ucallback.v1.UniversalReadEntry.value": + m := new(UniversalRead) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.UniversalReadEntry")) + } + panic(fmt.Errorf("message ucallback.v1.UniversalReadEntry does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_UniversalReadEntry) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.UniversalReadEntry", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_UniversalReadEntry) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_UniversalReadEntry) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_UniversalReadEntry) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_UniversalReadEntry) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*UniversalReadEntry) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Key) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Value != nil { + l = options.Size(x.Value) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*UniversalReadEntry) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Value != nil { + encoded, err := options.Marshal(x.Value) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + if len(x.Key) > 0 { + i -= len(x.Key) + copy(dAtA[i:], x.Key) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Key))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*UniversalReadEntry) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: UniversalReadEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: UniversalReadEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Value == nil { + x.Value = &UniversalRead{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Value); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -469,7 +1106,7 @@ func (x *Params) ProtoReflect() protoreflect.Message { } func (x *Params) slowProtoReflect() protoreflect.Message { - mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + mi := &file_ucallback_v1_genesis_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -880,6 +1517,13 @@ type GenesisState struct { // Params defines all the parameters of the module. Params *Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params,omitempty"` + // universal_reads are key-value pairs from the UniversalReads map. + // + // Only the canonical records are exported. PendingByExpiry and ReadsByTxHash + // are indexes derived from these, and are rebuilt during InitGenesis rather + // than exported — so they cannot be imported out of sync with the records they + // point at. + UniversalReads []*UniversalReadEntry `protobuf:"bytes,2,rep,name=universal_reads,json=universalReads,proto3" json:"universal_reads,omitempty"` } func (x *GenesisState) Reset() { @@ -909,6 +1553,57 @@ func (x *GenesisState) GetParams() *Params { return nil } +func (x *GenesisState) GetUniversalReads() []*UniversalReadEntry { + if x != nil { + return x.UniversalReads + } + return nil +} + +// UniversalReadEntry is one key-value pair from the UniversalReads map. +type UniversalReadEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value *UniversalRead `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *UniversalReadEntry) Reset() { + *x = UniversalReadEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UniversalReadEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UniversalReadEntry) ProtoMessage() {} + +// Deprecated: Use UniversalReadEntry.ProtoReflect.Descriptor instead. +func (*UniversalReadEntry) Descriptor() ([]byte, []int) { + return file_ucallback_v1_genesis_proto_rawDescGZIP(), []int{1} +} + +func (x *UniversalReadEntry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *UniversalReadEntry) GetValue() *UniversalRead { + if x != nil { + return x.Value + } + return nil +} + // Params defines the set of module parameters. type Params struct { state protoimpl.MessageState @@ -921,7 +1616,7 @@ type Params struct { func (x *Params) Reset() { *x = Params{} if protoimpl.UnsafeEnabled { - mi := &file_ucallback_v1_genesis_proto_msgTypes[1] + mi := &file_ucallback_v1_genesis_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -935,7 +1630,7 @@ func (*Params) ProtoMessage() {} // Deprecated: Use Params.ProtoReflect.Descriptor instead. func (*Params) Descriptor() ([]byte, []int) { - return file_ucallback_v1_genesis_proto_rawDescGZIP(), []int{1} + return file_ucallback_v1_genesis_proto_rawDescGZIP(), []int{2} } func (x *Params) GetSomeValue() bool { @@ -953,27 +1648,40 @@ var file_ucallback_v1_genesis_proto_rawDesc = []byte{ 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, - 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x46, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x6f, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x1d, 0x98, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x10, 0x75, - 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, - 0xb4, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, - 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, - 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, - 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, - 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, + 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x01, + 0x0a, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, + 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x12, 0x4f, 0x0a, 0x0f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x04, 0xc8, + 0xde, 0x1f, 0x00, 0x52, 0x0e, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, + 0x61, 0x64, 0x73, 0x22, 0x5f, 0x0a, 0x12, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, + 0x52, 0x65, 0x61, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x75, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x22, 0x46, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x73, 0x6f, 0x6d, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x1d, 0x98, + 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x8a, 0xe7, 0xb0, 0x2a, 0x10, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0xb4, 0x01, 0x0a, + 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, + 0x31, 0x42, 0x0c, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, + 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -988,18 +1696,22 @@ func file_ucallback_v1_genesis_proto_rawDescGZIP() []byte { return file_ucallback_v1_genesis_proto_rawDescData } -var file_ucallback_v1_genesis_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_genesis_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_ucallback_v1_genesis_proto_goTypes = []interface{}{ - (*GenesisState)(nil), // 0: ucallback.v1.GenesisState - (*Params)(nil), // 1: ucallback.v1.Params + (*GenesisState)(nil), // 0: ucallback.v1.GenesisState + (*UniversalReadEntry)(nil), // 1: ucallback.v1.UniversalReadEntry + (*Params)(nil), // 2: ucallback.v1.Params + (*UniversalRead)(nil), // 3: ucallback.v1.UniversalRead } var file_ucallback_v1_genesis_proto_depIdxs = []int32{ - 1, // 0: ucallback.v1.GenesisState.params:type_name -> ucallback.v1.Params - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 2, // 0: ucallback.v1.GenesisState.params:type_name -> ucallback.v1.Params + 1, // 1: ucallback.v1.GenesisState.universal_reads:type_name -> ucallback.v1.UniversalReadEntry + 3, // 2: ucallback.v1.UniversalReadEntry.value:type_name -> ucallback.v1.UniversalRead + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_ucallback_v1_genesis_proto_init() } @@ -1007,6 +1719,7 @@ func file_ucallback_v1_genesis_proto_init() { if File_ucallback_v1_genesis_proto != nil { return } + file_ucallback_v1_types_proto_init() if !protoimpl.UnsafeEnabled { file_ucallback_v1_genesis_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GenesisState); i { @@ -1021,6 +1734,18 @@ func file_ucallback_v1_genesis_proto_init() { } } file_ucallback_v1_genesis_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UniversalReadEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_genesis_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Params); i { case 0: return &v.state @@ -1039,7 +1764,7 @@ func file_ucallback_v1_genesis_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ucallback_v1_genesis_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/ucallback/v1/genesis.proto b/proto/ucallback/v1/genesis.proto index 352700b6..c8afff27 100755 --- a/proto/ucallback/v1/genesis.proto +++ b/proto/ucallback/v1/genesis.proto @@ -3,6 +3,7 @@ package ucallback.v1; import "gogoproto/gogo.proto"; import "amino/amino.proto"; +import "ucallback/v1/types.proto"; option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; @@ -10,6 +11,20 @@ option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; message GenesisState { // Params defines all the parameters of the module. Params params = 1 [(gogoproto.nullable) = false]; + + // universal_reads are key-value pairs from the UniversalReads map. + // + // Only the canonical records are exported. PendingByExpiry and ReadsByTxHash + // are indexes derived from these, and are rebuilt during InitGenesis rather + // than exported — so they cannot be imported out of sync with the records they + // point at. + repeated UniversalReadEntry universal_reads = 2 [(gogoproto.nullable) = false]; +} + +// UniversalReadEntry is one key-value pair from the UniversalReads map. +message UniversalReadEntry { + string key = 1; + UniversalRead value = 2 [(gogoproto.nullable) = false]; } // Params defines the set of module parameters. diff --git a/x/ucallback/types/genesis.pb.go b/x/ucallback/types/genesis.pb.go index dff37178..af640b34 100644 --- a/x/ucallback/types/genesis.pb.go +++ b/x/ucallback/types/genesis.pb.go @@ -28,6 +28,13 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type GenesisState struct { // Params defines all the parameters of the module. Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` + // universal_reads are key-value pairs from the UniversalReads map. + // + // Only the canonical records are exported. PendingByExpiry and ReadsByTxHash + // are indexes derived from these, and are rebuilt during InitGenesis rather + // than exported — so they cannot be imported out of sync with the records they + // point at. + UniversalReads []UniversalReadEntry `protobuf:"bytes,2,rep,name=universal_reads,json=universalReads,proto3" json:"universal_reads"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -70,6 +77,66 @@ func (m *GenesisState) GetParams() Params { return Params{} } +func (m *GenesisState) GetUniversalReads() []UniversalReadEntry { + if m != nil { + return m.UniversalReads + } + return nil +} + +// UniversalReadEntry is one key-value pair from the UniversalReads map. +type UniversalReadEntry struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value UniversalRead `protobuf:"bytes,2,opt,name=value,proto3" json:"value"` +} + +func (m *UniversalReadEntry) Reset() { *m = UniversalReadEntry{} } +func (m *UniversalReadEntry) String() string { return proto.CompactTextString(m) } +func (*UniversalReadEntry) ProtoMessage() {} +func (*UniversalReadEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_13c1287495624272, []int{1} +} +func (m *UniversalReadEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UniversalReadEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UniversalReadEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UniversalReadEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_UniversalReadEntry.Merge(m, src) +} +func (m *UniversalReadEntry) XXX_Size() int { + return m.Size() +} +func (m *UniversalReadEntry) XXX_DiscardUnknown() { + xxx_messageInfo_UniversalReadEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_UniversalReadEntry proto.InternalMessageInfo + +func (m *UniversalReadEntry) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +func (m *UniversalReadEntry) GetValue() UniversalRead { + if m != nil { + return m.Value + } + return UniversalRead{} +} + // Params defines the set of module parameters. type Params struct { SomeValue bool `protobuf:"varint,2,opt,name=some_value,json=someValue,proto3" json:"some_value,omitempty"` @@ -78,7 +145,7 @@ type Params struct { func (m *Params) Reset() { *m = Params{} } func (*Params) ProtoMessage() {} func (*Params) Descriptor() ([]byte, []int) { - return fileDescriptor_13c1287495624272, []int{1} + return fileDescriptor_13c1287495624272, []int{2} } func (m *Params) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -116,30 +183,36 @@ func (m *Params) GetSomeValue() bool { func init() { proto.RegisterType((*GenesisState)(nil), "ucallback.v1.GenesisState") + proto.RegisterType((*UniversalReadEntry)(nil), "ucallback.v1.UniversalReadEntry") proto.RegisterType((*Params)(nil), "ucallback.v1.Params") } func init() { proto.RegisterFile("ucallback/v1/genesis.proto", fileDescriptor_13c1287495624272) } var fileDescriptor_13c1287495624272 = []byte{ - // 257 bytes of a gzipped FileDescriptorProto + // 346 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2a, 0x4d, 0x4e, 0xcc, 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x81, 0xcb, 0xe9, 0x95, 0x19, 0x4a, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf4, 0x41, 0x2c, 0x88, 0x1a, 0x29, 0xc1, 0xc4, 0xdc, 0xcc, 0xbc, - 0x7c, 0x7d, 0x30, 0x09, 0x11, 0x52, 0x72, 0xe2, 0xe2, 0x71, 0x87, 0x98, 0x13, 0x5c, 0x92, 0x58, - 0x92, 0x2a, 0x64, 0xc4, 0xc5, 0x56, 0x90, 0x58, 0x94, 0x98, 0x5b, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, - 0xc1, 0x6d, 0x24, 0xa2, 0x87, 0x6c, 0xae, 0x5e, 0x00, 0x58, 0xce, 0x89, 0xe5, 0xc4, 0x3d, 0x79, - 0x86, 0x20, 0xa8, 0x4a, 0x25, 0x37, 0x2e, 0x36, 0x88, 0xb8, 0x90, 0x2c, 0x17, 0x57, 0x71, 0x7e, - 0x6e, 0x6a, 0x7c, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x27, - 0x48, 0x24, 0x0c, 0x24, 0x60, 0x25, 0x3b, 0x63, 0x81, 0x3c, 0xc3, 0x8b, 0x05, 0xf2, 0x8c, 0x5d, - 0xcf, 0x37, 0x68, 0x09, 0x20, 0x3c, 0x03, 0x31, 0xc7, 0x29, 0xe0, 0xc4, 0x23, 0x39, 0xc6, 0x0b, - 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, - 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xcc, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, - 0xf5, 0x0b, 0x4a, 0x8b, 0x33, 0x92, 0x33, 0x12, 0x33, 0xf3, 0xc0, 0x2c, 0x5d, 0x30, 0x53, 0x37, - 0x2f, 0x3f, 0x25, 0x55, 0xbf, 0x42, 0x1f, 0x61, 0x64, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, - 0xd8, 0x93, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd8, 0xe9, 0x3c, 0x8c, 0x39, 0x01, 0x00, - 0x00, + 0x7c, 0x7d, 0x30, 0x09, 0x15, 0x92, 0x40, 0x31, 0xb2, 0xa4, 0xb2, 0x20, 0x15, 0x6a, 0xa0, 0xd2, + 0x64, 0x46, 0x2e, 0x1e, 0x77, 0x88, 0x15, 0xc1, 0x25, 0x89, 0x25, 0xa9, 0x42, 0x46, 0x5c, 0x6c, + 0x05, 0x89, 0x45, 0x89, 0xb9, 0xc5, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x22, 0x7a, 0xc8, + 0x56, 0xea, 0x05, 0x80, 0xe5, 0x9c, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xaa, 0x14, 0xf2, + 0xe7, 0xe2, 0x2f, 0xcd, 0xcb, 0x2c, 0x4b, 0x2d, 0x2a, 0x4e, 0xcc, 0x89, 0x2f, 0x4a, 0x4d, 0x4c, + 0x29, 0x96, 0x60, 0x52, 0x60, 0xd6, 0xe0, 0x36, 0x52, 0x40, 0xd5, 0x1c, 0x0a, 0x53, 0x14, 0x94, + 0x9a, 0x98, 0xe2, 0x9a, 0x57, 0x52, 0x54, 0x09, 0x35, 0x88, 0xaf, 0x14, 0x59, 0xa6, 0x58, 0x29, + 0x9e, 0x4b, 0x08, 0x53, 0xad, 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd8, 0x5d, 0x9c, 0x41, + 0x20, 0xa6, 0x90, 0x39, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x13, 0xd8, 0xad, 0xd2, + 0x78, 0xac, 0x83, 0xda, 0x04, 0x51, 0xaf, 0xe4, 0xc6, 0xc5, 0x06, 0xf1, 0x89, 0x90, 0x2c, 0x17, + 0x57, 0x71, 0x7e, 0x6e, 0x6a, 0x3c, 0xc2, 0x1c, 0x8e, 0x20, 0x4e, 0x90, 0x48, 0x18, 0x48, 0xc0, + 0x4a, 0x76, 0xc6, 0x02, 0x79, 0x86, 0x17, 0x0b, 0xe4, 0x19, 0xbb, 0x9e, 0x6f, 0xd0, 0x12, 0x40, + 0x04, 0x23, 0xc4, 0xe7, 0x4e, 0x01, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, + 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, + 0x65, 0x96, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x5f, 0x50, 0x5a, 0x9c, + 0x91, 0x9c, 0x91, 0x98, 0x99, 0x07, 0x66, 0xe9, 0x82, 0x99, 0xba, 0x79, 0xf9, 0x29, 0xa9, 0xfa, + 0x15, 0xfa, 0x08, 0x23, 0xc1, 0xd1, 0x92, 0xc4, 0x06, 0x8e, 0x17, 0x63, 0x40, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x39, 0x04, 0xe2, 0xe1, 0x06, 0x02, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -186,6 +259,20 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.UniversalReads) > 0 { + for iNdEx := len(m.UniversalReads) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UniversalReads[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } { size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -199,6 +286,46 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *UniversalReadEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UniversalReadEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UniversalReadEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Value.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *Params) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -251,6 +378,27 @@ func (m *GenesisState) Size() (n int) { _ = l l = m.Params.Size() n += 1 + l + sovGenesis(uint64(l)) + if len(m.UniversalReads) > 0 { + for _, e := range m.UniversalReads { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func (m *UniversalReadEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Key) + if l > 0 { + n += 1 + l + sovGenesis(uint64(l)) + } + l = m.Value.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -334,6 +482,155 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UniversalReads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UniversalReads = append(m.UniversalReads, UniversalReadEntry{}) + if err := m.UniversalReads[len(m.UniversalReads)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UniversalReadEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UniversalReadEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UniversalReadEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Key = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Value.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) From 8c7c2b28fbb3b16f0918683e0e4027d412fa7689 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:47:39 +0530 Subject: [PATCH 29/54] feat(ucallback): add universal read record store --- x/ucallback/keeper/keeper.go | 10 +++++ x/ucallback/keeper/universal_read.go | 41 +++++++++++++++++++++ x/ucallback/keeper/universal_read_test.go | 45 +++++++++++++++++++++++ x/ucallback/types/keys.go | 4 ++ 4 files changed, 100 insertions(+) create mode 100644 x/ucallback/keeper/universal_read.go create mode 100644 x/ucallback/keeper/universal_read_test.go diff --git a/x/ucallback/keeper/keeper.go b/x/ucallback/keeper/keeper.go index 7062f0f4..643067b0 100755 --- a/x/ucallback/keeper/keeper.go +++ b/x/ucallback/keeper/keeper.go @@ -22,6 +22,11 @@ type Keeper struct { Schema collections.Schema Params collections.Item[types.Params] + // UniversalReads is the canonical record for every read request, keyed by + // requestId. Indexes over it are added alongside the lookups they serve, and + // are always derived — never a source of truth. + UniversalReads collections.Map[string, types.UniversalRead] + authority string } @@ -46,6 +51,11 @@ func NewKeeper( Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), + UniversalReads: collections.NewMap( + sb, types.UniversalReadsKey, "universal_reads", + collections.StringKey, codec.CollValue[types.UniversalRead](cdc), + ), + authority: authority, } diff --git a/x/ucallback/keeper/universal_read.go b/x/ucallback/keeper/universal_read.go new file mode 100644 index 00000000..c27b1697 --- /dev/null +++ b/x/ucallback/keeper/universal_read.go @@ -0,0 +1,41 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +// SetUniversalRead writes a read record. +// +// This is the only sanctioned way to mutate a UniversalRead. Indexes derived from +// the record are reconciled here, so writing k.UniversalReads directly will leave +// them stale. +func (k Keeper) SetUniversalRead(ctx context.Context, ur types.UniversalRead) error { + if ur.Id == "" { + return fmt.Errorf("universal read has empty request id") + } + + return k.UniversalReads.Set(ctx, ur.Id, ur) +} + +// GetUniversalRead returns the read for requestId, if it exists. +func (k Keeper) GetUniversalRead(ctx context.Context, requestID string) (types.UniversalRead, bool) { + return k.getUniversalReadRaw(ctx, requestID) +} + +func (k Keeper) getUniversalReadRaw(ctx context.Context, requestID string) (types.UniversalRead, bool) { + ur, err := k.UniversalReads.Get(ctx, requestID) + if err != nil { + return types.UniversalRead{}, false + } + return ur, true +} + +// HasUniversalRead reports whether a read already exists. Ingest uses this to +// stay idempotent when the same log is seen twice. +func (k Keeper) HasUniversalRead(ctx context.Context, requestID string) bool { + has, err := k.UniversalReads.Has(ctx, requestID) + return err == nil && has +} diff --git a/x/ucallback/keeper/universal_read_test.go b/x/ucallback/keeper/universal_read_test.go new file mode 100644 index 00000000..95f23853 --- /dev/null +++ b/x/ucallback/keeper/universal_read_test.go @@ -0,0 +1,45 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +func newRead(id, txHash string, expiry uint64, status types.UniversalReadStatus) types.UniversalRead { + return types.UniversalRead{ + Id: id, + Status: status, + Request: &types.ReadRequest{ + RequestId: id, + DestinationChain: "eip155:1", + ExpiryBlockHeight: expiry, + RequestedTxHash: txHash, + }, + } +} + +func TestSetUniversalRead_RoundTrips(t *testing.T) { + f := SetupTest(t) + + require.False(t, f.k.HasUniversalRead(f.ctx, "0xaaa")) + + ur := newRead("0xaaa", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + require.True(t, f.k.HasUniversalRead(f.ctx, "0xaaa")) + got, found := f.k.GetUniversalRead(f.ctx, "0xaaa") + require.True(t, found) + require.Equal(t, "0xaaa", got.Id) + require.Equal(t, "eip155:1", got.Request.DestinationChain) + + _, found = f.k.GetUniversalRead(f.ctx, "0xmissing") + require.False(t, found) +} + +func TestSetUniversalRead_RejectsEmptyID(t *testing.T) { + f := SetupTest(t) + require.Error(t, f.k.SetUniversalRead(f.ctx, types.UniversalRead{})) +} diff --git a/x/ucallback/types/keys.go b/x/ucallback/types/keys.go index 001e881e..a14ff25a 100755 --- a/x/ucallback/types/keys.go +++ b/x/ucallback/types/keys.go @@ -7,6 +7,10 @@ import ( var ( // ParamsKey saves the current module params. ParamsKey = collections.NewPrefix(0) + + // UniversalReadsKey is the canonical record for every read request, + // keyed by requestId. Everything else in this module is an index over it. + UniversalReadsKey = collections.NewPrefix(1) ) const ( From 1f9918e2978722eb67b3bbef14e226b69f519a2b Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:48:45 +0530 Subject: [PATCH 30/54] feat(ucallback): index unsettled reads by expiry height --- x/ucallback/keeper/keeper.go | 9 +++ x/ucallback/keeper/universal_read.go | 68 ++++++++++++++++++++++- x/ucallback/keeper/universal_read_test.go | 42 ++++++++++++++ x/ucallback/types/keys.go | 5 ++ 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/x/ucallback/keeper/keeper.go b/x/ucallback/keeper/keeper.go index 643067b0..bfd3cc48 100755 --- a/x/ucallback/keeper/keeper.go +++ b/x/ucallback/keeper/keeper.go @@ -27,6 +27,11 @@ type Keeper struct { // are always derived — never a source of truth. UniversalReads collections.Map[string, types.UniversalRead] + // PendingByExpiry holds (expiryHeight, requestId) for reads that have not + // settled — the module's in-flight set. Ordered composite key so the sweeper + // can range-scan by height. + PendingByExpiry collections.KeySet[collections.Pair[uint64, string]] + authority string } @@ -55,6 +60,10 @@ func NewKeeper( sb, types.UniversalReadsKey, "universal_reads", collections.StringKey, codec.CollValue[types.UniversalRead](cdc), ), + PendingByExpiry: collections.NewKeySet( + sb, types.PendingByExpiryKey, "pending_by_expiry", + collections.PairKeyCodec(collections.Uint64Key, collections.StringKey), + ), authority: authority, } diff --git a/x/ucallback/keeper/universal_read.go b/x/ucallback/keeper/universal_read.go index c27b1697..dc7ec786 100644 --- a/x/ucallback/keeper/universal_read.go +++ b/x/ucallback/keeper/universal_read.go @@ -4,20 +4,52 @@ import ( "context" "fmt" + "cosmossdk.io/collections" + "github.com/pushchain/push-chain-node/x/ucallback/types" ) +// isSettled reports whether a read has reached a terminal state and should no +// longer be swept for expiry. +func isSettled(s types.UniversalReadStatus) bool { + switch s { + case types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED, + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_EXPIRED, + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FAILED: + return true + default: + return false + } +} + // SetUniversalRead writes a read record. // // This is the only sanctioned way to mutate a UniversalRead. Indexes derived from // the record are reconciled here, so writing k.UniversalReads directly will leave -// them stale. +// them stale — in particular the sweeper would keep expiring a read that has +// already settled. func (k Keeper) SetUniversalRead(ctx context.Context, ur types.UniversalRead) error { if ur.Id == "" { return fmt.Errorf("universal read has empty request id") } - return k.UniversalReads.Set(ctx, ur.Id, ur) + if err := k.UniversalReads.Set(ctx, ur.Id, ur); err != nil { + return err + } + + // pending-by-expiry: present only while unsettled + if ur.Request != nil { + key := collections.Join(ur.Request.ExpiryBlockHeight, ur.Id) + if isSettled(ur.Status) { + if err := k.PendingByExpiry.Remove(ctx, key); err != nil { + return err + } + } else if err := k.PendingByExpiry.Set(ctx, key); err != nil { + return err + } + } + + return nil } // GetUniversalRead returns the read for requestId, if it exists. @@ -39,3 +71,35 @@ func (k Keeper) HasUniversalRead(ctx context.Context, requestID string) bool { has, err := k.UniversalReads.Has(ctx, requestID) return err == nil && has } + +// IterateExpiredBy calls fn for every unsettled read whose expiry height is at or +// below height, in ascending height order. The sweeper drives this. +// +// The key codec orders by expiryHeight first, so a plain ascending walk reaches +// every due entry before any that is not yet due — we break at the first key past +// height rather than constructing a cross-prefix range. +func (k Keeper) IterateExpiredBy(ctx context.Context, height uint64, fn func(types.UniversalRead) bool) error { + iter, err := k.PendingByExpiry.Iterate(ctx, nil) + if err != nil { + return err + } + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + return err + } + if key.K1() > height { + break + } + ur, found := k.getUniversalReadRaw(ctx, key.K2()) + if !found { + continue + } + if !fn(ur) { + return nil + } + } + return nil +} diff --git a/x/ucallback/keeper/universal_read_test.go b/x/ucallback/keeper/universal_read_test.go index 95f23853..bb3e6efa 100644 --- a/x/ucallback/keeper/universal_read_test.go +++ b/x/ucallback/keeper/universal_read_test.go @@ -43,3 +43,45 @@ func TestSetUniversalRead_RejectsEmptyID(t *testing.T) { f := SetupTest(t) require.Error(t, f.k.SetUniversalRead(f.ctx, types.UniversalRead{})) } + +func collectDueBy(t *testing.T, f *testFixture, height uint64) []string { + t.Helper() + var got []string + err := f.k.IterateExpiredBy(f.ctx, height, func(ur types.UniversalRead) bool { + got = append(got, ur.Id) + return true + }) + require.NoError(t, err) + return got +} + +// The sweep is bounded by height and ordered ascending. +func TestIterateExpiredBy_RespectsHeight(t *testing.T) { + f := SetupTest(t) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xlow", "0xTX", 50, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xmid", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xhigh", "0xTX", 150, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + + require.Equal(t, []string{"0xlow"}, collectDueBy(t, f, 50)) + require.Equal(t, []string{"0xlow", "0xmid"}, collectDueBy(t, f, 100), "ascending by expiry height") + require.Equal(t, []string{"0xlow", "0xmid", "0xhigh"}, collectDueBy(t, f, 999)) +} + +// Settling a read removes it from the in-flight set; the record itself remains. +func TestSetUniversalRead_SettledLeavesInFlightSet(t *testing.T) { + f := SetupTest(t) + + ur := newRead("0xaaa", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + require.Equal(t, []string{"0xaaa"}, collectDueBy(t, f, 100)) + + ur.Status = types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + require.Empty(t, collectDueBy(t, f, 999), "settled reads are not swept") + require.True(t, f.k.HasUniversalRead(f.ctx, "0xaaa"), "the record survives") +} diff --git a/x/ucallback/types/keys.go b/x/ucallback/types/keys.go index a14ff25a..0484c19f 100755 --- a/x/ucallback/types/keys.go +++ b/x/ucallback/types/keys.go @@ -11,6 +11,11 @@ var ( // UniversalReadsKey is the canonical record for every read request, // keyed by requestId. Everything else in this module is an index over it. UniversalReadsKey = collections.NewPrefix(1) + + // PendingByExpiryKey indexes unsettled reads by the Push Chain height they + // expire at. Key is (expiryHeight, requestId). Entries are removed the moment + // a read settles, which makes this the module's set of in-flight work. + PendingByExpiryKey = collections.NewPrefix(2) ) const ( From 286c1e2ab3fafdce726752f1cd70f0235e2d60eb Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:49:50 +0530 Subject: [PATCH 31/54] feat(ucallback): index reads by requesting tx hash --- x/ucallback/keeper/keeper.go | 8 ++++ x/ucallback/keeper/universal_read.go | 35 +++++++++++++++++ x/ucallback/keeper/universal_read_test.go | 47 +++++++++++++++++++++++ x/ucallback/types/keys.go | 6 +++ 4 files changed, 96 insertions(+) diff --git a/x/ucallback/keeper/keeper.go b/x/ucallback/keeper/keeper.go index bfd3cc48..9c4db40b 100755 --- a/x/ucallback/keeper/keeper.go +++ b/x/ucallback/keeper/keeper.go @@ -32,6 +32,10 @@ type Keeper struct { // can range-scan by height. PendingByExpiry collections.KeySet[collections.Pair[uint64, string]] + // ReadsByTxHash holds (pushTxHash, requestId) so every read emitted by one + // Push transaction can be listed together. + ReadsByTxHash collections.KeySet[collections.Pair[string, string]] + authority string } @@ -64,6 +68,10 @@ func NewKeeper( sb, types.PendingByExpiryKey, "pending_by_expiry", collections.PairKeyCodec(collections.Uint64Key, collections.StringKey), ), + ReadsByTxHash: collections.NewKeySet( + sb, types.ReadsByTxHashKey, "reads_by_tx_hash", + collections.PairKeyCodec(collections.StringKey, collections.StringKey), + ), authority: authority, } diff --git a/x/ucallback/keeper/universal_read.go b/x/ucallback/keeper/universal_read.go index dc7ec786..e6009f1b 100644 --- a/x/ucallback/keeper/universal_read.go +++ b/x/ucallback/keeper/universal_read.go @@ -47,6 +47,14 @@ func (k Keeper) SetUniversalRead(ctx context.Context, ur types.UniversalRead) er } else if err := k.PendingByExpiry.Set(ctx, key); err != nil { return err } + + // reads-by-tx: written once, never removed — it is provenance, not state + if ur.Request.RequestedTxHash != "" { + if err := k.ReadsByTxHash.Set(ctx, + collections.Join(ur.Request.RequestedTxHash, ur.Id)); err != nil { + return err + } + } } return nil @@ -103,3 +111,30 @@ func (k Keeper) IterateExpiredBy(ctx context.Context, height uint64, fn func(typ } return nil } + +// IterateReadsByTxHash calls fn for every read requested by the given Push tx. +// A single transaction can emit several ReadRequested logs; each is its own +// record, and this is how the batch is reassembled. +func (k Keeper) IterateReadsByTxHash(ctx context.Context, txHash string, fn func(types.UniversalRead) bool) error { + rng := collections.NewPrefixedPairRange[string, string](txHash) + iter, err := k.ReadsByTxHash.Iterate(ctx, rng) + if err != nil { + return err + } + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + key, err := iter.Key() + if err != nil { + return err + } + ur, found := k.getUniversalReadRaw(ctx, key.K2()) + if !found { + continue + } + if !fn(ur) { + return nil + } + } + return nil +} diff --git a/x/ucallback/keeper/universal_read_test.go b/x/ucallback/keeper/universal_read_test.go index bb3e6efa..b189c68f 100644 --- a/x/ucallback/keeper/universal_read_test.go +++ b/x/ucallback/keeper/universal_read_test.go @@ -85,3 +85,50 @@ func TestSetUniversalRead_SettledLeavesInFlightSet(t *testing.T) { require.Empty(t, collectDueBy(t, f, 999), "settled reads are not swept") require.True(t, f.k.HasUniversalRead(f.ctx, "0xaaa"), "the record survives") } + +func collectByTx(t *testing.T, f *testFixture, txHash string) []string { + t.Helper() + var got []string + err := f.k.IterateReadsByTxHash(f.ctx, txHash, func(ur types.UniversalRead) bool { + got = append(got, ur.Id) + return true + }) + require.NoError(t, err) + return got +} + +// One Push tx emitting several ReadRequested logs produces several independent +// records that are still reassemblable as a batch. +func TestSetUniversalRead_BatchedRequestsShareTxHash(t *testing.T) { + f := SetupTest(t) + + for _, id := range []string{"0xaaa", "0xbbb", "0xccc"} { + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead(id, "0xBATCH", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + } + // a read from a different tx must not leak into the batch + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xddd", "0xOTHER", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + + require.ElementsMatch(t, []string{"0xaaa", "0xbbb", "0xccc"}, collectByTx(t, f, "0xBATCH")) + require.Equal(t, []string{"0xddd"}, collectByTx(t, f, "0xOTHER")) +} + +// Siblings from one batch settle independently — one FULFILLED, one still pending. +func TestSetUniversalRead_BatchSiblingsSettleIndependently(t *testing.T) { + f := SetupTest(t) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xaaa", "0xBATCH", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xbbb", "0xBATCH", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + + // settle only one of them + settled := newRead("0xaaa", "0xBATCH", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED) + require.NoError(t, f.k.SetUniversalRead(f.ctx, settled)) + + // the settled one drops out of the expiry sweep, its sibling does not + require.Equal(t, []string{"0xbbb"}, collectDueBy(t, f, 100)) + // but both remain listed under the batch — reads-by-tx is provenance, not state + require.ElementsMatch(t, []string{"0xaaa", "0xbbb"}, collectByTx(t, f, "0xBATCH")) +} diff --git a/x/ucallback/types/keys.go b/x/ucallback/types/keys.go index 0484c19f..fc23ba7c 100755 --- a/x/ucallback/types/keys.go +++ b/x/ucallback/types/keys.go @@ -16,6 +16,12 @@ var ( // expire at. Key is (expiryHeight, requestId). Entries are removed the moment // a read settles, which makes this the module's set of in-flight work. PendingByExpiryKey = collections.NewPrefix(2) + + // ReadsByTxHashKey indexes reads by the Push Chain tx that requested them. + // One transaction can emit several ReadRequested logs; each becomes its own + // UniversalRead, and this index is what reassembles the batch. + // Key is (pushTxHash, requestId). + ReadsByTxHashKey = collections.NewPrefix(3) ) const ( From 9d93609101d6ef7dc81b74dd73ea28acf6fa9f05 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:50:55 +0530 Subject: [PATCH 32/54] feat(ucallback): resolve ballot key by scanning in-flight reads --- x/ucallback/keeper/universal_read.go | 35 +++++++++++++ x/ucallback/keeper/universal_read_test.go | 63 +++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/x/ucallback/keeper/universal_read.go b/x/ucallback/keeper/universal_read.go index e6009f1b..bfc127bf 100644 --- a/x/ucallback/keeper/universal_read.go +++ b/x/ucallback/keeper/universal_read.go @@ -112,6 +112,41 @@ func (k Keeper) IterateExpiredBy(ctx context.Context, height uint64, fn func(typ return nil } +// GetUniversalReadByBallot resolves a ballot key to its read. AfterBallotTerminal +// hands us only a ballot ID, and ballot IDs are one-way digests over the +// observation — not reversible — so this scans rather than indexes. +// +// The scan is over PendingByExpiry, not UniversalReads: entries leave that set the +// moment a read settles, so it holds only in-flight work. This mirrors uexecutor's +// ballot hook, which walks PendingInbounds for the same reason +// (x/uexecutor/keeper/ballot_hooks.go:86) — the pending set is small and transient, +// and this path only runs on terminal transitions. +// +// Returns false if no pending read owns the ballot: it may have already settled by +// another path, or the ballot may not belong to this module at all. +func (k Keeper) GetUniversalReadByBallot(ctx context.Context, ballotKey string) (types.UniversalRead, bool) { + if ballotKey == "" { + return types.UniversalRead{}, false + } + + var ( + found types.UniversalRead + ok bool + ) + err := k.PendingByExpiry.Walk(ctx, nil, func(key collections.Pair[uint64, string]) (bool, error) { + ur, exists := k.getUniversalReadRaw(ctx, key.K2()) + if exists && ur.BallotKey == ballotKey { + found, ok = ur, true + return true, nil + } + return false, nil + }) + if err != nil { + return types.UniversalRead{}, false + } + return found, ok +} + // IterateReadsByTxHash calls fn for every read requested by the given Push tx. // A single transaction can emit several ReadRequested logs; each is its own // record, and this is how the batch is reassembled. diff --git a/x/ucallback/keeper/universal_read_test.go b/x/ucallback/keeper/universal_read_test.go index b189c68f..2c73bfa5 100644 --- a/x/ucallback/keeper/universal_read_test.go +++ b/x/ucallback/keeper/universal_read_test.go @@ -132,3 +132,66 @@ func TestSetUniversalRead_BatchSiblingsSettleIndependently(t *testing.T) { // but both remain listed under the batch — reads-by-tx is provenance, not state require.ElementsMatch(t, []string{"0xaaa", "0xbbb"}, collectByTx(t, f, "0xBATCH")) } + +// Repointing a read's ballot key must not leave the old key resolvable. +func TestGetUniversalReadByBallot_Repointed(t *testing.T) { + f := SetupTest(t) + + ur := newRead("0xaaa", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + ur.BallotKey = "ballot-old" + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + ur.BallotKey = "ballot-new" + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + _, found := f.k.GetUniversalReadByBallot(f.ctx, "ballot-old") + require.False(t, found, "the old ballot key must no longer resolve") + + byNew, found := f.k.GetUniversalReadByBallot(f.ctx, "ballot-new") + require.True(t, found) + require.Equal(t, "0xaaa", byNew.Id) +} + +// The lookup scans the in-flight set, so a settled read is deliberately NOT +// findable by ballot. The ballot terminal hook must treat "not found" as +// "already handled", exactly as uexecutor's hook does. +func TestGetUniversalReadByBallot_SettledReadIsNotFound(t *testing.T) { + f := SetupTest(t) + + ur := newRead("0xaaa", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + ur.BallotKey = "ballot-1" + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + _, found := f.k.GetUniversalReadByBallot(f.ctx, "ballot-1") + require.True(t, found, "resolvable while in flight") + + ur.Status = types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + _, found = f.k.GetUniversalReadByBallot(f.ctx, "ballot-1") + require.False(t, found, "settled reads leave the in-flight set") + + // the record itself is untouched — only the index dropped it + got, ok := f.k.GetUniversalRead(f.ctx, "0xaaa") + require.True(t, ok) + require.Equal(t, "ballot-1", got.BallotKey) +} + +// Only the read owning the ballot is returned, never a sibling sharing the scan. +func TestGetUniversalReadByBallot_PicksTheRightRead(t *testing.T) { + f := SetupTest(t) + + for i, id := range []string{"0xaaa", "0xbbb", "0xccc"} { + ur := newRead(id, "0xBATCH", uint64(100+i), + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + ur.BallotKey = "ballot-" + id + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + } + + got, found := f.k.GetUniversalReadByBallot(f.ctx, "ballot-0xbbb") + require.True(t, found) + require.Equal(t, "0xbbb", got.Id) + + _, found = f.k.GetUniversalReadByBallot(f.ctx, "ballot-unknown") + require.False(t, found) +} From 359ee91db7bc7c5511526a021102166da4b13ac2 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:51:45 +0530 Subject: [PATCH 33/54] feat(ucallback): import and export universal reads --- x/ucallback/keeper/genesis.go | 28 ++++++++++++++++++-- x/ucallback/keeper/universal_read_test.go | 31 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/x/ucallback/keeper/genesis.go b/x/ucallback/keeper/genesis.go index e2f1b519..fd9b5f3a 100644 --- a/x/ucallback/keeper/genesis.go +++ b/x/ucallback/keeper/genesis.go @@ -7,12 +7,27 @@ import ( ) // InitGenesis initializes the module's state from a genesis state. +// +// Only UniversalReads is imported. The PendingByExpiry and ReadsByTxHash indexes +// are rebuilt here from the records themselves, via SetUniversalRead — importing +// them separately would allow a genesis file to carry indexes that disagree with +// the records they point at. func (k *Keeper) InitGenesis(ctx context.Context, data *types.GenesisState) error { if err := data.Params.Validate(); err != nil { return err } - return k.Params.Set(ctx, data.Params) + if err := k.Params.Set(ctx, data.Params); err != nil { + return err + } + + for _, entry := range data.UniversalReads { + if err := k.SetUniversalRead(ctx, entry.Value); err != nil { + return err + } + } + + return nil } // ExportGenesis exports the module's state to a genesis state. @@ -22,7 +37,16 @@ func (k *Keeper) ExportGenesis(ctx context.Context) *types.GenesisState { panic(err) } + reads := []types.UniversalReadEntry{} + if err := k.UniversalReads.Walk(ctx, nil, func(key string, value types.UniversalRead) (bool, error) { + reads = append(reads, types.UniversalReadEntry{Key: key, Value: value}) + return false, nil + }); err != nil { + panic(err) + } + return &types.GenesisState{ - Params: params, + Params: params, + UniversalReads: reads, } } diff --git a/x/ucallback/keeper/universal_read_test.go b/x/ucallback/keeper/universal_read_test.go index 2c73bfa5..c502d45a 100644 --- a/x/ucallback/keeper/universal_read_test.go +++ b/x/ucallback/keeper/universal_read_test.go @@ -195,3 +195,34 @@ func TestGetUniversalReadByBallot_PicksTheRightRead(t *testing.T) { _, found = f.k.GetUniversalReadByBallot(f.ctx, "ballot-unknown") require.False(t, found) } + +// Genesis round-trips records, and rebuilds every index from them. +func TestGenesis_RoundTripRebuildsIndexes(t *testing.T) { + f := SetupTest(t) + require.NoError(t, f.k.InitGenesis(f.ctx, types.DefaultGenesis())) + + pending := newRead("0xaaa", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING) + pending.BallotKey = "ballot-1" + require.NoError(t, f.k.SetUniversalRead(f.ctx, pending)) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xbbb", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED))) + + exported := f.k.ExportGenesis(f.ctx) + require.Len(t, exported.UniversalReads, 2) + + // re-import into a clean fixture + g := SetupTest(t) + require.NoError(t, g.k.InitGenesis(g.ctx, exported)) + + _, found := g.k.GetUniversalRead(g.ctx, "0xaaa") + require.True(t, found) + + // indexes are rebuilt, not imported + byBallot, found := g.k.GetUniversalReadByBallot(g.ctx, "ballot-1") + require.True(t, found) + require.Equal(t, "0xaaa", byBallot.Id) + + require.ElementsMatch(t, []string{"0xaaa", "0xbbb"}, collectByTx(t, g, "0xTX")) + require.Equal(t, []string{"0xaaa"}, collectDueBy(t, g, 100), + "only the unsettled read is pending after re-import") +} From c2d1e02d54bdbda28e6c24da6870704410569759 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 11:52:03 +0530 Subject: [PATCH 34/54] docs(ucallback): build order and open expiry question --- UCALLBACK_IMPLEMENTATION.md | 552 ++++++++++++++++++++++++++++++++++++ UCALLBACK_MODULE_PLAN.md | 441 ++++++++++++++++++++++++++++ 2 files changed, 993 insertions(+) create mode 100644 UCALLBACK_IMPLEMENTATION.md create mode 100644 UCALLBACK_MODULE_PLAN.md diff --git a/UCALLBACK_IMPLEMENTATION.md b/UCALLBACK_IMPLEMENTATION.md new file mode 100644 index 00000000..118de76b --- /dev/null +++ b/UCALLBACK_IMPLEMENTATION.md @@ -0,0 +1,552 @@ +# `x/ucallback` — core implementation guide + +**Companion to** `UCALLBACK_MODULE_PLAN.md` (architecture, rationale, open questions). +This file is the build order. Section numbers below match the **actual commits** on +`feat/read-state`, which diverged from the original plan — scaffolding landed before protos, state was +split out of the skeleton, and the protocgen fix was unplanned: + +| # | commit | state | +|---|---|---| +| C0 | `d2e033ce` fix(proto): stop protocgen deleting compat/orm-api | done (unplanned prerequisite) | +| C1 | `b9c966f3` feat(ucallback): scaffold module | done | +| C2 | `d0ad5720` feat(ucallback): add read-state types | done | +| C3 | keeper state + indexes | **staged, in review** | +| C4 | queries | next — unblocks the UV team | +| C5–C9 | ingestion → vote → hook → sweeper → upgrade | planned | + +**Design decisions already locked** (see plan §2 for evidence): + +| decision | why | +|---|---| +| new module `x/ucallback` | clean separation from uexecutor | +| EVM calls made **as the uexecutor module account** | `UniversalCallback.sol:25` hardcodes `0x14191Ea5…` immutable; a `ucallback` module account is rejected by `onlyUEModule` | +| aggregate named `UniversalRead` | read-side sibling of `UniversalTx` | +| `error_msg` absent from the ballot proto entirely | convention-only exclusion will be forgotten once generated | +| ballot key computed over the *identical-mode subset* | v2 `MEDIAN` must be excluded or it's a consensus-breaking change later | +| ingest filters on `log.Address` **and** topic0 | we listen to one trusted contract; the filter is what makes that true | + +--- + +## OPEN — expiry semantics, to confirm with the team + +**Not resolved. Do not treat the C8 sweeper design as settled until this is answered.** + +There are **two independent expiry clocks**, and the interaction between them is unspecified: + +| | clock | set by | enforced at | +|---|---|---|---| +| **A** | `ReadSpec.expiryPushChainHeight` → our `ReadRequest.expiry_block_height` | the app, per request | `UniversalCallback.sol:121` on request, `:207` in `expireExternalRead` | +| **B** | `Ballot.block_height_expiry` | us, as arg 8 to `VoteOnBallot` | `x/uvalidator/keeper/ballot.go:344` | + +Questions, in the order they change the design: + +1. **Is late fulfilment intended?** `fulfillExternalCallback` has **no expiry check** — the only guard + is `fulfilledRequests`. A quorum reached long after `expiryHeight` still fulfils and still calls the + app's callback. So A is not a deadline on fulfilment; it is one side of a fulfil-vs-expire race. +2. **Does expiry need to be prompt at all?** `expireExternalRead` **refunds nothing**. Prompt sweeping + frees contract storage and moves our record off `PENDING` — nothing a user feels. If the answer is + "no", the sweeper does not need per-block cadence, and the case for keeping `PendingByExpiry` rests + only on "don't scan an unboundedly-growing map", not on frequency. +3. **Should B be disabled?** uexecutor passes `DefaultExpiryAfterBlocks = 100_000_000` (~19 yrs) with + *"Ballots should not expire without an escape hatch for stuck pending items."* If we copy that, A is + the only real deadline. If we don't, a ballot can die while its read is still live — leaving a record + that can neither fulfil nor expire until A fires. Two clocks on one lifecycle is how records get stuck. + +**Consequences that are parked on this**: sweeper cadence (every block vs every N) and the +inclusive/exclusive boundary at exactly `expiryHeight`. + +**No longer parked on it: `PendingByExpiry` itself.** C3 dropped the `ballotKey → requestId` index and +made the ballot terminal hook scan `PendingByExpiry` instead, so that set now has two consumers. It +stays whichever way the cadence question is answered. + +--- + +## Reference points in existing code + +Copy these, don't invent: + +``` +x/uexecutor/keeper/evm_hooks.go:21 NewEVMHooks / PostTxProcessing shape +x/uexecutor/keeper/create_outbound.go:27-42 log scan: address filter → topic filter → decode +x/uexecutor/keeper/voting.go:73-125 VoteOnOutboundBallot — the exact voting template +x/uexecutor/keeper/ballot_hooks.go:56 AfterBallotTerminal dispatch +x/uexecutor/keeper/chain_meta.go median-without-ballots (relevant only for v2) +x/uexecutor/types/types.proto:186 UniversalTx shape · :123 PCTx (reuse) +proto/uexecutor/v1/tx.proto:121 MsgVoteOutbound shape +proto/uvalidator/v1/ballot.proto:25 BallotObservationType enum +app/app.go:794 EVMKeeper.SetHooks — currently single-hook +``` + +**The ballot model, stated plainly** — this shapes everything downstream: +`Ballot.votes` is a parallel array of binary `VoteResult{SUCCESS|FAILURE}`. The **ballot ID encodes the +observation**. Distinct observations produce distinct ballots; the one that reaches quorum wins. +Validators do not vote *values*. This is why `VoteChainMeta` bypasses ballots entirely to compute gas +medians, and why v2 `MEDIAN` cannot ride the ballot path. + +--- + +# C2 — protos · DONE `d0ad5720` + +**Files** +``` +proto/ucallback/v1/types.proto +proto/ucallback/v1/genesis.proto +proto/ucallback/v1/params.proto +``` + +```protobuf +// types.proto +message UniversalRead { + option (amino.name) = "ucallback/universal_read"; + option (gogoproto.equal) = true; + + string id = 1; // requestId, 0x-hex uint256 + ReadRequest request = 2; + ReadResult result = 3; // set when the ballot finalises + repeated uexecutor.v1.PCTx pc_tx = 4; // fulfil / expire attempts — REUSE + UniversalReadStatus status = 5; + string ballot_key = 6; +} + +message ReadRequest { + string request_id = 1; + string destination_chain = 2; // CAIP-2, composed by us + bytes owner = 3; + bytes query = 4; + uint32 min_confirmations = 5; + uint64 destination_block_height = 6; + uint64 expiry_block_height = 7; + uint64 created_at_height = 8; // derived from the log's block — NOT in the event + + // core-only, never read by the UV + string callback_target = 9; + string original_funder = 10; + string fees_deposited = 11; + string max_fee = 12; + string requested_tx_hash = 13; + uint64 requested_log_index = 14; +} + +// The ballot payload. NO error_msg field — deliberately. +message ReadResult { + ReadStatus status = 1; + bytes result_data = 2; + uint64 observed_block_height = 3; + bytes observed_block_hash = 4; + // reserved for v2 MEDIAN — excluded from the ballot key when populated + repeated AggregateValue aggregates = 5; +} + +message AggregateValue { + uint32 extract_index = 1; + uint32 mode = 2; + bytes value = 3; +} + +enum ReadStatus { + READ_STATUS_UNSPECIFIED = 0; + READ_STATUS_SUCCESS = 1; + READ_STATUS_ERROR = 2; +} + +enum UniversalReadStatus { + UNIVERSAL_READ_STATUS_UNSPECIFIED = 0; + UNIVERSAL_READ_STATUS_PENDING = 1; + UNIVERSAL_READ_STATUS_VOTING = 2; + UNIVERSAL_READ_STATUS_FULFILLED = 3; + UNIVERSAL_READ_STATUS_EXPIRED = 4; + UNIVERSAL_READ_STATUS_FAILED = 5; // quorum reached, callback reverted +} +``` + +**`ReadRequest` must satisfy `universalClient/uread/types.go:9` field-for-field** — that struct exists +only to be deleted once these types generate. + +**Generate:** `make proto-gen` (Docker required — not the script directly). + +**Verify:** `go build ./...`; generated types exist; `uread.ReadRequest` maps 1:1. + +--- + +# C1 + C3 — module skeleton `b9c966f3`, keeper state (staged) + +**Files** +``` +x/ucallback/module.go AppModule, depinject +x/ucallback/keeper/keeper.go collections wiring +x/ucallback/keeper/genesis.go InitGenesis / ExportGenesis +x/ucallback/types/{keys,codec,errors,constants}.go +app/app.go module registration + maccPerms +``` + +```go +type Keeper struct { + UniversalReads collections.Map[string, types.UniversalRead] + PendingByExpiry collections.KeySet[collections.Pair[uint64, string]] // in-flight set + ReadsByTxHash collections.KeySet[collections.Pair[string, string]] // (txHash, requestId) + Params collections.Item[types.Params] + + evmKeeper types.EVMKeeper + uexecutorKeeper types.UexecutorKeeper // for module addr + DerivedEVMCall + uvalidatorKeeper types.UvalidatorKeeper // for eligible voters + VoteOnBallot +} +``` + +`ReadsByTxHash` exists because a single Push transaction can emit several `ReadRequested` logs — +a batching app, or a contract that fires more than one `_requestRead` in one call. Each becomes its +own `UniversalRead` keyed by `requestId` (they share no lifecycle: one can be FULFILLED while its +sibling EXPIRES), so this index is what reassembles the batch for `reads-by-tx`. Ordered composite +key, prefix-scanned by `txHash`. + +`PendingByExpiry` **must** be an ordered composite key so the sweeper can range-scan +`[0, currentHeight]` rather than iterating the whole set. + +> Adding `ucallback` to `maccPerms` puts its address into `BlockedAddresses()`, and since cosmos/evm +> v0.7 that list also gates `SetBalance` — so the module address can't receive native EVM value. +> That is almost certainly correct here; note it if not. + +**Verify:** chain starts, genesis round-trips, `q ucallback params` responds. + +--- + +# C4 — queries ← **ship this early, it unblocks the UV team** + +**Files** +``` +proto/ucallback/v1/query.proto +x/ucallback/keeper/grpc_query.go +x/ucallback/client/cli/query.go +``` + +```protobuf +rpc AllPendingReadRequests(QueryAllPendingReadRequestsRequest) + returns (QueryAllPendingReadRequestsResponse); // paginated +rpc GetUniversalRead(QueryGetUniversalReadRequest) + returns (QueryGetUniversalReadResponse); +rpc ReadsByTxHash(QueryReadsByTxHashRequest) + returns (QueryReadsByTxHashResponse); // all reads from one Push tx +``` + +`AllPendingReadRequests` returns `[]ReadRequest` where status is `PENDING`. Mirror +`AllPendingOutbounds` for pagination shape. + +**Why third and not last:** it deletes `ErrReadQueriesNotAvailable` in +`universalClient/pushcore/pushCore.go:382` and lets the UV team integrate against a real endpoint — +even while it returns an empty list. Their TODO names this exact query. + +`ReadsByTxHash` prefix-scans `ReadsByTxHash` and returns the full `UniversalRead` for each hit — +`q ucallback reads-by-tx `. This is how a batched request is inspected as a unit, and it is why +we can key records by `requestId` without losing the grouping. + +`GetUniversalRead` is the operator tool. Model the CLI on `q uexecutor v2 get-universal-tx`; that query +is what made a stranded production tx diagnosable in minutes this week. + +**Verify:** UV team's `GetAllPendingReadRequests` returns `[]` instead of an error. + +--- + +# C5 — ingestion + +**Files** +``` +x/ucallback/keeper/evm_hooks.go +x/ucallback/keeper/ingest.go +x/ucallback/types/event_decode.go +app/app.go ← rewire to MultiEvmHooks +``` + +```go +func (h EVMHooks) PostTxProcessing(ctx sdk.Context, sender common.Address, + msg core.Message, receipt *ethtypes.Receipt) error { + if err := h.k.IngestReadRequests(ctx, receipt); err != nil { + h.k.Logger().Error("ucallback ingest failed", "tx", receipt.TxHash, "err", err) + } + return nil // NEVER non-nil — see below +} +``` + +> 🔴 `MultiEvmHooks.PostTxProcessing` aborts the whole hook chain on the first error +> (`x/vm/keeper/hooks.go:40-42`), which **fails the EVM transaction**. A bug in our hook would break +> unrelated user txs. Log and continue; never return an error for anything short of a consensus fault. + +```go +// ingest.go — mirrors create_outbound.go:27-42 +for _, lg := range receipt.Logs { + if lg.Removed { continue } + if !strings.EqualFold(lg.Address, ucAddr) { continue } + if len(lg.Topics) == 0 { continue } + if !strings.EqualFold(lg.Topics[0], ReadRequestedSig) { continue } + + ev, err := types.DecodeReadRequestedFromLog(lg) + if err != nil { k.Logger().Error(...); continue } + + if has, _ := k.UniversalReads.Has(ctx, ev.RequestID); has { continue } // idempotent + + ur := types.UniversalRead{ + Id: ev.RequestID, + Request: &types.ReadRequest{ + RequestId: ev.RequestID, + DestinationChain: ev.ChainNamespace + ":" + ev.ChainId, + Owner: ev.Owner, + Query: ev.Query, + MinConfirmations: uint32(ev.MinConfirmations), + DestinationBlockHeight: ev.BlockNumber, + ExpiryBlockHeight: ev.ExpiryPushChainHeight, + CreatedAtHeight: uint64(ctx.BlockHeight()), + CallbackTarget: ev.CallbackTarget, + OriginalFunder: ev.OriginalFunder, + FeesDeposited: ev.FeesDeposited.String(), + MaxFee: ev.MaxFee.String(), + RequestedTxHash: receipt.TxHash.Hex(), + RequestedLogIndex: uint64(lg.Index), + }, + Status: types.UNIVERSAL_READ_STATUS_PENDING, + } + k.UniversalReads.Set(ctx, ev.RequestID, ur) + k.PendingByExpiry.Set(ctx, collections.Join(ev.ExpiryPushChainHeight, ev.RequestID)) +} +``` + +**app.go — `SetHooks` panics if called twice** (`x/vm/keeper/keeper.go:255`), and line 794 already +registers uexecutor's: + +```go +app.EVMKeeper.SetHooks(evmkeeper.NewMultiEvmHooks( + uexecutorkeeper.NewEVMHooks(app.UexecutorKeeper), + ucallbackkeeper.NewEVMHooks(app.UcallbackKeeper), +)) +``` + +**⚠️ Put `ReadRequestedSig` and the `UniversalCallback` address in chain config, not Go constants.** +uexecutor hardcodes both, and that has already failed in production: `constants.go:56` still declares +`RescueFundsOnSourceChain(...)` for an event the contract renamed to `FundsRescued`, so it silently +matches nothing. Register these like `gateway_methods` / `vault_methods`. + +**Tests** +- a log from a **non-UniversalCallback** address is ignored (regression test — this is the filter that makes "we only listen to one trusted contract" true) +- wrong topic0 ignored +- duplicate log → single record +- `created_at_height` equals the block height, not anything from the event + +--- + +# C6 — vote message and ballot + +**Files** +``` +proto/uvalidator/v1/ballot.proto + BALLOT_OBSERVATION_TYPE_READ_RESULT = 5 +proto/ucallback/v1/tx.proto MsgVoteReadResult +x/ucallback/keeper/msg_vote_read_result.go +x/ucallback/keeper/voting.go VoteOnReadBallot +x/ucallback/types/keys.go GetReadBallotKey +``` + +```protobuf +rpc VoteReadResult(MsgVoteReadResult) returns (MsgVoteReadResultResponse); + +message MsgVoteReadResult { + option (amino.name) = "ucallback/MsgVoteReadResult"; + option (cosmos.msg.v1.signer) = "signer"; + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string request_id = 2; + ReadResult result = 3; +} +``` + +```go +// keys.go — mode-aware from day one. +// v1: every field is IDENTICAL, so this equals H(all of result_data). +// v2: aggregates are EXCLUDED here and medianed separately at quorum. +func GetReadBallotKey(requestId string, r *ReadResult) (string, error) { + // hash: requestId ‖ status ‖ result_data ‖ observed_block_height ‖ observed_block_hash + // NOT hashed: aggregates, and error_msg does not exist in the proto +} +``` + +```go +// voting.go — copy x/uexecutor/keeper/voting.go:73-125 verbatim, swapping the observation type +func (k Keeper) VoteOnReadBallot(ctx, universalValidator sdk.ValAddress, + requestId string, res *types.ReadResult) (isFinalized, isNew bool, err error) { + ballotKey, err := types.GetReadBallotKey(requestId, res) + voters, _ := k.uvalidatorKeeper.GetEligibleVoters(ctx) + votesNeeded := (types.VotesThresholdNumerator*len(voters))/types.VotesThresholdDenominator + 1 + + _, isFinalized, isNew, err = k.uvalidatorKeeper.VoteOnBallot( + ctx, ballotKey, + uvalidatortypes.BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT, + universalValidator.String(), + uvalidatortypes.VoteResult_VOTE_RESULT_SUCCESS, + voterAddrStrs, int64(votesNeeded), int64(types.DefaultExpiryAfterBlocks), + ) + // ballotKey is stored on the UniversalRead itself; there is no reverse index. + // AfterBallotTerminal resolves it by scanning PendingByExpiry — see + // GetUniversalReadByBallot. + return +} +``` + +**Msg server guards:** request exists · status is `PENDING` or `VOTING` · signer is an eligible +universal validator · not already `FULFILLED`/`EXPIRED`. + +**Open decision (plan Q2):** whether to reject `READ_STATUS_ERROR` ballots that carry no revert +evidence. PR #296's EVM executor votes ERROR on *any* `CallContract` failure — including pruned nodes +and 429s — and because ERROR ballots are byte-identical by design, infra faults converge into a +confident quorum. Decide before this commit lands. + +**Tests** +- two validators, identical results → one ballot, converges +- two validators, different `result_data` → two ballots, neither converges +- non-validator signer rejected +- vote on a `FULFILLED` request rejected + +--- + +# C7 — ballot terminal hook and fulfilment + +**Files** +``` +x/ucallback/keeper/ballot_hooks.go +x/ucallback/keeper/fulfill.go +x/ucallback/types/abi.go UniversalCallback ABI +``` + +```go +func (h BallotHooks) AfterBallotTerminal(ctx, ballotKey string, + ballotType uvalidatortypes.BallotObservationType, ...) error { + switch ballotType { + case uvalidatortypes.BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT: + return h.afterReadBallotTerminal(ctx, ballotKey) + } + return nil +} +``` + +```go +// fulfill.go +func (k Keeper) FulfillRead(ctx sdk.Context, ur types.UniversalRead) error { + abi, _ := types.ParseUniversalCallbackABI() + ueModuleAcc, _ := k.uexecutorKeeper.GetUeModuleAddress(ctx) + isModuleSender, nonce, _ := k.uexecutorKeeper.ModuleSenderNonce(ctx, ueModuleAcc) + + resp, err := k.evmKeeper.DerivedEVMCall( + ctx, abi, + ueModuleAcc, // MUST be uexecutor — contract hardcodes 0x14191Ea5… + universalCallbackAddr, + big.NewInt(0), nil, // gasLimit nil — estimate, per house convention + true, /*commit*/ false, /*gasless*/ + isModuleSender, nonce, + "fulfillExternalCallback", + requestIdBig, ur.Result.ResultData, + ur.Result.ObservedBlockHeight, ur.Result.ObservedBlockHash, + ) + + ur.PcTx = append(ur.PcTx, &uexecutortypes.PCTx{ + BlockHeight: uint64(ctx.BlockHeight()), + Status: err == nil && resp != nil && resp.VmError == "", + ErrorMsg: errMsgOf(err, resp), // ← the field that saves you at 2am + }) + if success { ur.Status = FULFILLED } else { ur.Status = FAILED } + k.PendingByExpiry.Remove(ctx, collections.Join(ur.Request.ExpiryBlockHeight, ur.Id)) + k.UniversalReads.Set(ctx, ur.Id, ur) +} +``` + +> **Decision: `gasLimit` is `nil`.** Ten of the eleven existing `DerivedEVMCall` sites pass `nil` +> and let `EstimateGasInternal` size it; only `CallUEAExecutePayload` passes a value, and only +> because the user's signed payload supplies one. We follow the convention. +> +> Consequence: we never need `callbackGasLimit`, so there is no `getPendingRead` call in the fulfil +> path and no dependency on contracts emitting it in `ReadRequested`. +> +> Residual risk, recorded not mitigated: the estimator's own doc says it *"may underpredict"*, and a +> `nil` site is how `CallPRC20Deposit` produced `intrinsic gas too low` on donut this week. If a read +> ever underpredicts, that request fails terminally (F4). The fix would be passing an explicit limit +> here — localised to this one call. + +> 🔴 **Never set `FULFILLED` optimistically.** If the submit itself fails (nonce drift, gas), the +> ballot is terminal but the callback never landed — you must be able to retry until expiry. + +**Note the uexecutor module-nonce drift bug**: module-sender calls skip `ModuleAccountNonce` +increment. Fix it before adding a fourth caller, or `FulfillRead` inherits it. + +**Tests** +- quorum → `fulfillExternalCallback` called with exactly the voted values +- callback reverts → `FAILED` + `error_msg` recorded, no retry +- submit fails → status unchanged, still retryable +- gas: `gasleft()` at the call ≥ `callbackGasLimit` + +--- + +# C8 — expiry sweeper + +**Files** +``` +x/ucallback/keeper/expire.go +x/ucallback/module.go EndBlock +app/app.go SetOrderEndBlockers +``` + +```go +func (k Keeper) SweepExpired(ctx sdk.Context) error { + h := uint64(ctx.BlockHeight()) + rng := collections.NewPrefixUntilPairRange[uint64, string](h) + n := 0 + for iter, _ := k.PendingByExpiry.Iterate(ctx, rng); iter.Valid() && n < maxExpiriesPerBlock; iter.Next() { + // DerivedEVMCall → expireExternalRead(requestId), same sender rules as C7 + // record PCTx, status = EXPIRED, de-index + n++ + } +} +``` + +Bound it per block — unbounded makes a fat EndBlocker, too low and a backlog never drains. + +> **Blocked on the open expiry question at the top of this file.** Cadence, whether `PendingByExpiry` +> exists at all, and the boundary at exactly `expiryHeight` are all downstream of that answer. The +> sketch above assumes per-block; that assumption is the thing under review. + +> The contract's `expireExternalRead` **transfers nothing** (verified: zero value-transfer statements), +> so the funder's fee is trapped. That is a contracts bug, not ours — but our sweeper is what makes it +> visible, so record it clearly in the `PCTx` and surface it in `GetUniversalRead`. + +**Tests** +- request past expiry swept exactly once +- request at exactly `expiryHeight` — decide inclusive/exclusive and pin it +- fulfil/expire race: contract's `fulfilledRequests` guard means first-wins; core must swallow the + loser's revert without corrupting status + +--- + +# C9 — upgrade handler + +**Files** +``` +app/upgrades//upgrade.go +app/upgrades.go +``` + +New store key → `StoreUpgrades.Added: []string{"ucallback"}`. Everything else is a no-op +`RunMigrations`. + +**Verify with a real upgrade simulation** from the current donut release to this branch — the +established flow: start the old binary, submit `MsgSoftwareUpgrade` at a height well past the end of +the voting period, let cosmovisor swap, confirm `q upgrade applied`, then run a tx. + +> Set the proposal height generously past the **end of the voting period**, not just the submit +> height — two simulation proposals failed with `upgrade cannot be scheduled in the past` learning +> this. + +--- + +## Ordering rationale + +C1→C3 are prerequisites. **C4 (queries) is deliberately early**: it is cheap, it unblocks the UV team, +and it can ship returning an empty list. C5 (ingestion) makes records real. C6–C7 are the consensus +core and should land together in review even if committed separately. C8 (sweeper) is safe to add last +because until it exists, expired requests simply accumulate — no corruption. C9 gates deployment. + +## Cross-team dependencies + +Nothing here is blocked on contracts. But six contract defects change behaviour at the edges, and all +are cheap while `feat-read-state` is unmerged — F6 (**no status channel in +`fulfillExternalCallback`**) is the only one with no core-side workaround. Full list in the plan §10. diff --git a/UCALLBACK_MODULE_PLAN.md b/UCALLBACK_MODULE_PLAN.md new file mode 100644 index 00000000..bccad453 --- /dev/null +++ b/UCALLBACK_MODULE_PLAN.md @@ -0,0 +1,441 @@ +# `x/ucallback` — Read-from-Chains, core-side module plan + +**Status:** Draft for review · **Written:** 2026-08-04 +**Scope:** core chain only. Contracts (`push-chain-core-contracts@feat-read-state`) and universal +validators (`push-chain-node#296`) are owned by other teams; both are already written, which means +**our interface is pinned from both ends**. + +Every claim below is cited to a file:line or a live query. Open questions are collected in §9. + +--- + +## 1. What we own + +``` +┌─ contracts (done, branch) ┌─ UV (done, draft PR #296) +│ UniversalCallback.sol │ externalchains/{evm,svm,web2}/read_executor.go +│ emits ReadRequested │ pushwatcher/ → polls us +│ exposes fulfillExternalCallback │ pushcore.GetAllPendingReadRequests() ← STUB +│ expireExternalRead │ +└────────────┬───────────────────────────────┴──────────┬───────────────── + │ │ + ┌────▼──────────────────────────────────────────▼────┐ + │ x/ucallback ← US │ + │ ingest ReadRequested → serve pending → tally │ + │ ballot → call fulfill/expire → record outcome │ + └─────────────────────────────────────────────────────┘ +``` + +The UV's stub states our deliverable verbatim (`universalClient/pushcore/pushCore.go:377`): + +> `TODO(core): blocked on x/uexecutor Query/PendingReadRequests … mirror GetAllPendingOutbounds` + +Note it says `x/uexecutor`. We are choosing `x/ucallback` instead — see §2.1 for why that is fine +and §9 Q1 for the one thing it forces. + +--- + +## 2. Hard constraints discovered + +### 2.1 🔴 The EVM caller must be the **uexecutor** module account + +`UniversalCallback.sol:25` hardcodes an immutable: + +```solidity +address public immutable UNIVERSAL_EXECUTOR_MODULE = 0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7; +modifier onlyUEModule() { if (msg.sender != UNIVERSAL_EXECUTOR_MODULE) revert CallerIsNotUEModule(); } +``` + +Verified against live donut: + +``` +uexecutor module push1zsv3af2tfstklnux75dsltruk8n3ma7hnxp8ew + → hex 0x14191ea54b4c176fcf86f51b0fac7cb1e71df7d7 ← identical +``` + +A new module gets `authtypes.NewModuleAddress("ucallback")`, a **different** address. Every +`fulfillExternalCallback` would revert. + +**Decision taken:** `x/ucallback` owns state and lifecycle; the EVM call is executed *as uexecutor* +by calling into the uexecutor keeper. No contract change, no redeploy coupling. + +> Consequence: we inherit uexecutor's module-nonce path, including the known drift bug where +> module-sender calls skip `ModuleAccountNonce` increment. Fix that before adding a fourth caller. + +### 2.2 🔴 `SetHooks` panics if called twice + +`x/vm/keeper/keeper.go:254`: + +```go +func (k *Keeper) SetHooks(eh types.EvmHooks) *Keeper { + if k.hooks != nil { panic("cannot set evm hooks twice") } +``` + +and `app/app.go:794` already does `app.EVMKeeper.SetHooks(uexecutorkeeper.NewEVMHooks(...))`. + +**Solution:** `evmkeeper.NewMultiEvmHooks(uexecutorHooks, ucallbackHooks)` (`x/vm/keeper/hooks.go:27`). + +> ⚠️ `MultiEvmHooks.PostTxProcessing` aborts the whole chain of hooks on the first error +> (`hooks.go:40-42`). A bug in our hook therefore **fails unrelated EVM transactions**. Our hook +> must never return a non-nil error for anything short of a genuine consensus fault — log and +> continue instead. + +### 2.3 🟡 Known defects on the contract side we must design around + +| defect | effect on us | +|---|---| +| `expireExternalRead` transfers nothing (verified: 0 value-transfer statements) | our sweeper "expires" a request but the funder is never repaid; fees accrue in the contract | +| refund uses `call{value}` + `revert` on failure | an app without `receive()` makes `fulfillExternalCallback` revert **forever**; our submit will never succeed | +| `fulfilledRequests[requestId] = true` set *before* dispatch | whatever we submit is final; a quorum on ERROR is permanent, no retry | +| `_localContext` leaks in the app on the failure/expiry paths | not ours, but it's what users will report to us | + +These are raised with the contracts team (§10). Our design must not *depend* on them being fixed. + +--- + +## 3. Data model + +### 3.1 `UniversalRead` — the aggregate + +Named as the read-side sibling of `UniversalTx` (`proto/uexecutor/v1/types.proto:186`). Deliberately +**not** a clone: a read is triggered by a Push-chain event, performs no external write, has no +external tx hash, and produces exactly one Push-chain fulfilment. + +```protobuf +// proto/ucallback/v1/types.proto +message UniversalRead { + option (amino.name) = "ucallback/universal_read"; + + string id = 1; // requestId, 0x-hex uint256 + ReadRequest request = 2; + ReadResult result = 3; // set when the ballot finalises + repeated PCTx pc_tx = 4; // fulfil / expire attempts (reuse uexecutor's PCTx) + UniversalReadStatus status = 5; + string ballot_key = 6; +} +``` + +`pc_tx` is repeated (fulfil, then possibly expire); `request`/`result` are singular. There is no +top-level `revert_error` — failures live in `PCTx.error_msg`, which is the field that made a stuck +`UniversalTx` debuggable in production. + +### 3.2 `ReadRequest` — served verbatim to UVs + +```protobuf +message ReadRequest { + string request_id = 1; + string destination_chain = 2; // CAIP-2, composed by us + bytes owner = 3; + bytes query = 4; + uint32 min_confirmations = 5; // uint16 on the wire + uint64 destination_block_height = 6; + uint64 expiry_block_height = 7; + uint64 created_at_height = 8; // derived — NOT in the event + + // core-only, never consumed by the UV + string callback_target = 9; + string original_funder = 10; + string fees_deposited = 11; // uint256 as string + string max_fee = 12; + string requested_tx_hash = 13; // provenance / dedup / debugging + uint64 requested_log_index = 14; +} +``` + +Field-for-field this must satisfy `uread.ReadRequest` (`universalClient/uread/types.go:9`), the +temporary struct we are meant to delete. + +### 3.3 `ReadResult` — the ballot payload + +```protobuf +message ReadResult { + ReadStatus status = 1; + bytes result_data = 2; + uint64 observed_block_height = 3; + bytes observed_block_hash = 4; +} +``` + +**`error_msg` is deliberately absent from the proto**, not merely unused. In `uread` it is excluded +by a comment — *"local diagnostic only — never part of the ballot"*. Once it is a generated type +that convention will be forgotten; make it structurally impossible. If error text ever enters the +ballot key, no two validators ever agree. + +### 3.4 Enums + +```protobuf +enum ReadStatus { READ_STATUS_UNSPECIFIED = 0; READ_STATUS_SUCCESS = 1; READ_STATUS_ERROR = 2; } + +enum UniversalReadStatus { + UNIVERSAL_READ_STATUS_UNSPECIFIED = 0; + UNIVERSAL_READ_STATUS_PENDING = 1; // ingested, awaiting votes + UNIVERSAL_READ_STATUS_VOTING = 2; // ≥1 vote, no quorum + UNIVERSAL_READ_STATUS_FULFILLED = 3; // callback dispatched OK + UNIVERSAL_READ_STATUS_EXPIRED = 4; // expireExternalRead submitted + UNIVERSAL_READ_STATUS_FAILED = 5; // quorum reached, callback reverted +} +``` + +### 3.5 Contract → our fields + +| our field | from `ReadRequested` | +|---|---| +| `request_id` | `requestId` | +| `destination_chain` | `account.chainNamespace + ":" + account.chainId` | +| `owner` / `query` / `min_confirmations` | `account.owner` / `readSpec.query` / `readSpec.minConfirmations` | +| `destination_block_height` | `readSpec.blockNumber` | +| `expiry_block_height` | `readSpec.expiryPushChainHeight` | +| `callback_target` / `original_funder` / `fees_deposited` / `max_fee` | same-named event args | +| **`created_at_height`** | **not emitted** — take from the log's Push block height | + +--- + +## 4. Storage + +``` +UniversalReads : requestId → UniversalRead (collections.Map) +PendingByExpiry : (expiryHeight, requestId) → () (KeySet, in-flight set) +ReadsByTxHash : (pushTxHash, requestId) → () (KeySet, batch reassembly) +Params : module params +``` + +There is deliberately **no `ballotKey → requestId` index**. The ballot terminal hook resolves a ballot +by scanning `PendingByExpiry`, which holds only unsettled reads — the same trade uexecutor already +makes in `ballot_hooks.go:86` for the identical problem. That leaves `PendingByExpiry` with two +consumers, so it stays regardless of how the open expiry-cadence question is answered. + +`PendingByExpiry` must be an ordered composite key so the sweeper can range-scan +`[0, currentHeight]` in `EndBlocker` rather than iterating everything. + +--- + +## 5. Components + +``` +proto/ucallback/v1/{types,tx,query,genesis,params}.proto +x/ucallback/ + keeper/ + keeper.go collections wiring, uexecutor + uvalidator keeper refs + evm_hooks.go PostTxProcessing → ingest ReadRequested + ingest.go log filter + decode → UniversalRead{PENDING} + msg_vote_read_result.go MsgVoteReadResult → VoteOnReadBallot + ballot_hooks.go AfterBallotTerminal(READ_RESULT) → fulfil + fulfill.go call UniversalCallback.fulfillExternalCallback via uexecutor + expire.go EndBlocker sweeper → expireExternalRead + grpc_query.go AllPendingReadRequests, GetUniversalRead + types/ + constants.go keys.go codec.go errors.go events.go + module.go / depinject +``` + +### 5.1 Ingestion (`evm_hooks.go` + `ingest.go`) + +Mirrors `x/uexecutor/keeper/create_outbound.go:27-42`: + +```go +for _, lg := range receipt.Logs { + if lg.Removed { continue } + if !strings.EqualFold(lg.Address, universalCallbackAddr) { continue } // ← MANDATORY + if len(lg.Topics) == 0 || !strings.EqualFold(lg.Topics[0], ReadRequestedEventSig) { continue } + ... +} +``` + +**The address filter is a security control, not a nicety.** Matching on topic0 alone lets anyone +deploy a contract emitting an identical `ReadRequested` and conscript the entire validator set into +performing free external reads — a cheap DoS. The eventual `fulfillExternalCallback` would be +rejected (`InvalidRequestId`), so no funds move, but validator work and module txs are burned. + +### 5.2 Query (`grpc_query.go`) + +```protobuf +rpc AllPendingReadRequests(QueryAllPendingReadRequestsRequest) + returns (QueryAllPendingReadRequestsResponse); // paginated, mirrors AllPendingOutbounds +rpc GetUniversalRead(QueryGetUniversalReadRequest) + returns (QueryGetUniversalReadResponse); // mirrors v2 GetUniversalTx +``` + +`AllPendingReadRequests` is the one the UV is blocked on. `GetUniversalRead` is the operator tool — +the `get-universal-tx` equivalent that made a stuck production tx diagnosable in minutes. + +### 5.3 Voting + +```protobuf +rpc VoteReadResult(MsgVoteReadResult) returns (MsgVoteReadResultResponse); + +message MsgVoteReadResult { + option (cosmos.msg.v1.signer) = "signer"; + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + string request_id = 2; + ReadResult result = 3; +} +``` + +Mirrors `MsgVoteOutbound` (`proto/uexecutor/v1/tx.proto:121`). Requires a new enum value in +`proto/uvalidator/v1/ballot.proto:25`: + +```protobuf +BALLOT_OBSERVATION_TYPE_READ_RESULT = 5; +``` + +`ballotKey = H(requestId ‖ status ‖ resultData ‖ observedBlockHeight ‖ observedBlockHash)` — +**excluding** error text. + +### 5.4 Ballot terminal hook + +Extend `BallotHooks.AfterBallotTerminal` (`x/uexecutor/keeper/ballot_hooks.go:56`) with a +`BALLOT_OBSERVATION_TYPE_READ_RESULT` case → `afterReadBallotTerminal` → §5.5. + +### 5.5 Fulfilment + +Call `UniversalCallback.fulfillExternalCallback(requestId, resultData, observedBlockHeight, +observedBlockHash)` through uexecutor's `DerivedEVMCall` so `msg.sender` is the uexecutor module +(§2.1). Record the outcome as a `PCTx` — **including `error_msg` on failure** — and set status +`FULFILLED` or `FAILED`. + +> `fulfillExternalCallback` does `call{gas: callbackGasLimit}` with `callbackGasLimit` up to +> `MAX_CALLBACK_GAS_LIMIT = 1_000_000` and performs **no 63/64 check**. Since we are the caller, we +> must ensure `gasleft() ≥ callbackGasLimit × 64/63 + buffer` before dispatch, or the callback +> silently under-runs and fails permanently. + +### 5.6 Expiry sweeper + +`EndBlocker`: range-scan `PendingByExpiry` over `[0, ctx.BlockHeight()]`, submit +`expireExternalRead(requestId)`, mark `EXPIRED`. Bound the per-block count (§9 Q5). + +--- + +## 6. Wiring (`app/app.go`) + +```go +app.EVMKeeper.SetHooks(evmkeeper.NewMultiEvmHooks( + uexecutorkeeper.NewEVMHooks(app.UexecutorKeeper), + ucallbackkeeper.NewEVMHooks(app.UcallbackKeeper), +)) +``` + +Replaces the single-hook call at `app/app.go:794`. Plus: module registration, `SetOrderEndBlockers` +entry for the sweeper, and a `ucallback` entry in `maccPerms`. + +> `BlockedAddresses()` derives from `GetMaccPerms()`, and since cosmos/evm v0.7 the blocked list also +> gates `SetBalance`. Adding `ucallback` to `maccPerms` therefore makes its address unable to receive +> native EVM value. That is almost certainly what we want — flag it if not. + +--- + +## 7. Delivery order + +1. protos + generated types (`make proto-gen`, Docker) +2. storage + keeper skeleton + genesis +3. `AllPendingReadRequests` → **unblocks the UV team immediately**, even returning empty +4. ingestion hook + `MultiEvmHooks` rewire +5. `MsgVoteReadResult` + ballot type + tally +6. terminal hook → fulfilment +7. expiry sweeper +8. `GetUniversalRead` + CLI +9. upgrade handler (new store key → `StoreUpgrades.Added`) + +Step 3 is deliberately early and cheap: it deletes `ErrReadQueriesNotAvailable` and lets the UV team +integrate against a real endpoint while the rest lands. + +--- + +## 8. Testing + +- ingestion: forged log from a non-`UniversalCallback` address is **ignored** (security regression test) +- ballot: two validators voting identical results converge; differing `error_msg` must not split them +- fulfilment: callback revert → `FAILED` + `error_msg` recorded, request not retried +- expiry: request past `expiry_block_height` is swept exactly once +- upgrade sim from the current donut release with the new store key + +--- + +## 9. Open questions / decisions needed + +**Q1 — module name vs the UV's expectation.** +The UV stub targets `x/uexecutor Query/PendingReadRequests`. If we ship `x/ucallback`, the UV team +must change the client path and proto import. Cheap, but it is a cross-team change that must be +agreed *before* they unblock. **Do we confirm `ucallback` with them now?** + +**Q2 — should we ever submit an ERROR ballot?** +PR #296's EVM executor votes ERROR on *any* `CallContract` failure +(`externalchains/evm/read_executor.go:61`), including non-archive nodes, 429s and timeouts — while +the SVM and web2 executors correctly treat transport failures as transient. Because ERROR ballots are +byte-identical by design, validators failing for unrelated infrastructure reasons converge into a +confident quorum indistinguishable from a genuine revert — and the contract makes it permanent. +**Do we harden core-side (refuse ERROR ballots lacking revert evidence), or require the UV fix first?** + +**Q3 — retry semantics.** +The contract marks `fulfilledRequests[requestId] = true` before dispatch, so a reverted callback is +terminal. Do we (a) accept that and record `FAILED`, or (b) ask contracts to mark fulfilled only on +success so a retry is possible? (b) is a contract change and must be requested while they are still +on a branch. + +**Q4 — do we gate on Push-chain confirmations before serving a request?** +The UV sets `ConfirmationType: store.ConfirmationInstant` (`pushwatcher/event_parser.go:114`) — it +acts immediately on whatever we serve. If we serve from a block that later reorgs, validators do work +for a request that never existed. **Serve immediately, or hold N blocks?** + +**Q5 — sweeper budget.** +Max expiries per block? Unbounded risks a fat EndBlocker; too low and a backlog never drains. + +**Q6 — who pays for callback gas?** +There is **no validator/reader reward path anywhere in `UniversalCallback.sol`** (grep: 0 matches). +The `callbackGasLimit × tx.gasprice` component is collected then refunded in full on both success and +failure — it pays nobody. The module bears real execution cost for up to 1M gas per read, gasless. +**Is that intentional for v1?** + +**Q7 — `ReadRequested` topic + `UniversalCallback` address: config or Go constants?** +uexecutor hardcodes both (`types/constants.go`, `uregistrytypes.SYSTEM_CONTRACTS`). That pattern has +already failed once in production: `RescueFundsOnSourceChainEventSig` still declares a signature the +contract renamed to `FundsRescued`, and it silently matches nothing. **Strong recommendation: put both +in chain config**, like `gateway_methods` / `vault_methods`. + +**Q9 — 🔴 web2 reads cannot be expressed by the contract.** +The UV has a complete web2 path (`externalchains/web2/read_executor.go`, 418 lines, SSRF-hardened), +`uread` documents the CAIP form `web2:https` and marks `DestinationBlockHeight` *"not applicable for +web2"*. But the contracts contain **zero** web2 references, and `requestExternalReadSelf` rejects it: + +```solidity +if (spec.blockNumber == 0 + || spec.blockNumber > _universalCore.chainHeightByChainNamespace(...)) revert InvalidBlockNumber(); +if (spec.account.owner.length == 0) revert InvalidAccountId(); +if (spec.minConfirmations < MIN_CONFIRMATIONS_FLOOR) revert InvalidMinConfirmations(); +``` + +A web2 request would need a fabricated `blockNumber`, a fictional `web2` namespace height in +`UniversalCore` exceeding it, a dummy `owner` (the URL lives in `query`), and a meaningless +`minConfirmations ≥ 1`. + +Ballots still converge — every web2 voter reports height `0` and an empty hash — so the tally needs no +special case. The costs are (a) we persist and serve a fake `destination_block_height`, and (b) any +future confirmation-gating on our side must exempt web2. + +**Is web2 in scope for v1? If yes, the contract needs a namespace-aware validation branch. If no, the +UV's web2 executor is dead code and we should not model for it.** + +**Q8 — one `ucallback` per read type, or reuse for future callbacks?** +The name implies a general callback module. If future non-read callbacks are planned, `UniversalRead` +should probably sit under a broader `UniversalCallbackRecord` umbrella now rather than later. + +--- + +## 10. Cross-team asks + +**Contracts** (`feat-read-state`, pre-merge — cheapest to fix now): +1. `expireExternalRead` refunds nothing — funder's fee is trapped +2. refund `call{value}` + `revert` lets an app with no `receive()` brick its own fulfilment forever +3. `UniversalReadClient` has no `receive()`; `CrossLendMock` adds one privately, so tests pass and the + requirement is invisible to integrators +4. consider marking fulfilled only on callback success (Q3) +5. `chainHeightByChainNamespace` ignores `chainId`, so all `eip155` chains share one height +6. fee derived from requester-controlled `tx.gasprice` +7. **web2 reads are unrequestable** — validation assumes a blockchain (`blockNumber != 0`, non-empty + `owner`, `minConfirmations ≥ 1`) while the UV has a complete web2 executor (Q9). Needs either a + namespace-aware validation branch or an explicit "web2 is not in v1" decision + +**UV team** (#296): +1. `eth_call` transport failures must not become ERROR ballots (Q2) — copy the web2 executor's own + three-way transient/deterministic split +2. confirm the `ucallback` module path (Q1) +3. `uread` deletion once our generated types land From 25011d84581739d06326e5b554e7c4956cb9f0c1 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:00:26 +0530 Subject: [PATCH 35/54] feat(ucallback): query pending read requests --- api/ucallback/v1/query.pulsar.go | 1245 ++++++++++++++++++++++- api/ucallback/v1/query_grpc.pb.go | 43 +- proto/ucallback/v1/query.proto | 21 + x/ucallback/autocli.go | 5 + x/ucallback/keeper/query_server.go | 44 + x/ucallback/keeper/query_server_test.go | 67 ++ x/ucallback/types/query.pb.go | 513 +++++++++- x/ucallback/types/query.pb.gw.go | 83 ++ 8 files changed, 1960 insertions(+), 61 deletions(-) create mode 100644 x/ucallback/keeper/query_server_test.go diff --git a/api/ucallback/v1/query.pulsar.go b/api/ucallback/v1/query.pulsar.go index 3dbf7787..4f7eecbd 100644 --- a/api/ucallback/v1/query.pulsar.go +++ b/api/ucallback/v1/query.pulsar.go @@ -2,8 +2,10 @@ package ucallbackv1 import ( + v1beta1 "cosmossdk.io/api/cosmos/base/query/v1beta1" fmt "fmt" runtime "github.com/cosmos/cosmos-proto/runtime" + _ "github.com/cosmos/gogoproto/gogoproto" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoiface "google.golang.org/protobuf/runtime/protoiface" @@ -804,6 +806,1014 @@ func (x *fastReflection_QueryParamsResponse) ProtoMethods() *protoiface.Methods } } +var ( + md_QueryAllPendingReadRequestsRequest protoreflect.MessageDescriptor + fd_QueryAllPendingReadRequestsRequest_pagination protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryAllPendingReadRequestsRequest = File_ucallback_v1_query_proto.Messages().ByName("QueryAllPendingReadRequestsRequest") + fd_QueryAllPendingReadRequestsRequest_pagination = md_QueryAllPendingReadRequestsRequest.Fields().ByName("pagination") +} + +var _ protoreflect.Message = (*fastReflection_QueryAllPendingReadRequestsRequest)(nil) + +type fastReflection_QueryAllPendingReadRequestsRequest QueryAllPendingReadRequestsRequest + +func (x *QueryAllPendingReadRequestsRequest) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryAllPendingReadRequestsRequest)(x) +} + +func (x *QueryAllPendingReadRequestsRequest) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryAllPendingReadRequestsRequest_messageType fastReflection_QueryAllPendingReadRequestsRequest_messageType +var _ protoreflect.MessageType = fastReflection_QueryAllPendingReadRequestsRequest_messageType{} + +type fastReflection_QueryAllPendingReadRequestsRequest_messageType struct{} + +func (x fastReflection_QueryAllPendingReadRequestsRequest_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryAllPendingReadRequestsRequest)(nil) +} +func (x fastReflection_QueryAllPendingReadRequestsRequest_messageType) New() protoreflect.Message { + return new(fastReflection_QueryAllPendingReadRequestsRequest) +} +func (x fastReflection_QueryAllPendingReadRequestsRequest_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryAllPendingReadRequestsRequest +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Descriptor() protoreflect.MessageDescriptor { + return md_QueryAllPendingReadRequestsRequest +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Type() protoreflect.MessageType { + return _fastReflection_QueryAllPendingReadRequestsRequest_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) New() protoreflect.Message { + return new(fastReflection_QueryAllPendingReadRequestsRequest) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Interface() protoreflect.ProtoMessage { + return (*QueryAllPendingReadRequestsRequest)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Pagination != nil { + value := protoreflect.ValueOfMessage(x.Pagination.ProtoReflect()) + if !f(fd_QueryAllPendingReadRequestsRequest_pagination, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + return x.Pagination != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + x.Pagination = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + value := x.Pagination + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + x.Pagination = value.Message().Interface().(*v1beta1.PageRequest) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + if x.Pagination == nil { + x.Pagination = new(v1beta1.PageRequest) + } + return protoreflect.ValueOfMessage(x.Pagination.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsRequest.pagination": + m := new(v1beta1.PageRequest) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsRequest does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryAllPendingReadRequestsRequest", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryAllPendingReadRequestsRequest) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryAllPendingReadRequestsRequest) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Pagination != nil { + l = options.Size(x.Pagination) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryAllPendingReadRequestsRequest) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Pagination != nil { + encoded, err := options.Marshal(x.Pagination) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryAllPendingReadRequestsRequest) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryAllPendingReadRequestsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryAllPendingReadRequestsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Pagination == nil { + x.Pagination = &v1beta1.PageRequest{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Pagination); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var _ protoreflect.List = (*_QueryAllPendingReadRequestsResponse_1_list)(nil) + +type _QueryAllPendingReadRequestsResponse_1_list struct { + list *[]*UniversalRead +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalRead) + (*x.list)[i] = concreteValue +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalRead) + *x.list = append(*x.list, concreteValue) +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) AppendMutable() protoreflect.Value { + v := new(UniversalRead) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) NewElement() protoreflect.Value { + v := new(UniversalRead) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_QueryAllPendingReadRequestsResponse_1_list) IsValid() bool { + return x.list != nil +} + +var ( + md_QueryAllPendingReadRequestsResponse protoreflect.MessageDescriptor + fd_QueryAllPendingReadRequestsResponse_reads protoreflect.FieldDescriptor + fd_QueryAllPendingReadRequestsResponse_pagination protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryAllPendingReadRequestsResponse = File_ucallback_v1_query_proto.Messages().ByName("QueryAllPendingReadRequestsResponse") + fd_QueryAllPendingReadRequestsResponse_reads = md_QueryAllPendingReadRequestsResponse.Fields().ByName("reads") + fd_QueryAllPendingReadRequestsResponse_pagination = md_QueryAllPendingReadRequestsResponse.Fields().ByName("pagination") +} + +var _ protoreflect.Message = (*fastReflection_QueryAllPendingReadRequestsResponse)(nil) + +type fastReflection_QueryAllPendingReadRequestsResponse QueryAllPendingReadRequestsResponse + +func (x *QueryAllPendingReadRequestsResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryAllPendingReadRequestsResponse)(x) +} + +func (x *QueryAllPendingReadRequestsResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryAllPendingReadRequestsResponse_messageType fastReflection_QueryAllPendingReadRequestsResponse_messageType +var _ protoreflect.MessageType = fastReflection_QueryAllPendingReadRequestsResponse_messageType{} + +type fastReflection_QueryAllPendingReadRequestsResponse_messageType struct{} + +func (x fastReflection_QueryAllPendingReadRequestsResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryAllPendingReadRequestsResponse)(nil) +} +func (x fastReflection_QueryAllPendingReadRequestsResponse_messageType) New() protoreflect.Message { + return new(fastReflection_QueryAllPendingReadRequestsResponse) +} +func (x fastReflection_QueryAllPendingReadRequestsResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryAllPendingReadRequestsResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Descriptor() protoreflect.MessageDescriptor { + return md_QueryAllPendingReadRequestsResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Type() protoreflect.MessageType { + return _fastReflection_QueryAllPendingReadRequestsResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) New() protoreflect.Message { + return new(fastReflection_QueryAllPendingReadRequestsResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Interface() protoreflect.ProtoMessage { + return (*QueryAllPendingReadRequestsResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if len(x.Reads) != 0 { + value := protoreflect.ValueOfList(&_QueryAllPendingReadRequestsResponse_1_list{list: &x.Reads}) + if !f(fd_QueryAllPendingReadRequestsResponse_reads, value) { + return + } + } + if x.Pagination != nil { + value := protoreflect.ValueOfMessage(x.Pagination.ProtoReflect()) + if !f(fd_QueryAllPendingReadRequestsResponse_pagination, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + return len(x.Reads) != 0 + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + return x.Pagination != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + x.Reads = nil + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + x.Pagination = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + if len(x.Reads) == 0 { + return protoreflect.ValueOfList(&_QueryAllPendingReadRequestsResponse_1_list{}) + } + listValue := &_QueryAllPendingReadRequestsResponse_1_list{list: &x.Reads} + return protoreflect.ValueOfList(listValue) + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + value := x.Pagination + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + lv := value.List() + clv := lv.(*_QueryAllPendingReadRequestsResponse_1_list) + x.Reads = *clv.list + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + x.Pagination = value.Message().Interface().(*v1beta1.PageResponse) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + if x.Reads == nil { + x.Reads = []*UniversalRead{} + } + value := &_QueryAllPendingReadRequestsResponse_1_list{list: &x.Reads} + return protoreflect.ValueOfList(value) + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + if x.Pagination == nil { + x.Pagination = new(v1beta1.PageResponse) + } + return protoreflect.ValueOfMessage(x.Pagination.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryAllPendingReadRequestsResponse.reads": + list := []*UniversalRead{} + return protoreflect.ValueOfList(&_QueryAllPendingReadRequestsResponse_1_list{list: &list}) + case "ucallback.v1.QueryAllPendingReadRequestsResponse.pagination": + m := new(v1beta1.PageResponse) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryAllPendingReadRequestsResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryAllPendingReadRequestsResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryAllPendingReadRequestsResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryAllPendingReadRequestsResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryAllPendingReadRequestsResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if len(x.Reads) > 0 { + for _, e := range x.Reads { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } + } + if x.Pagination != nil { + l = options.Size(x.Pagination) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryAllPendingReadRequestsResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Pagination != nil { + encoded, err := options.Marshal(x.Pagination) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + if len(x.Reads) > 0 { + for iNdEx := len(x.Reads) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.Reads[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryAllPendingReadRequestsResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryAllPendingReadRequestsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryAllPendingReadRequestsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Reads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Reads = append(x.Reads, &UniversalRead{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Reads[len(x.Reads)-1]); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Pagination == nil { + x.Pagination = &v1beta1.PageResponse{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Pagination); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.0 @@ -881,40 +1891,156 @@ func (x *QueryParamsResponse) GetParams() *Params { return nil } +type QueryAllPendingReadRequestsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pagination *v1beta1.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *QueryAllPendingReadRequestsRequest) Reset() { + *x = QueryAllPendingReadRequestsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryAllPendingReadRequestsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryAllPendingReadRequestsRequest) ProtoMessage() {} + +// Deprecated: Use QueryAllPendingReadRequestsRequest.ProtoReflect.Descriptor instead. +func (*QueryAllPendingReadRequestsRequest) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{2} +} + +func (x *QueryAllPendingReadRequestsRequest) GetPagination() *v1beta1.PageRequest { + if x != nil { + return x.Pagination + } + return nil +} + +type QueryAllPendingReadRequestsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Reads that are unsettled AND not yet past their expiry height. Requests past + // expiry are withheld here even before the sweeper retires them, so validators + // never take on work that can no longer be fulfilled in time. + Reads []*UniversalRead `protobuf:"bytes,1,rep,name=reads,proto3" json:"reads,omitempty"` + Pagination *v1beta1.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (x *QueryAllPendingReadRequestsResponse) Reset() { + *x = QueryAllPendingReadRequestsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryAllPendingReadRequestsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryAllPendingReadRequestsResponse) ProtoMessage() {} + +// Deprecated: Use QueryAllPendingReadRequestsResponse.ProtoReflect.Descriptor instead. +func (*QueryAllPendingReadRequestsResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{3} +} + +func (x *QueryAllPendingReadRequestsResponse) GetReads() []*UniversalRead { + if x != nil { + return x.Reads + } + return nil +} + +func (x *QueryAllPendingReadRequestsResponse) GetPagination() *v1beta1.PageResponse { + if x != nil { + return x.Pagination + } + return nil +} + var File_ucallback_v1_query_proto protoreflect.FileDescriptor var file_ucallback_v1_query_proto_rawDesc = []byte{ 0x0a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x75, 0x63, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x14, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2c, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x32, 0x74, 0x0a, - 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x12, 0x20, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, - 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, - 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, - 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, - 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, - 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x14, + 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x43, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x6c, 0x0a, 0x22, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, + 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa7, 0x01, 0x0a, 0x23, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x37, 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, + 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x42, 0x04, 0xc8, 0xde, 0x1f, + 0x00, 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x32, 0xa1, 0x02, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x16, 0x12, 0x14, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, + 0x31, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0xaa, 0x01, 0x0a, 0x16, 0x41, 0x6c, 0x6c, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, + 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, + 0x12, 0x23, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, + 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, + 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -929,21 +2055,31 @@ func file_ucallback_v1_query_proto_rawDescGZIP() []byte { return file_ucallback_v1_query_proto_rawDescData } -var file_ucallback_v1_query_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_query_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_ucallback_v1_query_proto_goTypes = []interface{}{ - (*QueryParamsRequest)(nil), // 0: ucallback.v1.QueryParamsRequest - (*QueryParamsResponse)(nil), // 1: ucallback.v1.QueryParamsResponse - (*Params)(nil), // 2: ucallback.v1.Params + (*QueryParamsRequest)(nil), // 0: ucallback.v1.QueryParamsRequest + (*QueryParamsResponse)(nil), // 1: ucallback.v1.QueryParamsResponse + (*QueryAllPendingReadRequestsRequest)(nil), // 2: ucallback.v1.QueryAllPendingReadRequestsRequest + (*QueryAllPendingReadRequestsResponse)(nil), // 3: ucallback.v1.QueryAllPendingReadRequestsResponse + (*Params)(nil), // 4: ucallback.v1.Params + (*v1beta1.PageRequest)(nil), // 5: cosmos.base.query.v1beta1.PageRequest + (*UniversalRead)(nil), // 6: ucallback.v1.UniversalRead + (*v1beta1.PageResponse)(nil), // 7: cosmos.base.query.v1beta1.PageResponse } var file_ucallback_v1_query_proto_depIdxs = []int32{ - 2, // 0: ucallback.v1.QueryParamsResponse.params:type_name -> ucallback.v1.Params - 0, // 1: ucallback.v1.Query.Params:input_type -> ucallback.v1.QueryParamsRequest - 1, // 2: ucallback.v1.Query.Params:output_type -> ucallback.v1.QueryParamsResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 4, // 0: ucallback.v1.QueryParamsResponse.params:type_name -> ucallback.v1.Params + 5, // 1: ucallback.v1.QueryAllPendingReadRequestsRequest.pagination:type_name -> cosmos.base.query.v1beta1.PageRequest + 6, // 2: ucallback.v1.QueryAllPendingReadRequestsResponse.reads:type_name -> ucallback.v1.UniversalRead + 7, // 3: ucallback.v1.QueryAllPendingReadRequestsResponse.pagination:type_name -> cosmos.base.query.v1beta1.PageResponse + 0, // 4: ucallback.v1.Query.Params:input_type -> ucallback.v1.QueryParamsRequest + 2, // 5: ucallback.v1.Query.AllPendingReadRequests:input_type -> ucallback.v1.QueryAllPendingReadRequestsRequest + 1, // 6: ucallback.v1.Query.Params:output_type -> ucallback.v1.QueryParamsResponse + 3, // 7: ucallback.v1.Query.AllPendingReadRequests:output_type -> ucallback.v1.QueryAllPendingReadRequestsResponse + 6, // [6:8] is the sub-list for method output_type + 4, // [4:6] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_ucallback_v1_query_proto_init() } @@ -952,6 +2088,7 @@ func file_ucallback_v1_query_proto_init() { return } file_ucallback_v1_genesis_proto_init() + file_ucallback_v1_types_proto_init() if !protoimpl.UnsafeEnabled { file_ucallback_v1_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*QueryParamsRequest); i { @@ -977,6 +2114,30 @@ func file_ucallback_v1_query_proto_init() { return nil } } + file_ucallback_v1_query_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryAllPendingReadRequestsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_query_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryAllPendingReadRequestsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -984,7 +2145,7 @@ func file_ucallback_v1_query_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ucallback_v1_query_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/api/ucallback/v1/query_grpc.pb.go b/api/ucallback/v1/query_grpc.pb.go index b6deab65..bf4d57d1 100644 --- a/api/ucallback/v1/query_grpc.pb.go +++ b/api/ucallback/v1/query_grpc.pb.go @@ -19,7 +19,8 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - Query_Params_FullMethodName = "/ucallback.v1.Query/Params" + Query_Params_FullMethodName = "/ucallback.v1.Query/Params" + Query_AllPendingReadRequests_FullMethodName = "/ucallback.v1.Query/AllPendingReadRequests" ) // QueryClient is the client API for Query service. @@ -28,6 +29,9 @@ const ( type QueryClient interface { // Params queries all parameters of the module. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // AllPendingReadRequests lists read requests still awaiting an observation. + // This is the endpoint universal validators poll. + AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) } type queryClient struct { @@ -47,12 +51,24 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . return out, nil } +func (c *queryClient) AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) { + out := new(QueryAllPendingReadRequestsResponse) + err := c.cc.Invoke(ctx, Query_AllPendingReadRequests_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. // All implementations must embed UnimplementedQueryServer // for forward compatibility type QueryServer interface { // Params queries all parameters of the module. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // AllPendingReadRequests lists read requests still awaiting an observation. + // This is the endpoint universal validators poll. + AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) mustEmbedUnimplementedQueryServer() } @@ -63,6 +79,9 @@ type UnimplementedQueryServer struct { func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } +func (UnimplementedQueryServer) AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllPendingReadRequests not implemented") +} func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {} // UnsafeQueryServer may be embedded to opt out of forward compatibility for this service. @@ -94,6 +113,24 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Query_AllPendingReadRequests_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllPendingReadRequestsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllPendingReadRequests(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Query_AllPendingReadRequests_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllPendingReadRequests(ctx, req.(*QueryAllPendingReadRequestsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Query_ServiceDesc is the grpc.ServiceDesc for Query service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -105,6 +142,10 @@ var Query_ServiceDesc = grpc.ServiceDesc{ MethodName: "Params", Handler: _Query_Params_Handler, }, + { + MethodName: "AllPendingReadRequests", + Handler: _Query_AllPendingReadRequests_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ucallback/v1/query.proto", diff --git a/proto/ucallback/v1/query.proto b/proto/ucallback/v1/query.proto index edd7cb94..ee99fc82 100755 --- a/proto/ucallback/v1/query.proto +++ b/proto/ucallback/v1/query.proto @@ -1,8 +1,11 @@ syntax = "proto3"; package ucallback.v1; +import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; import "ucallback/v1/genesis.proto"; +import "ucallback/v1/types.proto"; option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; @@ -12,6 +15,12 @@ service Query { rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/ucallback/v1/params"; } + + // AllPendingReadRequests lists read requests still awaiting an observation. + // This is the endpoint universal validators poll. + rpc AllPendingReadRequests(QueryAllPendingReadRequestsRequest) returns (QueryAllPendingReadRequestsResponse) { + option (google.api.http).get = "/ucallback/v1/pending_read_requests"; + } } // QueryParamsRequest is the request type for the Query/Params RPC method. @@ -22,3 +31,15 @@ message QueryParamsResponse { // params defines the parameters of the module. Params params = 1; } + +message QueryAllPendingReadRequestsRequest { + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +message QueryAllPendingReadRequestsResponse { + // Reads that are unsettled AND not yet past their expiry height. Requests past + // expiry are withheld here even before the sweeper retires them, so validators + // never take on work that can no longer be fulfilled in time. + repeated UniversalRead reads = 1 [(gogoproto.nullable) = false]; + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/x/ucallback/autocli.go b/x/ucallback/autocli.go index 13e9e7a6..202b6bba 100755 --- a/x/ucallback/autocli.go +++ b/x/ucallback/autocli.go @@ -16,6 +16,11 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { Use: "params", Short: "Query the current consensus parameters", }, + { + RpcMethod: "AllPendingReadRequests", + Use: "pending-read-requests", + Short: "List read requests awaiting an observation", + }, }, }, Tx: &autocliv1.ServiceCommandDescriptor{ diff --git a/x/ucallback/keeper/query_server.go b/x/ucallback/keeper/query_server.go index 498c2f03..b0ad608f 100755 --- a/x/ucallback/keeper/query_server.go +++ b/x/ucallback/keeper/query_server.go @@ -3,7 +3,13 @@ package keeper import ( "context" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" "github.com/pushchain/push-chain-node/x/ucallback/types" ) @@ -28,3 +34,41 @@ func (k Querier) Params(c context.Context, req *types.QueryParamsRequest) (*type return &types.QueryParamsResponse{Params: &p}, nil } + +// AllPendingReadRequests implements types.QueryServer. +// +// Paginates the in-flight set (PendingByExpiry), which already excludes settled +// reads. Requests whose expiry height has passed are filtered out here too, rather +// than waiting for the sweeper to retire them: a validator that picked one up would +// spend a destination-chain read on work the contract may no longer accept. That +// makes the visible set correct regardless of how often the sweeper runs. +func (k Querier) AllPendingReadRequests(goCtx context.Context, req *types.QueryAllPendingReadRequestsRequest) (*types.QueryAllPendingReadRequestsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) + height := uint64(ctx.BlockHeight()) + + reads, pageRes, err := query.CollectionFilteredPaginate( + ctx, k.Keeper.PendingByExpiry, req.Pagination, + func(key collections.Pair[uint64, string], _ collections.NoValue) (bool, error) { + return key.K1() > height, nil + }, + func(key collections.Pair[uint64, string], _ collections.NoValue) (types.UniversalRead, error) { + ur, found := k.Keeper.GetUniversalRead(ctx, key.K2()) + if !found { + // index entry with no record — skip rather than fail the page + return types.UniversalRead{}, nil + } + return ur, nil + }, + ) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryAllPendingReadRequestsResponse{ + Reads: reads, + Pagination: pageRes, + }, nil +} diff --git a/x/ucallback/keeper/query_server_test.go b/x/ucallback/keeper/query_server_test.go new file mode 100644 index 00000000..1e2a0c64 --- /dev/null +++ b/x/ucallback/keeper/query_server_test.go @@ -0,0 +1,67 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +func pendingIDs(t *testing.T, f *testFixture) []string { + t.Helper() + res, err := f.queryServer.AllPendingReadRequests(f.ctx, + &types.QueryAllPendingReadRequestsRequest{}) + require.NoError(t, err) + got := make([]string, 0, len(res.Reads)) + for _, r := range res.Reads { + got = append(got, r.Id) + } + return got +} + +// Only unsettled reads are listed. +func TestAllPendingReadRequests_ExcludesSettled(t *testing.T) { + f := SetupTest(t) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xpending", "0xTX", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xvoting", "0xTX", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xdone", "0xTX", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED))) + + require.ElementsMatch(t, []string{"0xpending", "0xvoting"}, pendingIDs(t, f)) +} + +// A read past its expiry height is withheld even though the sweeper has not run, +// so validators never pick up work that can no longer be fulfilled in time. +func TestAllPendingReadRequests_WithholdsExpired(t *testing.T) { + f := SetupTest(t) + + f.ctx = f.ctx.WithBlockHeight(100) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xexpired", "0xTX", 50, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xatheight", "0xTX", 100, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xlive", "0xTX", 150, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + + // still unsettled in state — the filter is at read time, not a mutation + require.True(t, f.k.HasUniversalRead(f.ctx, "0xexpired")) + + require.Equal(t, []string{"0xlive"}, pendingIDs(t, f), + "expiry height is exclusive: a read expiring at the current height is already too late") +} + +func TestAllPendingReadRequests_Empty(t *testing.T) { + f := SetupTest(t) + require.Empty(t, pendingIDs(t, f)) +} + +func TestAllPendingReadRequests_NilRequest(t *testing.T) { + f := SetupTest(t) + _, err := f.queryServer.AllPendingReadRequests(f.ctx, nil) + require.Error(t, err) +} diff --git a/x/ucallback/types/query.pb.go b/x/ucallback/types/query.pb.go index 0f81b76c..4a9d7e74 100644 --- a/x/ucallback/types/query.pb.go +++ b/x/ucallback/types/query.pb.go @@ -6,6 +6,8 @@ package types import ( context "context" fmt "fmt" + query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" _ "google.golang.org/genproto/googleapis/api/annotations" @@ -111,32 +113,145 @@ func (m *QueryParamsResponse) GetParams() *Params { return nil } +type QueryAllPendingReadRequestsRequest struct { + Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllPendingReadRequestsRequest) Reset() { *m = QueryAllPendingReadRequestsRequest{} } +func (m *QueryAllPendingReadRequestsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllPendingReadRequestsRequest) ProtoMessage() {} +func (*QueryAllPendingReadRequestsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{2} +} +func (m *QueryAllPendingReadRequestsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllPendingReadRequestsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllPendingReadRequestsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllPendingReadRequestsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllPendingReadRequestsRequest.Merge(m, src) +} +func (m *QueryAllPendingReadRequestsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllPendingReadRequestsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllPendingReadRequestsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllPendingReadRequestsRequest proto.InternalMessageInfo + +func (m *QueryAllPendingReadRequestsRequest) GetPagination() *query.PageRequest { + if m != nil { + return m.Pagination + } + return nil +} + +type QueryAllPendingReadRequestsResponse struct { + // Reads that are unsettled AND not yet past their expiry height. Requests past + // expiry are withheld here even before the sweeper retires them, so validators + // never take on work that can no longer be fulfilled in time. + Reads []UniversalRead `protobuf:"bytes,1,rep,name=reads,proto3" json:"reads"` + Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` +} + +func (m *QueryAllPendingReadRequestsResponse) Reset() { *m = QueryAllPendingReadRequestsResponse{} } +func (m *QueryAllPendingReadRequestsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllPendingReadRequestsResponse) ProtoMessage() {} +func (*QueryAllPendingReadRequestsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{3} +} +func (m *QueryAllPendingReadRequestsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllPendingReadRequestsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllPendingReadRequestsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllPendingReadRequestsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllPendingReadRequestsResponse.Merge(m, src) +} +func (m *QueryAllPendingReadRequestsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllPendingReadRequestsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllPendingReadRequestsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllPendingReadRequestsResponse proto.InternalMessageInfo + +func (m *QueryAllPendingReadRequestsResponse) GetReads() []UniversalRead { + if m != nil { + return m.Reads + } + return nil +} + +func (m *QueryAllPendingReadRequestsResponse) GetPagination() *query.PageResponse { + if m != nil { + return m.Pagination + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "ucallback.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "ucallback.v1.QueryParamsResponse") + proto.RegisterType((*QueryAllPendingReadRequestsRequest)(nil), "ucallback.v1.QueryAllPendingReadRequestsRequest") + proto.RegisterType((*QueryAllPendingReadRequestsResponse)(nil), "ucallback.v1.QueryAllPendingReadRequestsResponse") } func init() { proto.RegisterFile("ucallback/v1/query.proto", fileDescriptor_a64b97cfcca36b9d) } var fileDescriptor_a64b97cfcca36b9d = []byte{ - // 265 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x28, 0x4d, 0x4e, 0xcc, - 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, - 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x81, 0xcb, 0xe8, 0x95, 0x19, 0x4a, 0xc9, 0xa4, 0xe7, 0xe7, - 0xa7, 0xe7, 0xa4, 0xea, 0x27, 0x16, 0x64, 0xea, 0x27, 0xe6, 0xe5, 0xe5, 0x97, 0x24, 0x96, 0x64, - 0xe6, 0xe7, 0x15, 0x43, 0xd4, 0x4a, 0x49, 0xa1, 0x98, 0x92, 0x9e, 0x9a, 0x97, 0x5a, 0x9c, 0x09, - 0x95, 0x53, 0x12, 0xe1, 0x12, 0x0a, 0x04, 0x19, 0x1b, 0x90, 0x58, 0x94, 0x98, 0x5b, 0x1c, 0x94, - 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0xa2, 0xe4, 0xcc, 0x25, 0x8c, 0x22, 0x5a, 0x5c, 0x90, 0x9f, 0x57, - 0x9c, 0x2a, 0xa4, 0xc3, 0xc5, 0x56, 0x00, 0x16, 0x91, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x12, - 0xd1, 0x43, 0x76, 0x85, 0x1e, 0x54, 0x35, 0x54, 0x8d, 0x51, 0x09, 0x17, 0x2b, 0xd8, 0x10, 0xa1, - 0x6c, 0x2e, 0x36, 0x88, 0x94, 0x90, 0x02, 0xaa, 0x06, 0x4c, 0x9b, 0xa5, 0x14, 0xf1, 0xa8, 0x80, - 0xb8, 0x42, 0x49, 0xa6, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, 0x62, 0x42, 0x22, 0xfa, 0x28, 0xfe, 0x82, - 0xd8, 0xea, 0x14, 0x70, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, - 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x66, 0xe9, - 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x05, 0xa5, 0xc5, 0x19, 0xc9, 0x19, - 0x89, 0x99, 0x79, 0x60, 0x96, 0x2e, 0x98, 0xa9, 0x9b, 0x97, 0x9f, 0x92, 0xaa, 0x5f, 0x81, 0x64, - 0x6a, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0xa4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x4b, 0x9f, 0xf7, 0xc2, 0x8d, 0x01, 0x00, 0x00, + // 457 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x4f, 0x6b, 0xd4, 0x40, + 0x14, 0xdf, 0xac, 0x76, 0x0f, 0x53, 0x4f, 0xe3, 0x52, 0x4a, 0x2c, 0xb1, 0xa6, 0xa8, 0x45, 0xed, + 0x8c, 0x59, 0x41, 0xcf, 0x56, 0xd0, 0x6b, 0x0c, 0x78, 0xf1, 0x52, 0x26, 0xc9, 0x63, 0x76, 0x68, + 0x76, 0x26, 0xcd, 0x24, 0xc1, 0x5e, 0xfd, 0x04, 0x82, 0x9f, 0xc0, 0x93, 0xe0, 0x27, 0xe9, 0xb1, + 0xe0, 0xc5, 0x93, 0xc8, 0xae, 0x1f, 0x44, 0x32, 0x33, 0xd5, 0x44, 0xcb, 0x4a, 0x6f, 0x8f, 0x79, + 0xbf, 0xf7, 0xfb, 0xf3, 0xde, 0xa0, 0xed, 0x26, 0x63, 0x45, 0x91, 0xb2, 0xec, 0x98, 0xb6, 0x11, + 0x3d, 0x69, 0xa0, 0x3a, 0x25, 0x65, 0xa5, 0x6a, 0x85, 0x6f, 0xfc, 0xee, 0x90, 0x36, 0xf2, 0xa7, + 0x5c, 0x71, 0x65, 0x1a, 0xb4, 0xab, 0x2c, 0xc6, 0xdf, 0xe1, 0x4a, 0xf1, 0x02, 0x28, 0x2b, 0x05, + 0x65, 0x52, 0xaa, 0x9a, 0xd5, 0x42, 0x49, 0xed, 0xba, 0x0f, 0x32, 0xa5, 0x17, 0x4a, 0xd3, 0x94, + 0x69, 0xb0, 0xd4, 0xb4, 0x8d, 0x52, 0xa8, 0x59, 0x44, 0x4b, 0xc6, 0x85, 0x34, 0x60, 0x87, 0xf5, + 0x07, 0x3e, 0x38, 0x48, 0xd0, 0xe2, 0x82, 0x67, 0xe8, 0xb1, 0x3e, 0x2d, 0xc1, 0x75, 0xc2, 0x29, + 0xc2, 0xaf, 0x3b, 0xde, 0x98, 0x55, 0x6c, 0xa1, 0x13, 0x38, 0x69, 0x40, 0xd7, 0xe1, 0x0b, 0x74, + 0x73, 0xf0, 0xaa, 0x4b, 0x25, 0x35, 0xe0, 0x47, 0x68, 0x52, 0x9a, 0x97, 0x6d, 0x6f, 0xd7, 0xdb, + 0xdf, 0x9c, 0x4d, 0x49, 0x3f, 0x21, 0x71, 0x68, 0x87, 0x09, 0x0b, 0x14, 0x1a, 0x92, 0xe7, 0x45, + 0x11, 0x83, 0xcc, 0x85, 0xe4, 0x09, 0xb0, 0xdc, 0x49, 0x5c, 0x48, 0xe1, 0x97, 0x08, 0xfd, 0x89, + 0xe2, 0x78, 0xef, 0x11, 0x9b, 0x9b, 0x74, 0xb9, 0x89, 0x5d, 0xa9, 0xcb, 0x4d, 0x62, 0xc6, 0xc1, + 0xcd, 0x26, 0xbd, 0xc9, 0xf0, 0xb3, 0x87, 0xf6, 0xd6, 0xca, 0xb9, 0x0c, 0xcf, 0xd0, 0x46, 0x05, + 0x2c, 0xef, 0x22, 0x5c, 0xdb, 0xdf, 0x9c, 0xdd, 0x1a, 0x46, 0x78, 0x23, 0x45, 0x0b, 0x95, 0x66, + 0x45, 0x37, 0x7b, 0x78, 0xfd, 0xec, 0xfb, 0xed, 0x51, 0x62, 0xf1, 0xf8, 0xd5, 0xc0, 0xe8, 0xd8, + 0x18, 0xbd, 0xff, 0x5f, 0xa3, 0x56, 0xb5, 0xef, 0x74, 0xf6, 0x69, 0x8c, 0x36, 0x8c, 0x53, 0x7c, + 0x8c, 0x26, 0x76, 0x67, 0x78, 0x77, 0x68, 0xe3, 0xdf, 0x93, 0xf8, 0x77, 0xd6, 0x20, 0xac, 0x48, + 0xb8, 0xf3, 0xfe, 0xeb, 0xcf, 0x8f, 0xe3, 0x2d, 0x3c, 0xa5, 0x83, 0x73, 0xdb, 0x73, 0xe0, 0x2f, + 0x1e, 0xda, 0xba, 0x7c, 0x37, 0xf8, 0xf1, 0x25, 0xdc, 0x6b, 0xaf, 0xe6, 0x47, 0x57, 0x98, 0x70, + 0xee, 0x1e, 0x1a, 0x77, 0x77, 0xf1, 0xde, 0x5f, 0xee, 0xec, 0xc8, 0x51, 0xb7, 0xe4, 0xa3, 0xca, + 0x0d, 0x1d, 0xc6, 0x67, 0xcb, 0xc0, 0x3b, 0x5f, 0x06, 0xde, 0x8f, 0x65, 0xe0, 0x7d, 0x58, 0x05, + 0xa3, 0xf3, 0x55, 0x30, 0xfa, 0xb6, 0x0a, 0x46, 0x6f, 0x9f, 0x72, 0x51, 0xcf, 0x9b, 0x94, 0x64, + 0x6a, 0x41, 0xcb, 0x46, 0xcf, 0xb3, 0x39, 0x13, 0xd2, 0x54, 0x07, 0xa6, 0x3c, 0x90, 0x2a, 0x07, + 0xfa, 0xae, 0x27, 0x62, 0xbe, 0x7b, 0x3a, 0x31, 0xff, 0xfd, 0xc9, 0xaf, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xb6, 0x88, 0xa6, 0x77, 0xaf, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -153,6 +268,9 @@ const _ = grpc.SupportPackageIsVersion4 type QueryClient interface { // Params queries all parameters of the module. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // AllPendingReadRequests lists read requests still awaiting an observation. + // This is the endpoint universal validators poll. + AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) } type queryClient struct { @@ -172,10 +290,22 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . return out, nil } +func (c *queryClient) AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) { + out := new(QueryAllPendingReadRequestsResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Query/AllPendingReadRequests", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Params queries all parameters of the module. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // AllPendingReadRequests lists read requests still awaiting an observation. + // This is the endpoint universal validators poll. + AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -185,6 +315,9 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } +func (*UnimplementedQueryServer) AllPendingReadRequests(ctx context.Context, req *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AllPendingReadRequests not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -208,6 +341,24 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Query_AllPendingReadRequests_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllPendingReadRequestsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AllPendingReadRequests(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Query/AllPendingReadRequests", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AllPendingReadRequests(ctx, req.(*QueryAllPendingReadRequestsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "ucallback.v1.Query", HandlerType: (*QueryServer)(nil), @@ -216,6 +367,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Params", Handler: _Query_Params_Handler, }, + { + MethodName: "AllPendingReadRequests", + Handler: _Query_AllPendingReadRequests_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ucallback/v1/query.proto", @@ -279,6 +434,90 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryAllPendingReadRequestsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllPendingReadRequestsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllPendingReadRequestsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAllPendingReadRequestsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllPendingReadRequestsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllPendingReadRequestsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Pagination != nil { + { + size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Reads) > 0 { + for iNdEx := len(m.Reads) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reads[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -312,6 +551,38 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryAllPendingReadRequestsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryAllPendingReadRequestsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Reads) > 0 { + for _, e := range m.Reads { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.Pagination != nil { + l = m.Pagination.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -454,6 +725,212 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryAllPendingReadRequestsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllPendingReadRequestsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllPendingReadRequestsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageRequest{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAllPendingReadRequestsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllPendingReadRequestsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllPendingReadRequestsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reads = append(m.Reads, UniversalRead{}) + if err := m.Reads[len(m.Reads)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagination", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pagination == nil { + m.Pagination = &query.PageResponse{} + } + if err := m.Pagination.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ucallback/types/query.pb.gw.go b/x/ucallback/types/query.pb.gw.go index 423a5545..18acef35 100644 --- a/x/ucallback/types/query.pb.gw.go +++ b/x/ucallback/types/query.pb.gw.go @@ -51,6 +51,42 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal } +var ( + filter_Query_AllPendingReadRequests_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_AllPendingReadRequests_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllPendingReadRequestsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_AllPendingReadRequests_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.AllPendingReadRequests(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AllPendingReadRequests_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllPendingReadRequestsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_AllPendingReadRequests_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.AllPendingReadRequests(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -80,6 +116,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_AllPendingReadRequests_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_AllPendingReadRequests_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_AllPendingReadRequests_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -141,13 +200,37 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_AllPendingReadRequests_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_AllPendingReadRequests_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_AllPendingReadRequests_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"ucallback", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_AllPendingReadRequests_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"ucallback", "v1", "pending_read_requests"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_AllPendingReadRequests_0 = runtime.ForwardResponseMessage ) From 1501b72f00d699fb5d74d4623b6d223dfb3087fe Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:03:12 +0530 Subject: [PATCH 36/54] feat(ucallback): query a read by id and by requesting tx --- api/ucallback/v1/query.pulsar.go | 2103 ++++++++++++++++++++++- api/ucallback/v1/query_grpc.pb.go | 80 + proto/ucallback/v1/query.proto | 28 + x/ucallback/autocli.go | 12 + x/ucallback/keeper/query_server.go | 43 + x/ucallback/keeper/query_server_test.go | 64 + x/ucallback/types/query.pb.go | 856 ++++++++- x/ucallback/types/query.pb.gw.go | 202 +++ 8 files changed, 3301 insertions(+), 87 deletions(-) diff --git a/api/ucallback/v1/query.pulsar.go b/api/ucallback/v1/query.pulsar.go index 4f7eecbd..d0a8203f 100644 --- a/api/ucallback/v1/query.pulsar.go +++ b/api/ucallback/v1/query.pulsar.go @@ -1814,6 +1814,1775 @@ func (x *fastReflection_QueryAllPendingReadRequestsResponse) ProtoMethods() *pro } } +var ( + md_QueryUniversalReadRequest protoreflect.MessageDescriptor + fd_QueryUniversalReadRequest_request_id protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryUniversalReadRequest = File_ucallback_v1_query_proto.Messages().ByName("QueryUniversalReadRequest") + fd_QueryUniversalReadRequest_request_id = md_QueryUniversalReadRequest.Fields().ByName("request_id") +} + +var _ protoreflect.Message = (*fastReflection_QueryUniversalReadRequest)(nil) + +type fastReflection_QueryUniversalReadRequest QueryUniversalReadRequest + +func (x *QueryUniversalReadRequest) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryUniversalReadRequest)(x) +} + +func (x *QueryUniversalReadRequest) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryUniversalReadRequest_messageType fastReflection_QueryUniversalReadRequest_messageType +var _ protoreflect.MessageType = fastReflection_QueryUniversalReadRequest_messageType{} + +type fastReflection_QueryUniversalReadRequest_messageType struct{} + +func (x fastReflection_QueryUniversalReadRequest_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryUniversalReadRequest)(nil) +} +func (x fastReflection_QueryUniversalReadRequest_messageType) New() protoreflect.Message { + return new(fastReflection_QueryUniversalReadRequest) +} +func (x fastReflection_QueryUniversalReadRequest_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryUniversalReadRequest +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryUniversalReadRequest) Descriptor() protoreflect.MessageDescriptor { + return md_QueryUniversalReadRequest +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryUniversalReadRequest) Type() protoreflect.MessageType { + return _fastReflection_QueryUniversalReadRequest_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryUniversalReadRequest) New() protoreflect.Message { + return new(fastReflection_QueryUniversalReadRequest) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryUniversalReadRequest) Interface() protoreflect.ProtoMessage { + return (*QueryUniversalReadRequest)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryUniversalReadRequest) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.RequestId != "" { + value := protoreflect.ValueOfString(x.RequestId) + if !f(fd_QueryUniversalReadRequest_request_id, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryUniversalReadRequest) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + return x.RequestId != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadRequest) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + x.RequestId = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryUniversalReadRequest) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + value := x.RequestId + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadRequest) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + x.RequestId = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadRequest) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + panic(fmt.Errorf("field request_id of message ucallback.v1.QueryUniversalReadRequest is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryUniversalReadRequest) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadRequest.request_id": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadRequest does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryUniversalReadRequest) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryUniversalReadRequest", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryUniversalReadRequest) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadRequest) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryUniversalReadRequest) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryUniversalReadRequest) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryUniversalReadRequest) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.RequestId) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryUniversalReadRequest) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.RequestId) > 0 { + i -= len(x.RequestId) + copy(dAtA[i:], x.RequestId) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.RequestId))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryUniversalReadRequest) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryUniversalReadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryUniversalReadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_QueryUniversalReadResponse protoreflect.MessageDescriptor + fd_QueryUniversalReadResponse_read protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryUniversalReadResponse = File_ucallback_v1_query_proto.Messages().ByName("QueryUniversalReadResponse") + fd_QueryUniversalReadResponse_read = md_QueryUniversalReadResponse.Fields().ByName("read") +} + +var _ protoreflect.Message = (*fastReflection_QueryUniversalReadResponse)(nil) + +type fastReflection_QueryUniversalReadResponse QueryUniversalReadResponse + +func (x *QueryUniversalReadResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryUniversalReadResponse)(x) +} + +func (x *QueryUniversalReadResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryUniversalReadResponse_messageType fastReflection_QueryUniversalReadResponse_messageType +var _ protoreflect.MessageType = fastReflection_QueryUniversalReadResponse_messageType{} + +type fastReflection_QueryUniversalReadResponse_messageType struct{} + +func (x fastReflection_QueryUniversalReadResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryUniversalReadResponse)(nil) +} +func (x fastReflection_QueryUniversalReadResponse_messageType) New() protoreflect.Message { + return new(fastReflection_QueryUniversalReadResponse) +} +func (x fastReflection_QueryUniversalReadResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryUniversalReadResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryUniversalReadResponse) Descriptor() protoreflect.MessageDescriptor { + return md_QueryUniversalReadResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryUniversalReadResponse) Type() protoreflect.MessageType { + return _fastReflection_QueryUniversalReadResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryUniversalReadResponse) New() protoreflect.Message { + return new(fastReflection_QueryUniversalReadResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryUniversalReadResponse) Interface() protoreflect.ProtoMessage { + return (*QueryUniversalReadResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryUniversalReadResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Read != nil { + value := protoreflect.ValueOfMessage(x.Read.ProtoReflect()) + if !f(fd_QueryUniversalReadResponse_read, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryUniversalReadResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + return x.Read != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + x.Read = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryUniversalReadResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + value := x.Read + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + x.Read = value.Message().Interface().(*UniversalRead) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + if x.Read == nil { + x.Read = new(UniversalRead) + } + return protoreflect.ValueOfMessage(x.Read.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryUniversalReadResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryUniversalReadResponse.read": + m := new(UniversalRead) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryUniversalReadResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryUniversalReadResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryUniversalReadResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryUniversalReadResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryUniversalReadResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryUniversalReadResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryUniversalReadResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryUniversalReadResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryUniversalReadResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Read != nil { + l = options.Size(x.Read) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryUniversalReadResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Read != nil { + encoded, err := options.Marshal(x.Read) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryUniversalReadResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryUniversalReadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryUniversalReadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Read", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Read == nil { + x.Read = &UniversalRead{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Read); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_QueryReadsByTxRequest protoreflect.MessageDescriptor + fd_QueryReadsByTxRequest_tx_hash protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryReadsByTxRequest = File_ucallback_v1_query_proto.Messages().ByName("QueryReadsByTxRequest") + fd_QueryReadsByTxRequest_tx_hash = md_QueryReadsByTxRequest.Fields().ByName("tx_hash") +} + +var _ protoreflect.Message = (*fastReflection_QueryReadsByTxRequest)(nil) + +type fastReflection_QueryReadsByTxRequest QueryReadsByTxRequest + +func (x *QueryReadsByTxRequest) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryReadsByTxRequest)(x) +} + +func (x *QueryReadsByTxRequest) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryReadsByTxRequest_messageType fastReflection_QueryReadsByTxRequest_messageType +var _ protoreflect.MessageType = fastReflection_QueryReadsByTxRequest_messageType{} + +type fastReflection_QueryReadsByTxRequest_messageType struct{} + +func (x fastReflection_QueryReadsByTxRequest_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryReadsByTxRequest)(nil) +} +func (x fastReflection_QueryReadsByTxRequest_messageType) New() protoreflect.Message { + return new(fastReflection_QueryReadsByTxRequest) +} +func (x fastReflection_QueryReadsByTxRequest_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryReadsByTxRequest +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryReadsByTxRequest) Descriptor() protoreflect.MessageDescriptor { + return md_QueryReadsByTxRequest +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryReadsByTxRequest) Type() protoreflect.MessageType { + return _fastReflection_QueryReadsByTxRequest_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryReadsByTxRequest) New() protoreflect.Message { + return new(fastReflection_QueryReadsByTxRequest) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryReadsByTxRequest) Interface() protoreflect.ProtoMessage { + return (*QueryReadsByTxRequest)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryReadsByTxRequest) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.TxHash != "" { + value := protoreflect.ValueOfString(x.TxHash) + if !f(fd_QueryReadsByTxRequest_tx_hash, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryReadsByTxRequest) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + return x.TxHash != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxRequest) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + x.TxHash = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryReadsByTxRequest) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + value := x.TxHash + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxRequest) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + x.TxHash = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxRequest) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + panic(fmt.Errorf("field tx_hash of message ucallback.v1.QueryReadsByTxRequest is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryReadsByTxRequest) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxRequest.tx_hash": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxRequest")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxRequest does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryReadsByTxRequest) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryReadsByTxRequest", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryReadsByTxRequest) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxRequest) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryReadsByTxRequest) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryReadsByTxRequest) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryReadsByTxRequest) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.TxHash) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryReadsByTxRequest) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.TxHash) > 0 { + i -= len(x.TxHash) + copy(dAtA[i:], x.TxHash) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.TxHash))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryReadsByTxRequest) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryReadsByTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryReadsByTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var _ protoreflect.List = (*_QueryReadsByTxResponse_1_list)(nil) + +type _QueryReadsByTxResponse_1_list struct { + list *[]*UniversalRead +} + +func (x *_QueryReadsByTxResponse_1_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_QueryReadsByTxResponse_1_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_QueryReadsByTxResponse_1_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalRead) + (*x.list)[i] = concreteValue +} + +func (x *_QueryReadsByTxResponse_1_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*UniversalRead) + *x.list = append(*x.list, concreteValue) +} + +func (x *_QueryReadsByTxResponse_1_list) AppendMutable() protoreflect.Value { + v := new(UniversalRead) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_QueryReadsByTxResponse_1_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_QueryReadsByTxResponse_1_list) NewElement() protoreflect.Value { + v := new(UniversalRead) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_QueryReadsByTxResponse_1_list) IsValid() bool { + return x.list != nil +} + +var ( + md_QueryReadsByTxResponse protoreflect.MessageDescriptor + fd_QueryReadsByTxResponse_reads protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_query_proto_init() + md_QueryReadsByTxResponse = File_ucallback_v1_query_proto.Messages().ByName("QueryReadsByTxResponse") + fd_QueryReadsByTxResponse_reads = md_QueryReadsByTxResponse.Fields().ByName("reads") +} + +var _ protoreflect.Message = (*fastReflection_QueryReadsByTxResponse)(nil) + +type fastReflection_QueryReadsByTxResponse QueryReadsByTxResponse + +func (x *QueryReadsByTxResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_QueryReadsByTxResponse)(x) +} + +func (x *QueryReadsByTxResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_query_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_QueryReadsByTxResponse_messageType fastReflection_QueryReadsByTxResponse_messageType +var _ protoreflect.MessageType = fastReflection_QueryReadsByTxResponse_messageType{} + +type fastReflection_QueryReadsByTxResponse_messageType struct{} + +func (x fastReflection_QueryReadsByTxResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_QueryReadsByTxResponse)(nil) +} +func (x fastReflection_QueryReadsByTxResponse_messageType) New() protoreflect.Message { + return new(fastReflection_QueryReadsByTxResponse) +} +func (x fastReflection_QueryReadsByTxResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_QueryReadsByTxResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_QueryReadsByTxResponse) Descriptor() protoreflect.MessageDescriptor { + return md_QueryReadsByTxResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_QueryReadsByTxResponse) Type() protoreflect.MessageType { + return _fastReflection_QueryReadsByTxResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_QueryReadsByTxResponse) New() protoreflect.Message { + return new(fastReflection_QueryReadsByTxResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_QueryReadsByTxResponse) Interface() protoreflect.ProtoMessage { + return (*QueryReadsByTxResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_QueryReadsByTxResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if len(x.Reads) != 0 { + value := protoreflect.ValueOfList(&_QueryReadsByTxResponse_1_list{list: &x.Reads}) + if !f(fd_QueryReadsByTxResponse_reads, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_QueryReadsByTxResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + return len(x.Reads) != 0 + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + x.Reads = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_QueryReadsByTxResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + if len(x.Reads) == 0 { + return protoreflect.ValueOfList(&_QueryReadsByTxResponse_1_list{}) + } + listValue := &_QueryReadsByTxResponse_1_list{list: &x.Reads} + return protoreflect.ValueOfList(listValue) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + lv := value.List() + clv := lv.(*_QueryReadsByTxResponse_1_list) + x.Reads = *clv.list + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + if x.Reads == nil { + x.Reads = []*UniversalRead{} + } + value := &_QueryReadsByTxResponse_1_list{list: &x.Reads} + return protoreflect.ValueOfList(value) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_QueryReadsByTxResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.QueryReadsByTxResponse.reads": + list := []*UniversalRead{} + return protoreflect.ValueOfList(&_QueryReadsByTxResponse_1_list{list: &list}) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.QueryReadsByTxResponse")) + } + panic(fmt.Errorf("message ucallback.v1.QueryReadsByTxResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_QueryReadsByTxResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.QueryReadsByTxResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_QueryReadsByTxResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_QueryReadsByTxResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_QueryReadsByTxResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_QueryReadsByTxResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*QueryReadsByTxResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if len(x.Reads) > 0 { + for _, e := range x.Reads { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*QueryReadsByTxResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.Reads) > 0 { + for iNdEx := len(x.Reads) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.Reads[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*QueryReadsByTxResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryReadsByTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: QueryReadsByTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Reads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Reads = append(x.Reads, &UniversalRead{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Reads[len(x.Reads)-1]); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.0 @@ -1972,6 +3741,147 @@ func (x *QueryAllPendingReadRequestsResponse) GetPagination() *v1beta1.PageRespo return nil } +type QueryUniversalReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` +} + +func (x *QueryUniversalReadRequest) Reset() { + *x = QueryUniversalReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryUniversalReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryUniversalReadRequest) ProtoMessage() {} + +// Deprecated: Use QueryUniversalReadRequest.ProtoReflect.Descriptor instead. +func (*QueryUniversalReadRequest) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{4} +} + +func (x *QueryUniversalReadRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +type QueryUniversalReadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Read *UniversalRead `protobuf:"bytes,1,opt,name=read,proto3" json:"read,omitempty"` +} + +func (x *QueryUniversalReadResponse) Reset() { + *x = QueryUniversalReadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryUniversalReadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryUniversalReadResponse) ProtoMessage() {} + +// Deprecated: Use QueryUniversalReadResponse.ProtoReflect.Descriptor instead. +func (*QueryUniversalReadResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{5} +} + +func (x *QueryUniversalReadResponse) GetRead() *UniversalRead { + if x != nil { + return x.Read + } + return nil +} + +type QueryReadsByTxRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxHash string `protobuf:"bytes,1,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` +} + +func (x *QueryReadsByTxRequest) Reset() { + *x = QueryReadsByTxRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryReadsByTxRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryReadsByTxRequest) ProtoMessage() {} + +// Deprecated: Use QueryReadsByTxRequest.ProtoReflect.Descriptor instead. +func (*QueryReadsByTxRequest) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{6} +} + +func (x *QueryReadsByTxRequest) GetTxHash() string { + if x != nil { + return x.TxHash + } + return "" +} + +type QueryReadsByTxResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Every read the transaction requested, settled or not, in request-id order. + Reads []*UniversalRead `protobuf:"bytes,1,rep,name=reads,proto3" json:"reads,omitempty"` +} + +func (x *QueryReadsByTxResponse) Reset() { + *x = QueryReadsByTxResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_query_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *QueryReadsByTxResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryReadsByTxResponse) ProtoMessage() {} + +// Deprecated: Use QueryReadsByTxResponse.ProtoReflect.Descriptor instead. +func (*QueryReadsByTxResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_query_proto_rawDescGZIP(), []int{7} +} + +func (x *QueryReadsByTxResponse) GetReads() []*UniversalRead { + if x != nil { + return x.Reads + } + return nil +} + var File_ucallback_v1_query_proto protoreflect.FileDescriptor var file_ucallback_v1_query_proto_rawDesc = []byte{ @@ -2010,37 +3920,72 @@ var file_ucallback_v1_query_proto_rawDesc = []byte{ 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x32, 0xa1, 0x02, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x16, 0x12, 0x14, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, - 0x31, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0xaa, 0x01, 0x0a, 0x16, 0x41, 0x6c, 0x6c, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, - 0x12, 0x23, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, - 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, - 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, - 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, - 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, - 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, - 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, - 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6e, 0x22, 0x3a, 0x0a, 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x22, 0x53, 0x0a, + 0x1a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x72, + 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x04, 0x72, 0x65, + 0x61, 0x64, 0x22, 0x30, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x73, + 0x42, 0x79, 0x54, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, + 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x78, + 0x48, 0x61, 0x73, 0x68, 0x22, 0x51, 0x0a, 0x16, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x61, + 0x64, 0x73, 0x42, 0x79, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, + 0x0a, 0x05, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x69, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, + 0x52, 0x05, 0x72, 0x65, 0x61, 0x64, 0x73, 0x32, 0xc0, 0x04, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x12, 0x6b, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x20, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0xaa, + 0x01, 0x0a, 0x16, 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x6c, + 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x41, 0x6c, 0x6c, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x96, 0x01, 0x0a, 0x0d, + 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x12, 0x27, 0x2e, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x55, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x55, 0x6e, 0x69, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x12, 0x2a, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x6c, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x73, 0x42, 0x79, + 0x54, 0x78, 0x12, 0x23, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x73, 0x42, 0x79, 0x54, 0x78, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, + 0x73, 0x42, 0x79, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x74, 0x78, + 0x2f, 0x7b, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x7d, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, + 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, + 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, + 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, + 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2055,31 +4000,41 @@ func file_ucallback_v1_query_proto_rawDescGZIP() []byte { return file_ucallback_v1_query_proto_rawDescData } -var file_ucallback_v1_query_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_ucallback_v1_query_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_ucallback_v1_query_proto_goTypes = []interface{}{ (*QueryParamsRequest)(nil), // 0: ucallback.v1.QueryParamsRequest (*QueryParamsResponse)(nil), // 1: ucallback.v1.QueryParamsResponse (*QueryAllPendingReadRequestsRequest)(nil), // 2: ucallback.v1.QueryAllPendingReadRequestsRequest (*QueryAllPendingReadRequestsResponse)(nil), // 3: ucallback.v1.QueryAllPendingReadRequestsResponse - (*Params)(nil), // 4: ucallback.v1.Params - (*v1beta1.PageRequest)(nil), // 5: cosmos.base.query.v1beta1.PageRequest - (*UniversalRead)(nil), // 6: ucallback.v1.UniversalRead - (*v1beta1.PageResponse)(nil), // 7: cosmos.base.query.v1beta1.PageResponse + (*QueryUniversalReadRequest)(nil), // 4: ucallback.v1.QueryUniversalReadRequest + (*QueryUniversalReadResponse)(nil), // 5: ucallback.v1.QueryUniversalReadResponse + (*QueryReadsByTxRequest)(nil), // 6: ucallback.v1.QueryReadsByTxRequest + (*QueryReadsByTxResponse)(nil), // 7: ucallback.v1.QueryReadsByTxResponse + (*Params)(nil), // 8: ucallback.v1.Params + (*v1beta1.PageRequest)(nil), // 9: cosmos.base.query.v1beta1.PageRequest + (*UniversalRead)(nil), // 10: ucallback.v1.UniversalRead + (*v1beta1.PageResponse)(nil), // 11: cosmos.base.query.v1beta1.PageResponse } var file_ucallback_v1_query_proto_depIdxs = []int32{ - 4, // 0: ucallback.v1.QueryParamsResponse.params:type_name -> ucallback.v1.Params - 5, // 1: ucallback.v1.QueryAllPendingReadRequestsRequest.pagination:type_name -> cosmos.base.query.v1beta1.PageRequest - 6, // 2: ucallback.v1.QueryAllPendingReadRequestsResponse.reads:type_name -> ucallback.v1.UniversalRead - 7, // 3: ucallback.v1.QueryAllPendingReadRequestsResponse.pagination:type_name -> cosmos.base.query.v1beta1.PageResponse - 0, // 4: ucallback.v1.Query.Params:input_type -> ucallback.v1.QueryParamsRequest - 2, // 5: ucallback.v1.Query.AllPendingReadRequests:input_type -> ucallback.v1.QueryAllPendingReadRequestsRequest - 1, // 6: ucallback.v1.Query.Params:output_type -> ucallback.v1.QueryParamsResponse - 3, // 7: ucallback.v1.Query.AllPendingReadRequests:output_type -> ucallback.v1.QueryAllPendingReadRequestsResponse - 6, // [6:8] is the sub-list for method output_type - 4, // [4:6] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 8, // 0: ucallback.v1.QueryParamsResponse.params:type_name -> ucallback.v1.Params + 9, // 1: ucallback.v1.QueryAllPendingReadRequestsRequest.pagination:type_name -> cosmos.base.query.v1beta1.PageRequest + 10, // 2: ucallback.v1.QueryAllPendingReadRequestsResponse.reads:type_name -> ucallback.v1.UniversalRead + 11, // 3: ucallback.v1.QueryAllPendingReadRequestsResponse.pagination:type_name -> cosmos.base.query.v1beta1.PageResponse + 10, // 4: ucallback.v1.QueryUniversalReadResponse.read:type_name -> ucallback.v1.UniversalRead + 10, // 5: ucallback.v1.QueryReadsByTxResponse.reads:type_name -> ucallback.v1.UniversalRead + 0, // 6: ucallback.v1.Query.Params:input_type -> ucallback.v1.QueryParamsRequest + 2, // 7: ucallback.v1.Query.AllPendingReadRequests:input_type -> ucallback.v1.QueryAllPendingReadRequestsRequest + 4, // 8: ucallback.v1.Query.UniversalRead:input_type -> ucallback.v1.QueryUniversalReadRequest + 6, // 9: ucallback.v1.Query.ReadsByTx:input_type -> ucallback.v1.QueryReadsByTxRequest + 1, // 10: ucallback.v1.Query.Params:output_type -> ucallback.v1.QueryParamsResponse + 3, // 11: ucallback.v1.Query.AllPendingReadRequests:output_type -> ucallback.v1.QueryAllPendingReadRequestsResponse + 5, // 12: ucallback.v1.Query.UniversalRead:output_type -> ucallback.v1.QueryUniversalReadResponse + 7, // 13: ucallback.v1.Query.ReadsByTx:output_type -> ucallback.v1.QueryReadsByTxResponse + 10, // [10:14] is the sub-list for method output_type + 6, // [6:10] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_ucallback_v1_query_proto_init() } @@ -2138,6 +4093,54 @@ func file_ucallback_v1_query_proto_init() { return nil } } + file_ucallback_v1_query_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryUniversalReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_query_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryUniversalReadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_query_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryReadsByTxRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_query_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*QueryReadsByTxResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -2145,7 +4148,7 @@ func file_ucallback_v1_query_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ucallback_v1_query_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/api/ucallback/v1/query_grpc.pb.go b/api/ucallback/v1/query_grpc.pb.go index bf4d57d1..20db2bc9 100644 --- a/api/ucallback/v1/query_grpc.pb.go +++ b/api/ucallback/v1/query_grpc.pb.go @@ -21,6 +21,8 @@ const _ = grpc.SupportPackageIsVersion7 const ( Query_Params_FullMethodName = "/ucallback.v1.Query/Params" Query_AllPendingReadRequests_FullMethodName = "/ucallback.v1.Query/AllPendingReadRequests" + Query_UniversalRead_FullMethodName = "/ucallback.v1.Query/UniversalRead" + Query_ReadsByTx_FullMethodName = "/ucallback.v1.Query/ReadsByTx" ) // QueryClient is the client API for Query service. @@ -32,6 +34,11 @@ type QueryClient interface { // AllPendingReadRequests lists read requests still awaiting an observation. // This is the endpoint universal validators poll. AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) + // UniversalRead returns one read by request id, at any point in its lifecycle. + UniversalRead(ctx context.Context, in *QueryUniversalReadRequest, opts ...grpc.CallOption) (*QueryUniversalReadResponse, error) + // ReadsByTxHash returns every read requested by one Push transaction. A single + // transaction can emit several ReadRequested logs; this reassembles that batch. + ReadsByTx(ctx context.Context, in *QueryReadsByTxRequest, opts ...grpc.CallOption) (*QueryReadsByTxResponse, error) } type queryClient struct { @@ -60,6 +67,24 @@ func (c *queryClient) AllPendingReadRequests(ctx context.Context, in *QueryAllPe return out, nil } +func (c *queryClient) UniversalRead(ctx context.Context, in *QueryUniversalReadRequest, opts ...grpc.CallOption) (*QueryUniversalReadResponse, error) { + out := new(QueryUniversalReadResponse) + err := c.cc.Invoke(ctx, Query_UniversalRead_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ReadsByTx(ctx context.Context, in *QueryReadsByTxRequest, opts ...grpc.CallOption) (*QueryReadsByTxResponse, error) { + out := new(QueryReadsByTxResponse) + err := c.cc.Invoke(ctx, Query_ReadsByTx_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. // All implementations must embed UnimplementedQueryServer // for forward compatibility @@ -69,6 +94,11 @@ type QueryServer interface { // AllPendingReadRequests lists read requests still awaiting an observation. // This is the endpoint universal validators poll. AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) + // UniversalRead returns one read by request id, at any point in its lifecycle. + UniversalRead(context.Context, *QueryUniversalReadRequest) (*QueryUniversalReadResponse, error) + // ReadsByTxHash returns every read requested by one Push transaction. A single + // transaction can emit several ReadRequested logs; this reassembles that batch. + ReadsByTx(context.Context, *QueryReadsByTxRequest) (*QueryReadsByTxResponse, error) mustEmbedUnimplementedQueryServer() } @@ -82,6 +112,12 @@ func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*Q func (UnimplementedQueryServer) AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AllPendingReadRequests not implemented") } +func (UnimplementedQueryServer) UniversalRead(context.Context, *QueryUniversalReadRequest) (*QueryUniversalReadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UniversalRead not implemented") +} +func (UnimplementedQueryServer) ReadsByTx(context.Context, *QueryReadsByTxRequest) (*QueryReadsByTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReadsByTx not implemented") +} func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {} // UnsafeQueryServer may be embedded to opt out of forward compatibility for this service. @@ -131,6 +167,42 @@ func _Query_AllPendingReadRequests_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Query_UniversalRead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUniversalReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UniversalRead(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Query_UniversalRead_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UniversalRead(ctx, req.(*QueryUniversalReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ReadsByTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryReadsByTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ReadsByTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Query_ReadsByTx_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ReadsByTx(ctx, req.(*QueryReadsByTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Query_ServiceDesc is the grpc.ServiceDesc for Query service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -146,6 +218,14 @@ var Query_ServiceDesc = grpc.ServiceDesc{ MethodName: "AllPendingReadRequests", Handler: _Query_AllPendingReadRequests_Handler, }, + { + MethodName: "UniversalRead", + Handler: _Query_UniversalRead_Handler, + }, + { + MethodName: "ReadsByTx", + Handler: _Query_ReadsByTx_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ucallback/v1/query.proto", diff --git a/proto/ucallback/v1/query.proto b/proto/ucallback/v1/query.proto index ee99fc82..318992d4 100755 --- a/proto/ucallback/v1/query.proto +++ b/proto/ucallback/v1/query.proto @@ -21,6 +21,17 @@ service Query { rpc AllPendingReadRequests(QueryAllPendingReadRequestsRequest) returns (QueryAllPendingReadRequestsResponse) { option (google.api.http).get = "/ucallback/v1/pending_read_requests"; } + + // UniversalRead returns one read by request id, at any point in its lifecycle. + rpc UniversalRead(QueryUniversalReadRequest) returns (QueryUniversalReadResponse) { + option (google.api.http).get = "/ucallback/v1/universal_reads/{request_id}"; + } + + // ReadsByTxHash returns every read requested by one Push transaction. A single + // transaction can emit several ReadRequested logs; this reassembles that batch. + rpc ReadsByTx(QueryReadsByTxRequest) returns (QueryReadsByTxResponse) { + option (google.api.http).get = "/ucallback/v1/reads_by_tx/{tx_hash}"; + } } // QueryParamsRequest is the request type for the Query/Params RPC method. @@ -43,3 +54,20 @@ message QueryAllPendingReadRequestsResponse { repeated UniversalRead reads = 1 [(gogoproto.nullable) = false]; cosmos.base.query.v1beta1.PageResponse pagination = 2; } + +message QueryUniversalReadRequest { + string request_id = 1; +} + +message QueryUniversalReadResponse { + UniversalRead read = 1 [(gogoproto.nullable) = false]; +} + +message QueryReadsByTxRequest { + string tx_hash = 1; +} + +message QueryReadsByTxResponse { + // Every read the transaction requested, settled or not, in request-id order. + repeated UniversalRead reads = 1 [(gogoproto.nullable) = false]; +} diff --git a/x/ucallback/autocli.go b/x/ucallback/autocli.go index 202b6bba..a8848f53 100755 --- a/x/ucallback/autocli.go +++ b/x/ucallback/autocli.go @@ -21,6 +21,18 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { Use: "pending-read-requests", Short: "List read requests awaiting an observation", }, + { + RpcMethod: "UniversalRead", + Use: "universal-read ", + Short: "Query one read request by id", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "request_id"}}, + }, + { + RpcMethod: "ReadsByTx", + Use: "reads-by-tx ", + Short: "List every read requested by one Push transaction", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "tx_hash"}}, + }, }, }, Tx: &autocliv1.ServiceCommandDescriptor{ diff --git a/x/ucallback/keeper/query_server.go b/x/ucallback/keeper/query_server.go index b0ad608f..4ed65af3 100755 --- a/x/ucallback/keeper/query_server.go +++ b/x/ucallback/keeper/query_server.go @@ -72,3 +72,46 @@ func (k Querier) AllPendingReadRequests(goCtx context.Context, req *types.QueryA Pagination: pageRes, }, nil } + +// UniversalRead implements types.QueryServer. +// +// Serves a read at any point in its lifecycle, settled or not — this is the +// endpoint for "what happened to my request", so it must not filter the way +// AllPendingReadRequests does. +func (k Querier) UniversalRead(goCtx context.Context, req *types.QueryUniversalReadRequest) (*types.QueryUniversalReadResponse, error) { + if req == nil || req.RequestId == "" { + return nil, status.Error(codes.InvalidArgument, "request_id is required") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + ur, found := k.Keeper.GetUniversalRead(ctx, req.RequestId) + if !found { + return nil, status.Errorf(codes.NotFound, "no read request with id %s", req.RequestId) + } + + return &types.QueryUniversalReadResponse{Read: ur}, nil +} + +// ReadsByTx implements types.QueryServer. +// +// Returns every read a single Push transaction requested, settled or not. Batches +// are the reason this exists: one transaction can emit several ReadRequested logs, +// each becoming an independent record that settles on its own schedule. +// +// Unpaginated by design — the fan-out is bounded by what fits in one transaction. +func (k Querier) ReadsByTx(goCtx context.Context, req *types.QueryReadsByTxRequest) (*types.QueryReadsByTxResponse, error) { + if req == nil || req.TxHash == "" { + return nil, status.Error(codes.InvalidArgument, "tx_hash is required") + } + ctx := sdk.UnwrapSDKContext(goCtx) + + reads := []types.UniversalRead{} + if err := k.Keeper.IterateReadsByTxHash(ctx, req.TxHash, func(ur types.UniversalRead) bool { + reads = append(reads, ur) + return true + }); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryReadsByTxResponse{Reads: reads}, nil +} diff --git a/x/ucallback/keeper/query_server_test.go b/x/ucallback/keeper/query_server_test.go index 1e2a0c64..b16c06d7 100644 --- a/x/ucallback/keeper/query_server_test.go +++ b/x/ucallback/keeper/query_server_test.go @@ -65,3 +65,67 @@ func TestAllPendingReadRequests_NilRequest(t *testing.T) { _, err := f.queryServer.AllPendingReadRequests(f.ctx, nil) require.Error(t, err) } + +// A read is served at any lifecycle stage — this endpoint answers "what happened +// to my request", so unlike the pending list it must not filter. +func TestUniversalRead_ServesSettledAndExpired(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(100) + + for id, st := range map[string]types.UniversalReadStatus{ + "0xpending": types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, + "0xdone": types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED, + "0xgone": types.UniversalReadStatus_UNIVERSAL_READ_STATUS_EXPIRED, + } { + require.NoError(t, f.k.SetUniversalRead(f.ctx, newRead(id, "0xTX", 50, st))) + } + + for _, id := range []string{"0xpending", "0xdone", "0xgone"} { + res, err := f.queryServer.UniversalRead(f.ctx, + &types.QueryUniversalReadRequest{RequestId: id}) + require.NoError(t, err, id) + require.Equal(t, id, res.Read.Id) + } + + // ...even though only one of them is visible to validators + require.Empty(t, pendingIDs(t, f)) +} + +func TestUniversalRead_NotFound(t *testing.T) { + f := SetupTest(t) + + _, err := f.queryServer.UniversalRead(f.ctx, + &types.QueryUniversalReadRequest{RequestId: "0xmissing"}) + require.Error(t, err) + + _, err = f.queryServer.UniversalRead(f.ctx, &types.QueryUniversalReadRequest{}) + require.Error(t, err, "empty request_id is rejected, not treated as not-found") +} + +// The batch view returns siblings regardless of how each one settled. +func TestReadsByTx_ReturnsWholeBatch(t *testing.T) { + f := SetupTest(t) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xaaa", "0xBATCH", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xbbb", "0xBATCH", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED))) + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead("0xccc", "0xOTHER", 500, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) + + res, err := f.queryServer.ReadsByTx(f.ctx, &types.QueryReadsByTxRequest{TxHash: "0xBATCH"}) + require.NoError(t, err) + ids := []string{} + for _, r := range res.Reads { + ids = append(ids, r.Id) + } + require.ElementsMatch(t, []string{"0xaaa", "0xbbb"}, ids) + + // an unknown tx is an empty batch, not an error + res, err = f.queryServer.ReadsByTx(f.ctx, &types.QueryReadsByTxRequest{TxHash: "0xNOPE"}) + require.NoError(t, err) + require.Empty(t, res.Reads) + + _, err = f.queryServer.ReadsByTx(f.ctx, &types.QueryReadsByTxRequest{}) + require.Error(t, err) +} diff --git a/x/ucallback/types/query.pb.go b/x/ucallback/types/query.pb.go index 4a9d7e74..cd789ad1 100644 --- a/x/ucallback/types/query.pb.go +++ b/x/ucallback/types/query.pb.go @@ -212,46 +212,237 @@ func (m *QueryAllPendingReadRequestsResponse) GetPagination() *query.PageRespons return nil } +type QueryUniversalReadRequest struct { + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` +} + +func (m *QueryUniversalReadRequest) Reset() { *m = QueryUniversalReadRequest{} } +func (m *QueryUniversalReadRequest) String() string { return proto.CompactTextString(m) } +func (*QueryUniversalReadRequest) ProtoMessage() {} +func (*QueryUniversalReadRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{4} +} +func (m *QueryUniversalReadRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUniversalReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUniversalReadRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryUniversalReadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUniversalReadRequest.Merge(m, src) +} +func (m *QueryUniversalReadRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryUniversalReadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUniversalReadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUniversalReadRequest proto.InternalMessageInfo + +func (m *QueryUniversalReadRequest) GetRequestId() string { + if m != nil { + return m.RequestId + } + return "" +} + +type QueryUniversalReadResponse struct { + Read UniversalRead `protobuf:"bytes,1,opt,name=read,proto3" json:"read"` +} + +func (m *QueryUniversalReadResponse) Reset() { *m = QueryUniversalReadResponse{} } +func (m *QueryUniversalReadResponse) String() string { return proto.CompactTextString(m) } +func (*QueryUniversalReadResponse) ProtoMessage() {} +func (*QueryUniversalReadResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{5} +} +func (m *QueryUniversalReadResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryUniversalReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryUniversalReadResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryUniversalReadResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryUniversalReadResponse.Merge(m, src) +} +func (m *QueryUniversalReadResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryUniversalReadResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryUniversalReadResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryUniversalReadResponse proto.InternalMessageInfo + +func (m *QueryUniversalReadResponse) GetRead() UniversalRead { + if m != nil { + return m.Read + } + return UniversalRead{} +} + +type QueryReadsByTxRequest struct { + TxHash string `protobuf:"bytes,1,opt,name=tx_hash,json=txHash,proto3" json:"tx_hash,omitempty"` +} + +func (m *QueryReadsByTxRequest) Reset() { *m = QueryReadsByTxRequest{} } +func (m *QueryReadsByTxRequest) String() string { return proto.CompactTextString(m) } +func (*QueryReadsByTxRequest) ProtoMessage() {} +func (*QueryReadsByTxRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{6} +} +func (m *QueryReadsByTxRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReadsByTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReadsByTxRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReadsByTxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReadsByTxRequest.Merge(m, src) +} +func (m *QueryReadsByTxRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryReadsByTxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReadsByTxRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReadsByTxRequest proto.InternalMessageInfo + +func (m *QueryReadsByTxRequest) GetTxHash() string { + if m != nil { + return m.TxHash + } + return "" +} + +type QueryReadsByTxResponse struct { + // Every read the transaction requested, settled or not, in request-id order. + Reads []UniversalRead `protobuf:"bytes,1,rep,name=reads,proto3" json:"reads"` +} + +func (m *QueryReadsByTxResponse) Reset() { *m = QueryReadsByTxResponse{} } +func (m *QueryReadsByTxResponse) String() string { return proto.CompactTextString(m) } +func (*QueryReadsByTxResponse) ProtoMessage() {} +func (*QueryReadsByTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a64b97cfcca36b9d, []int{7} +} +func (m *QueryReadsByTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryReadsByTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryReadsByTxResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryReadsByTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryReadsByTxResponse.Merge(m, src) +} +func (m *QueryReadsByTxResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryReadsByTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryReadsByTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryReadsByTxResponse proto.InternalMessageInfo + +func (m *QueryReadsByTxResponse) GetReads() []UniversalRead { + if m != nil { + return m.Reads + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "ucallback.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "ucallback.v1.QueryParamsResponse") proto.RegisterType((*QueryAllPendingReadRequestsRequest)(nil), "ucallback.v1.QueryAllPendingReadRequestsRequest") proto.RegisterType((*QueryAllPendingReadRequestsResponse)(nil), "ucallback.v1.QueryAllPendingReadRequestsResponse") + proto.RegisterType((*QueryUniversalReadRequest)(nil), "ucallback.v1.QueryUniversalReadRequest") + proto.RegisterType((*QueryUniversalReadResponse)(nil), "ucallback.v1.QueryUniversalReadResponse") + proto.RegisterType((*QueryReadsByTxRequest)(nil), "ucallback.v1.QueryReadsByTxRequest") + proto.RegisterType((*QueryReadsByTxResponse)(nil), "ucallback.v1.QueryReadsByTxResponse") } func init() { proto.RegisterFile("ucallback/v1/query.proto", fileDescriptor_a64b97cfcca36b9d) } var fileDescriptor_a64b97cfcca36b9d = []byte{ - // 457 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x4f, 0x6b, 0xd4, 0x40, - 0x14, 0xdf, 0xac, 0x76, 0x0f, 0x53, 0x4f, 0xe3, 0x52, 0x4a, 0x2c, 0xb1, 0xa6, 0xa8, 0x45, 0xed, - 0x8c, 0x59, 0x41, 0xcf, 0x56, 0xd0, 0x6b, 0x0c, 0x78, 0xf1, 0x52, 0x26, 0xc9, 0x63, 0x76, 0x68, - 0x76, 0x26, 0xcd, 0x24, 0xc1, 0x5e, 0xfd, 0x04, 0x82, 0x9f, 0xc0, 0x93, 0xe0, 0x27, 0xe9, 0xb1, - 0xe0, 0xc5, 0x93, 0xc8, 0xae, 0x1f, 0x44, 0x32, 0x33, 0xd5, 0x44, 0xcb, 0x4a, 0x6f, 0x8f, 0x79, - 0xbf, 0xf7, 0xfb, 0xf3, 0xde, 0xa0, 0xed, 0x26, 0x63, 0x45, 0x91, 0xb2, 0xec, 0x98, 0xb6, 0x11, - 0x3d, 0x69, 0xa0, 0x3a, 0x25, 0x65, 0xa5, 0x6a, 0x85, 0x6f, 0xfc, 0xee, 0x90, 0x36, 0xf2, 0xa7, - 0x5c, 0x71, 0x65, 0x1a, 0xb4, 0xab, 0x2c, 0xc6, 0xdf, 0xe1, 0x4a, 0xf1, 0x02, 0x28, 0x2b, 0x05, - 0x65, 0x52, 0xaa, 0x9a, 0xd5, 0x42, 0x49, 0xed, 0xba, 0x0f, 0x32, 0xa5, 0x17, 0x4a, 0xd3, 0x94, - 0x69, 0xb0, 0xd4, 0xb4, 0x8d, 0x52, 0xa8, 0x59, 0x44, 0x4b, 0xc6, 0x85, 0x34, 0x60, 0x87, 0xf5, - 0x07, 0x3e, 0x38, 0x48, 0xd0, 0xe2, 0x82, 0x67, 0xe8, 0xb1, 0x3e, 0x2d, 0xc1, 0x75, 0xc2, 0x29, - 0xc2, 0xaf, 0x3b, 0xde, 0x98, 0x55, 0x6c, 0xa1, 0x13, 0x38, 0x69, 0x40, 0xd7, 0xe1, 0x0b, 0x74, - 0x73, 0xf0, 0xaa, 0x4b, 0x25, 0x35, 0xe0, 0x47, 0x68, 0x52, 0x9a, 0x97, 0x6d, 0x6f, 0xd7, 0xdb, - 0xdf, 0x9c, 0x4d, 0x49, 0x3f, 0x21, 0x71, 0x68, 0x87, 0x09, 0x0b, 0x14, 0x1a, 0x92, 0xe7, 0x45, - 0x11, 0x83, 0xcc, 0x85, 0xe4, 0x09, 0xb0, 0xdc, 0x49, 0x5c, 0x48, 0xe1, 0x97, 0x08, 0xfd, 0x89, - 0xe2, 0x78, 0xef, 0x11, 0x9b, 0x9b, 0x74, 0xb9, 0x89, 0x5d, 0xa9, 0xcb, 0x4d, 0x62, 0xc6, 0xc1, - 0xcd, 0x26, 0xbd, 0xc9, 0xf0, 0xb3, 0x87, 0xf6, 0xd6, 0xca, 0xb9, 0x0c, 0xcf, 0xd0, 0x46, 0x05, - 0x2c, 0xef, 0x22, 0x5c, 0xdb, 0xdf, 0x9c, 0xdd, 0x1a, 0x46, 0x78, 0x23, 0x45, 0x0b, 0x95, 0x66, - 0x45, 0x37, 0x7b, 0x78, 0xfd, 0xec, 0xfb, 0xed, 0x51, 0x62, 0xf1, 0xf8, 0xd5, 0xc0, 0xe8, 0xd8, - 0x18, 0xbd, 0xff, 0x5f, 0xa3, 0x56, 0xb5, 0xef, 0x74, 0xf6, 0x69, 0x8c, 0x36, 0x8c, 0x53, 0x7c, - 0x8c, 0x26, 0x76, 0x67, 0x78, 0x77, 0x68, 0xe3, 0xdf, 0x93, 0xf8, 0x77, 0xd6, 0x20, 0xac, 0x48, - 0xb8, 0xf3, 0xfe, 0xeb, 0xcf, 0x8f, 0xe3, 0x2d, 0x3c, 0xa5, 0x83, 0x73, 0xdb, 0x73, 0xe0, 0x2f, - 0x1e, 0xda, 0xba, 0x7c, 0x37, 0xf8, 0xf1, 0x25, 0xdc, 0x6b, 0xaf, 0xe6, 0x47, 0x57, 0x98, 0x70, - 0xee, 0x1e, 0x1a, 0x77, 0x77, 0xf1, 0xde, 0x5f, 0xee, 0xec, 0xc8, 0x51, 0xb7, 0xe4, 0xa3, 0xca, - 0x0d, 0x1d, 0xc6, 0x67, 0xcb, 0xc0, 0x3b, 0x5f, 0x06, 0xde, 0x8f, 0x65, 0xe0, 0x7d, 0x58, 0x05, - 0xa3, 0xf3, 0x55, 0x30, 0xfa, 0xb6, 0x0a, 0x46, 0x6f, 0x9f, 0x72, 0x51, 0xcf, 0x9b, 0x94, 0x64, - 0x6a, 0x41, 0xcb, 0x46, 0xcf, 0xb3, 0x39, 0x13, 0xd2, 0x54, 0x07, 0xa6, 0x3c, 0x90, 0x2a, 0x07, - 0xfa, 0xae, 0x27, 0x62, 0xbe, 0x7b, 0x3a, 0x31, 0xff, 0xfd, 0xc9, 0xaf, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xb6, 0x88, 0xa6, 0x77, 0xaf, 0x03, 0x00, 0x00, + // 614 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0x9b, 0xd1, 0x15, 0xd5, 0x83, 0x8b, 0x29, 0x65, 0x84, 0x11, 0x46, 0x0a, 0xac, 0x1a, + 0x5b, 0xbc, 0x16, 0x01, 0x12, 0x37, 0x8a, 0xc4, 0x9f, 0x5b, 0x57, 0xe0, 0xc2, 0x25, 0x72, 0x1a, + 0x2b, 0x89, 0x96, 0xc6, 0x59, 0x9c, 0x54, 0xad, 0xa6, 0x5d, 0xe0, 0x0b, 0x20, 0x21, 0xf1, 0x15, + 0x90, 0xf8, 0x14, 0x1c, 0x77, 0x9c, 0xc4, 0x85, 0x13, 0x42, 0x2d, 0x1f, 0x04, 0xc5, 0x71, 0xb6, + 0xba, 0x44, 0x1d, 0x68, 0x37, 0xcb, 0x7e, 0xde, 0xf7, 0xfd, 0x3d, 0xaf, 0x9e, 0x04, 0xac, 0x26, + 0x7d, 0xec, 0xfb, 0x16, 0xee, 0xef, 0xa1, 0x61, 0x0b, 0xed, 0x27, 0x24, 0x1a, 0x1b, 0x61, 0x44, + 0x63, 0x0a, 0x2f, 0x9d, 0xbc, 0x18, 0xc3, 0x96, 0x5a, 0x73, 0xa8, 0x43, 0xf9, 0x03, 0x4a, 0x4f, + 0x99, 0x46, 0x5d, 0x73, 0x28, 0x75, 0x7c, 0x82, 0x70, 0xe8, 0x21, 0x1c, 0x04, 0x34, 0xc6, 0xb1, + 0x47, 0x03, 0x26, 0x5e, 0x37, 0xfb, 0x94, 0x0d, 0x28, 0x43, 0x16, 0x66, 0x24, 0x6b, 0x8d, 0x86, + 0x2d, 0x8b, 0xc4, 0xb8, 0x85, 0x42, 0xec, 0x78, 0x01, 0x17, 0x0b, 0xad, 0x2a, 0x71, 0x38, 0x24, + 0x20, 0xcc, 0xcb, 0xfb, 0xc8, 0x8c, 0xf1, 0x38, 0x24, 0xe2, 0x45, 0xaf, 0x01, 0xb8, 0x9b, 0xf6, + 0xed, 0xe2, 0x08, 0x0f, 0x58, 0x8f, 0xec, 0x27, 0x84, 0xc5, 0xfa, 0x33, 0x70, 0x45, 0xba, 0x65, + 0x21, 0x0d, 0x18, 0x81, 0x5b, 0xa0, 0x12, 0xf2, 0x9b, 0x55, 0x65, 0x5d, 0x69, 0xae, 0xb4, 0x6b, + 0xc6, 0xac, 0x43, 0x43, 0xa8, 0x85, 0x46, 0xf7, 0x81, 0xce, 0x9b, 0x3c, 0xf5, 0xfd, 0x2e, 0x09, + 0x6c, 0x2f, 0x70, 0x7a, 0x04, 0xdb, 0x62, 0x44, 0x3e, 0x0a, 0x3e, 0x07, 0xe0, 0xd4, 0x8a, 0xe8, + 0x7b, 0xcf, 0xc8, 0x7c, 0x1b, 0xa9, 0x6f, 0x23, 0x5b, 0xa9, 0xf0, 0x6d, 0x74, 0xb1, 0x43, 0x44, + 0x6d, 0x6f, 0xa6, 0x52, 0xff, 0xa2, 0x80, 0xc6, 0xc2, 0x71, 0xc2, 0xc3, 0x63, 0xb0, 0x1c, 0x11, + 0x6c, 0xa7, 0x16, 0x2e, 0x34, 0x57, 0xda, 0x37, 0x64, 0x0b, 0x6f, 0x03, 0x6f, 0x48, 0x22, 0x86, + 0xfd, 0xb4, 0xb6, 0x53, 0x3e, 0xfa, 0x79, 0xab, 0xd4, 0xcb, 0xf4, 0xf0, 0x85, 0x04, 0xba, 0xc4, + 0x41, 0x37, 0xce, 0x04, 0xcd, 0xa6, 0x4a, 0xa4, 0x4f, 0xc0, 0x75, 0x0e, 0x2a, 0xcd, 0xca, 0xd7, + 0x71, 0x13, 0x80, 0x28, 0x3b, 0x9a, 0x9e, 0xcd, 0xd7, 0x51, 0xed, 0x55, 0xc5, 0xcd, 0x2b, 0x5b, + 0x7f, 0x0d, 0xd4, 0xa2, 0x5a, 0xe1, 0xed, 0x21, 0x28, 0xa7, 0xac, 0x62, 0x8b, 0xff, 0x60, 0x8d, + 0xcb, 0xf5, 0x1d, 0x70, 0x95, 0x37, 0x4d, 0x1f, 0x58, 0x67, 0xfc, 0x66, 0x94, 0xc3, 0x5c, 0x03, + 0x17, 0xe3, 0x91, 0xe9, 0x62, 0xe6, 0x0a, 0x92, 0x4a, 0x3c, 0x7a, 0x89, 0x99, 0xab, 0xef, 0x82, + 0xfa, 0x7c, 0xc5, 0x39, 0xd7, 0xdb, 0xfe, 0x56, 0x06, 0xcb, 0xbc, 0x27, 0xdc, 0x03, 0x95, 0x2c, + 0x49, 0x70, 0x5d, 0xae, 0xfe, 0x3b, 0xa8, 0xea, 0xed, 0x05, 0x8a, 0x8c, 0x48, 0x5f, 0x7b, 0xff, + 0xfd, 0xf7, 0xa7, 0xa5, 0x3a, 0xac, 0x21, 0xe9, 0x23, 0xc8, 0x42, 0x0a, 0xbf, 0x2a, 0xa0, 0x5e, + 0x9c, 0x18, 0xb8, 0x53, 0xd0, 0x7b, 0x61, 0x96, 0xd5, 0xd6, 0x7f, 0x54, 0x08, 0xba, 0xfb, 0x9c, + 0xee, 0x2e, 0x6c, 0xcc, 0xd1, 0x65, 0x25, 0x66, 0xba, 0x1b, 0x33, 0xca, 0x89, 0x3e, 0x2b, 0xe0, + 0xb2, 0xb4, 0x42, 0xb8, 0x51, 0x30, 0xb1, 0x28, 0x57, 0x6a, 0xf3, 0x6c, 0xa1, 0x20, 0x6a, 0x73, + 0xa2, 0x2d, 0xb8, 0x29, 0x13, 0x25, 0xb9, 0x98, 0x33, 0x31, 0x74, 0x70, 0x1a, 0xd3, 0x43, 0xf8, + 0x41, 0x01, 0xd5, 0x93, 0x2c, 0xc0, 0x46, 0xc1, 0xac, 0xf9, 0x6c, 0xa9, 0x77, 0x16, 0x8b, 0x16, + 0xaf, 0x87, 0x23, 0x98, 0xd6, 0xd8, 0x8c, 0x47, 0xe8, 0x40, 0x44, 0xf4, 0xb0, 0xd3, 0x3d, 0x9a, + 0x68, 0xca, 0xf1, 0x44, 0x53, 0x7e, 0x4d, 0x34, 0xe5, 0xe3, 0x54, 0x2b, 0x1d, 0x4f, 0xb5, 0xd2, + 0x8f, 0xa9, 0x56, 0x7a, 0xf7, 0xc8, 0xf1, 0x62, 0x37, 0xb1, 0x8c, 0x3e, 0x1d, 0xa0, 0x30, 0x61, + 0x6e, 0xdf, 0xc5, 0x5e, 0xc0, 0x4f, 0xdb, 0xfc, 0xb8, 0x1d, 0x50, 0x9b, 0xa0, 0xd1, 0xcc, 0x10, + 0xfe, 0x8f, 0xb4, 0x2a, 0xfc, 0x27, 0xf9, 0xe0, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x65, + 0x30, 0x7f, 0xe4, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -271,6 +462,11 @@ type QueryClient interface { // AllPendingReadRequests lists read requests still awaiting an observation. // This is the endpoint universal validators poll. AllPendingReadRequests(ctx context.Context, in *QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*QueryAllPendingReadRequestsResponse, error) + // UniversalRead returns one read by request id, at any point in its lifecycle. + UniversalRead(ctx context.Context, in *QueryUniversalReadRequest, opts ...grpc.CallOption) (*QueryUniversalReadResponse, error) + // ReadsByTxHash returns every read requested by one Push transaction. A single + // transaction can emit several ReadRequested logs; this reassembles that batch. + ReadsByTx(ctx context.Context, in *QueryReadsByTxRequest, opts ...grpc.CallOption) (*QueryReadsByTxResponse, error) } type queryClient struct { @@ -299,6 +495,24 @@ func (c *queryClient) AllPendingReadRequests(ctx context.Context, in *QueryAllPe return out, nil } +func (c *queryClient) UniversalRead(ctx context.Context, in *QueryUniversalReadRequest, opts ...grpc.CallOption) (*QueryUniversalReadResponse, error) { + out := new(QueryUniversalReadResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Query/UniversalRead", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) ReadsByTx(ctx context.Context, in *QueryReadsByTxRequest, opts ...grpc.CallOption) (*QueryReadsByTxResponse, error) { + out := new(QueryReadsByTxResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Query/ReadsByTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Params queries all parameters of the module. @@ -306,6 +520,11 @@ type QueryServer interface { // AllPendingReadRequests lists read requests still awaiting an observation. // This is the endpoint universal validators poll. AllPendingReadRequests(context.Context, *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) + // UniversalRead returns one read by request id, at any point in its lifecycle. + UniversalRead(context.Context, *QueryUniversalReadRequest) (*QueryUniversalReadResponse, error) + // ReadsByTxHash returns every read requested by one Push transaction. A single + // transaction can emit several ReadRequested logs; this reassembles that batch. + ReadsByTx(context.Context, *QueryReadsByTxRequest) (*QueryReadsByTxResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -318,6 +537,12 @@ func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsReq func (*UnimplementedQueryServer) AllPendingReadRequests(ctx context.Context, req *QueryAllPendingReadRequestsRequest) (*QueryAllPendingReadRequestsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AllPendingReadRequests not implemented") } +func (*UnimplementedQueryServer) UniversalRead(ctx context.Context, req *QueryUniversalReadRequest) (*QueryUniversalReadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UniversalRead not implemented") +} +func (*UnimplementedQueryServer) ReadsByTx(ctx context.Context, req *QueryReadsByTxRequest) (*QueryReadsByTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReadsByTx not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -359,6 +584,42 @@ func _Query_AllPendingReadRequests_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Query_UniversalRead_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryUniversalReadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).UniversalRead(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Query/UniversalRead", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).UniversalRead(ctx, req.(*QueryUniversalReadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_ReadsByTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryReadsByTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ReadsByTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Query/ReadsByTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ReadsByTx(ctx, req.(*QueryReadsByTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "ucallback.v1.Query", HandlerType: (*QueryServer)(nil), @@ -371,6 +632,14 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "AllPendingReadRequests", Handler: _Query_AllPendingReadRequests_Handler, }, + { + MethodName: "UniversalRead", + Handler: _Query_UniversalRead_Handler, + }, + { + MethodName: "ReadsByTx", + Handler: _Query_ReadsByTx_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ucallback/v1/query.proto", @@ -518,6 +787,136 @@ func (m *QueryAllPendingReadRequestsResponse) MarshalToSizedBuffer(dAtA []byte) return len(dAtA) - i, nil } +func (m *QueryUniversalReadRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryUniversalReadRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUniversalReadRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RequestId) > 0 { + i -= len(m.RequestId) + copy(dAtA[i:], m.RequestId) + i = encodeVarintQuery(dAtA, i, uint64(len(m.RequestId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryUniversalReadResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryUniversalReadResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryUniversalReadResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Read.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryReadsByTxRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReadsByTxRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReadsByTxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TxHash) > 0 { + i -= len(m.TxHash) + copy(dAtA[i:], m.TxHash) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TxHash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryReadsByTxResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryReadsByTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryReadsByTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Reads) > 0 { + for iNdEx := len(m.Reads) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Reads[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -583,13 +982,65 @@ func (m *QueryAllPendingReadRequestsResponse) Size() (n int) { return n } -func sovQuery(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozQuery(x uint64) (n int) { - return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { +func (m *QueryUniversalReadRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RequestId) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryUniversalReadResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Read.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryReadsByTxRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TxHash) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryReadsByTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Reads) > 0 { + for _, e := range m.Reads { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -931,6 +1382,337 @@ func (m *QueryAllPendingReadRequestsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryUniversalReadRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryUniversalReadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUniversalReadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryUniversalReadResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryUniversalReadResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryUniversalReadResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Read", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Read.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReadsByTxRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReadsByTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReadsByTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxHash", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxHash = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryReadsByTxResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryReadsByTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryReadsByTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reads = append(m.Reads, UniversalRead{}) + if err := m.Reads[len(m.Reads)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/ucallback/types/query.pb.gw.go b/x/ucallback/types/query.pb.gw.go index 18acef35..afad9fe3 100644 --- a/x/ucallback/types/query.pb.gw.go +++ b/x/ucallback/types/query.pb.gw.go @@ -87,6 +87,114 @@ func local_request_Query_AllPendingReadRequests_0(ctx context.Context, marshaler } +func request_Query_UniversalRead_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUniversalReadRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["request_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id") + } + + protoReq.RequestId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err) + } + + msg, err := client.UniversalRead(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_UniversalRead_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryUniversalReadRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["request_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "request_id") + } + + protoReq.RequestId, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "request_id", err) + } + + msg, err := server.UniversalRead(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_ReadsByTx_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReadsByTxRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["tx_hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "tx_hash") + } + + protoReq.TxHash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "tx_hash", err) + } + + msg, err := client.ReadsByTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ReadsByTx_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryReadsByTxRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["tx_hash"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "tx_hash") + } + + protoReq.TxHash, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "tx_hash", err) + } + + msg, err := server.ReadsByTx(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -139,6 +247,52 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_UniversalRead_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_UniversalRead_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_UniversalRead_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ReadsByTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ReadsByTx_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ReadsByTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -220,6 +374,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_UniversalRead_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_UniversalRead_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_UniversalRead_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_ReadsByTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ReadsByTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ReadsByTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -227,10 +421,18 @@ var ( pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"ucallback", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_AllPendingReadRequests_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"ucallback", "v1", "pending_read_requests"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_UniversalRead_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"ucallback", "v1", "universal_reads", "request_id"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ReadsByTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"ucallback", "v1", "reads_by_tx", "tx_hash"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_Params_0 = runtime.ForwardResponseMessage forward_Query_AllPendingReadRequests_0 = runtime.ForwardResponseMessage + + forward_Query_UniversalRead_0 = runtime.ForwardResponseMessage + + forward_Query_ReadsByTx_0 = runtime.ForwardResponseMessage ) From 5f2b5e866430910caaa749bae40b69da1428bcda Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:29:27 +0530 Subject: [PATCH 37/54] feat(uregistry): name 0xC2 as UNIVERSAL_CALLBACK --- x/uregistry/types/constants.go | 23 ++++++++++++++++- x/uregistry/types/constants_test.go | 38 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/x/uregistry/types/constants.go b/x/uregistry/types/constants.go index 95d5f9e1..68d1499e 100644 --- a/x/uregistry/types/constants.go +++ b/x/uregistry/types/constants.go @@ -51,6 +51,16 @@ var SYSTEM_CONTRACTS = map[string]ContractAddresses{ ProxyAdmin: "0xF2000000000000000000000000000000000000C1", Implementation: "0xF1000000000000000000000000000000000000C1", }, + // UNIVERSAL_CALLBACK is the read-request contract x/ucallback listens to. + // Promoted out of the RESERVED_C2 auto-reservation below: same address, same + // admin, same implementation, and (until the real contract ships) the same + // placeholder bytecode — so genesis state is byte-identical to before the + // rename, and chains that already deployed 0xC2 skip it as already-deployed. + "UNIVERSAL_CALLBACK": { + Address: "0x00000000000000000000000000000000000000C2", + ProxyAdmin: "0xF2000000000000000000000000000000000000C2", + Implementation: "0xF1000000000000000000000000000000000000C2", + }, "VAULT_PC": { Address: "0x00000000000000000000000000000000000000B0", ProxyAdmin: "0xF2000000000000000000000000000000000000b0", @@ -142,7 +152,8 @@ func reservedProxyBytecode(slotByte byte) []byte { // Range policy: // - 0xA0-0xAF: Proxy Admins / low-level modules (0xAA pre-occupied by uexecutor) // - 0xB0-0xBF: Vaults + utility (0xB0 = VAULT_PC; 0xB1 = VAULT_PC20; 0xB2 = RESERVED_2; 0xBC = UNIVERSAL_BATCH_CALL) -// - 0xC0-0xCF: Chain abstraction (0xC0 = UNIVERSAL_CORE; 0xC1 = UNIVERSAL_GATEWAY_PC) +// - 0xC0-0xCF: Chain abstraction (0xC0 = UNIVERSAL_CORE; 0xC1 = UNIVERSAL_GATEWAY_PC; +// 0xC2 = UNIVERSAL_CALLBACK) // - 0xD0-0xFF: NOT reserved — left to other chains / future debug use // // Choice of full triples (vs bytecode-only): future activation of a reserved @@ -154,6 +165,16 @@ func init() { 0xB0: true, 0xB1: true, 0xB2: true, // VAULT_PC / VAULT_PC20 / RESERVED_2 0xBC: true, 0xC0: true, 0xC1: true, // UNIVERSAL_CORE, UNIVERSAL_GATEWAY_PC + 0xC2: true, // UNIVERSAL_CALLBACK + } + + // Placeholder bytecode for UNIVERSAL_CALLBACK, identical to what RESERVED_C2 + // carried before the promotion. Replaced with the real compiled contract in + // the commit that deploys it. + BYTECODE["UNIVERSAL_CALLBACK"] = ByteCodes{ + IMPL_RUNTIME: ReservedImplRuntimeBytecode, + PROXY_RUNTIME: reservedProxyBytecode(0xC2), + ADMIN_RUNTIME: ProxyAdminRuntimeBytecode, } for _, hi := range []byte{0xA, 0xB, 0xC} { diff --git a/x/uregistry/types/constants_test.go b/x/uregistry/types/constants_test.go index 83183626..5f345bda 100644 --- a/x/uregistry/types/constants_test.go +++ b/x/uregistry/types/constants_test.go @@ -22,6 +22,7 @@ func TestReservedSlots_FullTripleDeployedForEveryUnoccupiedABCSlot(t *testing.T) 0xAA: true, 0xB0: true, 0xB1: true, 0xB2: true, 0xBC: true, 0xC0: true, 0xC1: true, + 0xC2: true, // promoted to UNIVERSAL_CALLBACK; covered by the test below } for _, hi := range []byte{0xA, 0xB, 0xC} { @@ -166,3 +167,40 @@ func TestReservedSlots_BytecodeIsCaseInsensitiveAcrossSlots(t *testing.T) { require.Equal(t, src, upperBytes, "UPPERCASE hex must decode to identical bytes (case-insensitive)") require.Equal(t, src, mixedBytes, "MiXeD hex must decode to identical bytes (case-insensitive)") } + +// TestUniversalCallbackSlot_KeepsReservedTriple asserts that promoting 0xC2 out of +// the RESERVED_* auto-reservation did not weaken it. The slot must still carry a +// complete proxy + admin + impl triple with the same addresses RESERVED_C2 had, so +// the promotion is a rename and genesis state is unchanged. +func TestUniversalCallbackSlot_KeepsReservedTriple(t *testing.T) { + addrs, ok := SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"] + require.True(t, ok, "UNIVERSAL_CALLBACK missing from SYSTEM_CONTRACTS") + + require.Equal(t, "0x00000000000000000000000000000000000000c2", strings.ToLower(addrs.Address)) + require.Equal(t, "0xf2000000000000000000000000000000000000c2", strings.ToLower(addrs.ProxyAdmin)) + require.Equal(t, "0xf1000000000000000000000000000000000000c2", strings.ToLower(addrs.Implementation)) + + bc, ok := BYTECODE["UNIVERSAL_CALLBACK"] + require.True(t, ok, "BYTECODE missing entry UNIVERSAL_CALLBACK") + require.NotEmpty(t, bc.IMPL_RUNTIME) + require.NotEmpty(t, bc.PROXY_RUNTIME) + require.NotEmpty(t, bc.ADMIN_RUNTIME) + + // the proxy must embed ITS OWN admin, not the 0xB0 template's + require.Contains(t, + strings.ToLower(hex.EncodeToString(bc.PROXY_RUNTIME)), + "f2000000000000000000000000000000000000c2") + + // the old name must be gone, or genesis would deploy the slot twice + _, stale := SYSTEM_CONTRACTS["RESERVED_C2"] + require.False(t, stale, "RESERVED_C2 must not coexist with UNIVERSAL_CALLBACK") +} + +// The address x/ucallback filters logs on must round-trip through common.Address, +// since the hook compares against a parsed address, not the raw string. +func TestUniversalCallbackAddress_RoundTrips(t *testing.T) { + addr := common.HexToAddress(SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"].Address) + require.Equal(t, + "0x00000000000000000000000000000000000000c2", + strings.ToLower(addr.Hex())) +} From 1b6b83d5232d94b488a8cfdcd5bb5e07c9e46eef Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:30:02 +0530 Subject: [PATCH 38/54] feat(ucallback): decode ReadRequested events --- x/ucallback/types/read_event.go | 157 ++++++++++++++++++++++++ x/ucallback/types/read_event_test.go | 174 +++++++++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 x/ucallback/types/read_event.go create mode 100644 x/ucallback/types/read_event_test.go diff --git a/x/ucallback/types/read_event.go b/x/ucallback/types/read_event.go new file mode 100644 index 00000000..62c826bf --- /dev/null +++ b/x/ucallback/types/read_event.go @@ -0,0 +1,157 @@ +package types + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/cosmos/evm/x/vm/types" +) + +// readRequestedABI is the ABI fragment for UniversalCallback's ReadRequested +// event, transcribed from push-chain-core-contracts: +// +// src/Interfaces/IUniversalCallback.sol — the event +// src/libraries/ReadTypes.sol — ReadSpec +// src/libraries/Types.sol — UniversalAccountId +// +// Held as ABI JSON rather than hand-assembled abi.Type values so that go-ethereum +// derives topic0 for us. A hand-written signature string would be one silent typo +// away from a filter that never matches anything. +const readRequestedABI = `[{ + "type": "event", + "name": "ReadRequested", + "anonymous": false, + "inputs": [ + {"name": "requestId", "type": "uint256", "indexed": true}, + {"name": "readSpec", "type": "tuple", "indexed": false, "components": [ + {"name": "account", "type": "tuple", "components": [ + {"name": "chainNamespace", "type": "string"}, + {"name": "chainId", "type": "string"}, + {"name": "owner", "type": "bytes"} + ]}, + {"name": "query", "type": "bytes"}, + {"name": "minConfirmations", "type": "uint16"}, + {"name": "blockNumber", "type": "uint64"}, + {"name": "expiryPushChainHeight", "type": "uint64"}, + {"name": "maxFee", "type": "uint256"} + ]}, + {"name": "callbackTarget", "type": "address", "indexed": true}, + {"name": "originalFunder", "type": "address", "indexed": true}, + {"name": "feesDeposited", "type": "uint256", "indexed": false} + ] +}]` + +var ( + readRequestedEvent abi.Event + // ReadRequestedEventSig is topic0 for ReadRequested, derived from the ABI above. + ReadRequestedEventSig common.Hash +) + +func init() { + parsed, err := abi.JSON(strings.NewReader(readRequestedABI)) + if err != nil { + panic(fmt.Sprintf("ucallback: bad ReadRequested ABI: %v", err)) + } + ev, ok := parsed.Events["ReadRequested"] + if !ok { + panic("ucallback: ReadRequested missing from parsed ABI") + } + readRequestedEvent = ev + ReadRequestedEventSig = ev.ID +} + +// ReadRequestedEvent is a decoded ReadRequested log. +// +// RequestID keeps the raw 32-byte topic hex rather than a decimal string: it is +// handed straight back to the contract as a uint256 on fulfil/expire, and the hex +// form round-trips without a base conversion in the middle. +type ReadRequestedEvent struct { + RequestID string + CallbackTarget string + OriginalFunder string + + ChainNamespace string + ChainID string + Owner []byte + + Query []byte + MinConfirmations uint16 + BlockNumber uint64 + ExpiryPushChainHeight uint64 + MaxFee *big.Int + + FeesDeposited *big.Int +} + +// DestinationChain returns the CAIP-2 identifier the event's account refers to, +// e.g. "eip155:11155111". The contract emits namespace and id separately; every +// other module keys chains by the joined form. +func (e *ReadRequestedEvent) DestinationChain() string { + return e.ChainNamespace + ":" + e.ChainID +} + +// unpackTarget mirrors the non-indexed argument layout of ReadRequested. Field +// order and types must match the ABI above exactly; go-ethereum maps by position +// within each tuple, not by name. +type unpackTarget struct { + ReadSpec struct { + Account struct { + ChainNamespace string + ChainId string + Owner []byte + } + Query []byte + MinConfirmations uint16 + BlockNumber uint64 + ExpiryPushChainHeight uint64 + MaxFee *big.Int + } + FeesDeposited *big.Int +} + +// DecodeReadRequestedFromLog decodes a ReadRequested log. +// +// The caller is responsible for having checked log.Address — this function only +// validates the topic layout, so on its own it would happily decode a forged event +// from an arbitrary contract. +func DecodeReadRequestedFromLog(log *evmtypes.Log) (*ReadRequestedEvent, error) { + if log == nil { + return nil, fmt.Errorf("nil log") + } + if len(log.Topics) != 4 { + return nil, fmt.Errorf("ReadRequested expects 4 topics, got %d", len(log.Topics)) + } + if !strings.EqualFold(log.Topics[0], ReadRequestedEventSig.Hex()) { + return nil, fmt.Errorf("not a ReadRequested event") + } + + var out unpackTarget + values, err := readRequestedEvent.Inputs.NonIndexed().Unpack(log.Data) + if err != nil { + return nil, fmt.Errorf("failed to unpack ReadRequested: %w", err) + } + if err := readRequestedEvent.Inputs.NonIndexed().Copy(&out, values); err != nil { + return nil, fmt.Errorf("failed to map ReadRequested fields: %w", err) + } + + return &ReadRequestedEvent{ + RequestID: strings.ToLower(log.Topics[1]), + CallbackTarget: common.HexToAddress(log.Topics[2]).Hex(), + OriginalFunder: common.HexToAddress(log.Topics[3]).Hex(), + + ChainNamespace: out.ReadSpec.Account.ChainNamespace, + ChainID: out.ReadSpec.Account.ChainId, + Owner: out.ReadSpec.Account.Owner, + + Query: out.ReadSpec.Query, + MinConfirmations: out.ReadSpec.MinConfirmations, + BlockNumber: out.ReadSpec.BlockNumber, + ExpiryPushChainHeight: out.ReadSpec.ExpiryPushChainHeight, + MaxFee: out.ReadSpec.MaxFee, + + FeesDeposited: out.FeesDeposited, + }, nil +} diff --git a/x/ucallback/types/read_event_test.go b/x/ucallback/types/read_event_test.go new file mode 100644 index 00000000..7f0f8552 --- /dev/null +++ b/x/ucallback/types/read_event_test.go @@ -0,0 +1,174 @@ +package types_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + evmtypes "github.com/cosmos/evm/x/vm/types" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +// readSpec mirrors the contract's ReadSpec for encoding test fixtures. +type account struct { + ChainNamespace string + ChainId string + Owner []byte +} + +type readSpec struct { + Account account + Query []byte + MinConfirmations uint16 + BlockNumber uint64 + ExpiryPushChainHeight uint64 + MaxFee *big.Int +} + +func specArgs(t *testing.T) abi.Arguments { + t.Helper() + specType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "account", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "chainNamespace", Type: "string"}, + {Name: "chainId", Type: "string"}, + {Name: "owner", Type: "bytes"}, + }}, + {Name: "query", Type: "bytes"}, + {Name: "minConfirmations", Type: "uint16"}, + {Name: "blockNumber", Type: "uint64"}, + {Name: "expiryPushChainHeight", Type: "uint64"}, + {Name: "maxFee", Type: "uint256"}, + }) + require.NoError(t, err) + uint256Type, err := abi.NewType("uint256", "", nil) + require.NoError(t, err) + return abi.Arguments{{Type: specType}, {Type: uint256Type}} +} + +func encodeLog(t *testing.T, spec readSpec, fees *big.Int, requestID, target, funder string) *evmtypes.Log { + t.Helper() + data, err := specArgs(t).Pack(spec, fees) + require.NoError(t, err) + + return &evmtypes.Log{ + Address: "0x00000000000000000000000000000000000000C2", + Topics: []string{ + types.ReadRequestedEventSig.Hex(), + requestID, + common.HexToHash(target).Hex(), + common.HexToHash(funder).Hex(), + }, + Data: data, + } +} + +func sampleSpec() readSpec { + return readSpec{ + Account: account{ + ChainNamespace: "eip155", + ChainId: "11155111", + Owner: common.FromHex("0x1111111111111111111111111111111111111111"), + }, + Query: common.FromHex("0xdeadbeef"), + MinConfirmations: 12, + BlockNumber: 8_000_123, + ExpiryPushChainHeight: 900_000, + MaxFee: big.NewInt(5_000_000), + } +} + +// A round-trip through real ABI encoding — if the ABI fragment in read_event.go +// disagrees with the contract's struct layout, this fails. +func TestDecodeReadRequestedFromLog_RoundTrip(t *testing.T) { + spec := sampleSpec() + lg := encodeLog(t, spec, big.NewInt(42_000), + "0x00000000000000000000000000000000000000000000000000000000000000ab", + "0x2222222222222222222222222222222222222222", + "0x3333333333333333333333333333333333333333") + + ev, err := types.DecodeReadRequestedFromLog(lg) + require.NoError(t, err) + + require.Equal(t, "eip155", ev.ChainNamespace) + require.Equal(t, "11155111", ev.ChainID) + require.Equal(t, "eip155:11155111", ev.DestinationChain()) + require.Equal(t, common.FromHex("0x1111111111111111111111111111111111111111"), ev.Owner) + require.Equal(t, common.FromHex("0xdeadbeef"), ev.Query) + require.Equal(t, uint16(12), ev.MinConfirmations) + require.Equal(t, uint64(8_000_123), ev.BlockNumber) + require.Equal(t, uint64(900_000), ev.ExpiryPushChainHeight) + require.Equal(t, big.NewInt(5_000_000), ev.MaxFee) + require.Equal(t, big.NewInt(42_000), ev.FeesDeposited) + + require.Equal(t, "0x2222222222222222222222222222222222222222", ev.CallbackTarget) + require.Equal(t, "0x3333333333333333333333333333333333333333", ev.OriginalFunder) + require.Equal(t, + "0x00000000000000000000000000000000000000000000000000000000000000ab", + ev.RequestID, "request id keeps the full 32-byte topic, lowercased") +} + +// topic0 must be derived, never hand-written: a typo would silently produce a +// filter that matches nothing. +func TestReadRequestedEventSig_IsStable(t *testing.T) { + require.Equal(t, + "0xef9f2bd93134c510809440802fbb5f8056161a88fa47ca5758104364a83d9d8e", + types.ReadRequestedEventSig.Hex(), + "topic0 changed — the contract's event signature moved, or the ABI fragment drifted") +} + +func TestDecodeReadRequestedFromLog_Rejects(t *testing.T) { + spec := sampleSpec() + good := encodeLog(t, spec, big.NewInt(1), + "0x00000000000000000000000000000000000000000000000000000000000000ab", + "0x2222222222222222222222222222222222222222", + "0x3333333333333333333333333333333333333333") + + t.Run("nil log", func(t *testing.T) { + _, err := types.DecodeReadRequestedFromLog(nil) + require.Error(t, err) + }) + + t.Run("wrong topic0", func(t *testing.T) { + bad := *good + bad.Topics = append([]string{}, good.Topics...) + bad.Topics[0] = common.HexToHash("0xdead").Hex() + _, err := types.DecodeReadRequestedFromLog(&bad) + require.Error(t, err) + }) + + t.Run("too few topics", func(t *testing.T) { + bad := *good + bad.Topics = good.Topics[:3] + _, err := types.DecodeReadRequestedFromLog(&bad) + require.Error(t, err) + }) + + t.Run("truncated data", func(t *testing.T) { + bad := *good + bad.Data = good.Data[:len(good.Data)/2] + _, err := types.DecodeReadRequestedFromLog(&bad) + require.Error(t, err) + }) +} + +// Empty owner/query are legitimate on some chains; they must not be an error. +func TestDecodeReadRequestedFromLog_EmptyBytes(t *testing.T) { + spec := sampleSpec() + spec.Account.Owner = []byte{} + spec.Query = []byte{} + spec.MaxFee = big.NewInt(0) + + lg := encodeLog(t, spec, big.NewInt(0), + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x2222222222222222222222222222222222222222", + "0x3333333333333333333333333333333333333333") + + ev, err := types.DecodeReadRequestedFromLog(lg) + require.NoError(t, err) + require.Empty(t, ev.Owner) + require.Empty(t, ev.Query) +} From 73cbbdf5b560ae88ace9baa73f85f914371d6b3e Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:30:43 +0530 Subject: [PATCH 39/54] feat(ucallback): ingest read requests from EVM logs --- x/ucallback/keeper/evm_hooks.go | 77 ++++++++++ x/ucallback/keeper/ingest.go | 124 ++++++++++++++++ x/ucallback/keeper/ingest_test.go | 234 ++++++++++++++++++++++++++++++ 3 files changed, 435 insertions(+) create mode 100644 x/ucallback/keeper/evm_hooks.go create mode 100644 x/ucallback/keeper/ingest.go create mode 100644 x/ucallback/keeper/ingest_test.go diff --git a/x/ucallback/keeper/evm_hooks.go b/x/ucallback/keeper/evm_hooks.go new file mode 100644 index 00000000..d539556a --- /dev/null +++ b/x/ucallback/keeper/evm_hooks.go @@ -0,0 +1,77 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + evmtypes "github.com/cosmos/evm/x/vm/types" + "github.com/ethereum/go-ethereum/common" + core "github.com/ethereum/go-ethereum/core" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// EVMHooks implements the EVM post-processing hooks for x/ucallback. +// +// Runs after every EVM transaction, so the log filter in IngestReadRequests must +// stay tight: this hook sees traffic for the whole chain and must be a no-op for +// all of it except UniversalCallback's ReadRequested events. +type EVMHooks struct { + k Keeper +} + +// NewEVMHooks creates a new instance of EVMHooks. +func NewEVMHooks(k Keeper) evmtypes.EvmHooks { + return EVMHooks{k: k} +} + +// PostTxProcessing inspects the receipt and records a UniversalRead for every +// ReadRequested event the transaction emitted. +// +// Returning an error reverts the whole EVM transaction. That is the behaviour we +// want here: a ReadRequested log we cannot record is a request the user paid for +// that no validator would ever serve. Reverting returns their fee instead of +// stranding it. +func (h EVMHooks) PostTxProcessing( + ctx sdk.Context, + sender common.Address, + msg core.Message, + receipt *ethtypes.Receipt, +) error { + if receipt == nil || len(receipt.Logs) == 0 { + return nil + } + + protoReceipt := &evmtypes.MsgEthereumTxResponse{ + Hash: receipt.TxHash.Hex(), + GasUsed: receipt.GasUsed, + Logs: convertReceiptLogs(receipt.Logs), + } + + return h.k.IngestReadRequests(ctx, protoReceipt) +} + +func convertReceiptLogs(logs []*ethtypes.Log) []*evmtypes.Log { + out := make([]*evmtypes.Log, 0, len(logs)) + + for _, l := range logs { + out = append(out, &evmtypes.Log{ + Address: l.Address.Hex(), + Topics: convertTopics(l.Topics), + Data: l.Data, + BlockNumber: l.BlockNumber, + TxHash: l.TxHash.Hex(), + TxIndex: uint64(l.TxIndex), + BlockHash: l.BlockHash.Hex(), + Index: uint64(l.Index), + Removed: l.Removed, + }) + } + + return out +} + +func convertTopics(topics []common.Hash) []string { + out := make([]string, len(topics)) + for i, t := range topics { + out[i] = t.Hex() + } + return out +} diff --git a/x/ucallback/keeper/ingest.go b/x/ucallback/keeper/ingest.go new file mode 100644 index 00000000..0877d739 --- /dev/null +++ b/x/ucallback/keeper/ingest.go @@ -0,0 +1,124 @@ +package keeper + +import ( + "context" + "fmt" + "math/big" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + evmtypes "github.com/cosmos/evm/x/vm/types" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" +) + +// IngestReadRequests records a UniversalRead for every ReadRequested event in the +// receipt. +// +// The two-part filter — log.Address must be UniversalCallback, topic0 must be +// ReadRequested — is what makes the decoded event trustworthy. Any contract can +// emit a log with the same topic0; only the system contract's address makes it +// ours. Dropping the address check would let anyone mint read requests. +func (k Keeper) IngestReadRequests(ctx context.Context, receipt *evmtypes.MsgEthereumTxResponse) error { + if receipt == nil || len(receipt.Logs) == 0 { + return nil + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + callbackAddr := strings.ToLower(uregistrytypes.SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"].Address) + + for _, lg := range receipt.Logs { + if lg.Removed { + continue + } + if strings.ToLower(lg.Address) != callbackAddr { + continue + } + if len(lg.Topics) == 0 || + !strings.EqualFold(lg.Topics[0], types.ReadRequestedEventSig.Hex()) { + continue + } + + event, err := types.DecodeReadRequestedFromLog(lg) + if err != nil { + return fmt.Errorf("failed to decode ReadRequested (tx %s log %d): %w", + receipt.Hash, lg.Index, err) + } + + if err := k.recordReadRequest(ctx, sdkCtx, event, receipt.Hash, lg.Index); err != nil { + return err + } + } + + return nil +} + +// recordReadRequest writes one decoded event as a PENDING UniversalRead. +func (k Keeper) recordReadRequest( + ctx context.Context, + sdkCtx sdk.Context, + event *types.ReadRequestedEvent, + txHash string, + logIndex uint64, +) error { + // requestId is derived on-chain from an incrementing nonce, so a repeat means + // the same log was replayed rather than a genuine second request. Keep the + // first record: it is the one validators may already be working from. + if k.HasUniversalRead(ctx, event.RequestID) { + k.Logger().Debug("read request already recorded, skipping", + "request_id", event.RequestID, "tx_hash", txHash) + return nil + } + + ur := types.UniversalRead{ + Id: event.RequestID, + Status: types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, + Request: &types.ReadRequest{ + RequestId: event.RequestID, + DestinationChain: event.DestinationChain(), + Owner: event.Owner, + Query: event.Query, + MinConfirmations: uint32(event.MinConfirmations), + DestinationBlockHeight: event.BlockNumber, + ExpiryBlockHeight: event.ExpiryPushChainHeight, + // Not carried by the event — the height at which we observed it is the + // only honest answer, and it is what expiry is measured against. + CreatedAtHeight: uint64(sdkCtx.BlockHeight()), + CallbackTarget: event.CallbackTarget, + OriginalFunder: event.OriginalFunder, + FeesDeposited: bigOrZero(event.FeesDeposited), + MaxFee: bigOrZero(event.MaxFee), + RequestedTxHash: txHash, + RequestedLogIndex: logIndex, + }, + } + + if err := k.SetUniversalRead(ctx, ur); err != nil { + return fmt.Errorf("failed to record read request %s: %w", event.RequestID, err) + } + + k.Logger().Info("read request recorded", + "request_id", event.RequestID, + "destination_chain", ur.Request.DestinationChain, + "expiry_height", ur.Request.ExpiryBlockHeight, + "tx_hash", txHash, + "log_index", logIndex, + ) + + return nil +} + +// bigOrZero renders a *big.Int as a decimal string, tolerating nil. The proto +// carries these as strings because they are uint256 values that do not fit any +// protobuf integer type. +// +// Takes *big.Int rather than a String()-bearing interface on purpose: a nil +// *big.Int boxed into an interface is not itself nil, so the guard would miss it +// and String() would be called on a nil receiver. +func bigOrZero(v *big.Int) string { + if v == nil { + return "0" + } + return v.String() +} diff --git a/x/ucallback/keeper/ingest_test.go b/x/ucallback/keeper/ingest_test.go new file mode 100644 index 00000000..da08f0f1 --- /dev/null +++ b/x/ucallback/keeper/ingest_test.go @@ -0,0 +1,234 @@ +package keeper_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + evmtypes "github.com/cosmos/evm/x/vm/types" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" +) + +type acct struct { + ChainNamespace string + ChainId string + Owner []byte +} + +type spec struct { + Account acct + Query []byte + MinConfirmations uint16 + BlockNumber uint64 + ExpiryPushChainHeight uint64 + MaxFee *big.Int +} + +func readSpecArgs(t *testing.T) abi.Arguments { + t.Helper() + specType, err := abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "account", Type: "tuple", Components: []abi.ArgumentMarshaling{ + {Name: "chainNamespace", Type: "string"}, + {Name: "chainId", Type: "string"}, + {Name: "owner", Type: "bytes"}, + }}, + {Name: "query", Type: "bytes"}, + {Name: "minConfirmations", Type: "uint16"}, + {Name: "blockNumber", Type: "uint64"}, + {Name: "expiryPushChainHeight", Type: "uint64"}, + {Name: "maxFee", Type: "uint256"}, + }) + require.NoError(t, err) + u256, err := abi.NewType("uint256", "", nil) + require.NoError(t, err) + return abi.Arguments{{Type: specType}, {Type: u256}} +} + +// readLog builds a well-formed ReadRequested log emitted by the real system +// contract address. +func readLog(t *testing.T, requestID string, expiry uint64, index uint64) *evmtypes.Log { + t.Helper() + data, err := readSpecArgs(t).Pack(spec{ + Account: acct{ + ChainNamespace: "eip155", + ChainId: "11155111", + Owner: common.FromHex("0x1111111111111111111111111111111111111111"), + }, + Query: common.FromHex("0xdeadbeef"), + MinConfirmations: 6, + BlockNumber: 8_000_000, + ExpiryPushChainHeight: expiry, + MaxFee: big.NewInt(7), + }, big.NewInt(99)) + require.NoError(t, err) + + return &evmtypes.Log{ + Address: uregistrytypes.SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"].Address, + Topics: []string{ + types.ReadRequestedEventSig.Hex(), + common.HexToHash(requestID).Hex(), + common.HexToHash("0x2222222222222222222222222222222222222222").Hex(), + common.HexToHash("0x3333333333333333333333333333333333333333").Hex(), + }, + Data: data, + Index: index, + } +} + +func receipt(hash string, logs ...*evmtypes.Log) *evmtypes.MsgEthereumTxResponse { + return &evmtypes.MsgEthereumTxResponse{Hash: hash, Logs: logs} +} + +func TestIngestReadRequests_RecordsPendingRead(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(4242) + + lg := readLog(t, "0xaa", 900_000, 3) + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + + ur, found := f.k.GetUniversalRead(f.ctx, lg.Topics[1]) + require.True(t, found) + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, ur.Status) + + r := ur.Request + require.Equal(t, "eip155:11155111", r.DestinationChain, "namespace and id joined to CAIP-2") + require.Equal(t, common.FromHex("0x1111111111111111111111111111111111111111"), r.Owner) + require.Equal(t, common.FromHex("0xdeadbeef"), r.Query) + require.Equal(t, uint32(6), r.MinConfirmations) + require.Equal(t, uint64(8_000_000), r.DestinationBlockHeight) + require.Equal(t, uint64(900_000), r.ExpiryBlockHeight) + require.Equal(t, uint64(4242), r.CreatedAtHeight, "taken from the block, not the event") + require.Equal(t, "0xTX", r.RequestedTxHash) + require.Equal(t, uint64(3), r.RequestedLogIndex) + require.Equal(t, "99", r.FeesDeposited) + require.Equal(t, "7", r.MaxFee) +} + +// The address filter is the whole trust boundary: topic0 alone is forgeable by +// any contract, so a matching event from elsewhere must be ignored entirely. +func TestIngestReadRequests_IgnoresForeignContract(t *testing.T) { + f := SetupTest(t) + + lg := readLog(t, "0xaa", 900_000, 0) + lg.Address = "0x000000000000000000000000000000000000dEaD" + + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + require.False(t, f.k.HasUniversalRead(f.ctx, lg.Topics[1]), + "a ReadRequested-shaped log from a foreign address must not mint a request") +} + +func TestIngestReadRequests_IgnoresUnrelatedLogs(t *testing.T) { + f := SetupTest(t) + + other := &evmtypes.Log{ + Address: uregistrytypes.SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"].Address, + Topics: []string{common.HexToHash("0xfeed").Hex()}, + Data: []byte{1, 2, 3}, + } + noTopics := &evmtypes.Log{ + Address: uregistrytypes.SYSTEM_CONTRACTS["UNIVERSAL_CALLBACK"].Address, + } + + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", other, noTopics))) + require.Empty(t, pendingIDs(t, f)) +} + +func TestIngestReadRequests_SkipsRemovedLogs(t *testing.T) { + f := SetupTest(t) + + lg := readLog(t, "0xaa", 900_000, 0) + lg.Removed = true + + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + require.False(t, f.k.HasUniversalRead(f.ctx, lg.Topics[1])) +} + +// One transaction emitting several ReadRequested logs becomes several independent +// records that still reassemble as a batch. +func TestIngestReadRequests_Batch(t *testing.T) { + f := SetupTest(t) + + a := readLog(t, "0xaa", 900_000, 0) + b := readLog(t, "0xbb", 900_001, 1) + c := readLog(t, "0xcc", 900_002, 2) + + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xBATCH", a, b, c))) + + res, err := f.queryServer.ReadsByTx(f.ctx, &types.QueryReadsByTxRequest{TxHash: "0xBATCH"}) + require.NoError(t, err) + require.Len(t, res.Reads, 3) + + for _, r := range res.Reads { + require.Equal(t, "0xBATCH", r.Request.RequestedTxHash) + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, r.Status) + } + // log index is preserved per sibling, so each is individually addressable + require.ElementsMatch(t, []uint64{0, 1, 2}, + []uint64{res.Reads[0].Request.RequestedLogIndex, + res.Reads[1].Request.RequestedLogIndex, + res.Reads[2].Request.RequestedLogIndex}) +} + +// Replaying the same log must not overwrite progress already made on the request. +func TestIngestReadRequests_IsIdempotent(t *testing.T) { + f := SetupTest(t) + + lg := readLog(t, "0xaa", 900_000, 0) + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + + // the request advances + ur, found := f.k.GetUniversalRead(f.ctx, lg.Topics[1]) + require.True(t, found) + ur.Status = types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING + ur.BallotKey = "ballot-1" + require.NoError(t, f.k.SetUniversalRead(f.ctx, ur)) + + // replay + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + + again, found := f.k.GetUniversalRead(f.ctx, lg.Topics[1]) + require.True(t, found) + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING, again.Status, + "replay must not reset an in-flight request to PENDING") + require.Equal(t, "ballot-1", again.BallotKey) +} + +// An undecodable log from our own contract is a bug, not user error. Returning an +// error reverts the EVM tx so the funder keeps their fee rather than paying for a +// request no validator will ever see. +func TestIngestReadRequests_UndecodableIsAnError(t *testing.T) { + f := SetupTest(t) + + lg := readLog(t, "0xaa", 900_000, 0) + lg.Data = lg.Data[:len(lg.Data)/2] + + require.Error(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", lg))) + require.Empty(t, pendingIDs(t, f)) +} + +func TestIngestReadRequests_EmptyReceipt(t *testing.T) { + f := SetupTest(t) + require.NoError(t, f.k.IngestReadRequests(f.ctx, nil)) + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX"))) + require.Empty(t, pendingIDs(t, f)) +} + +// Ingested reads are immediately visible to validators through the polling query. +func TestIngestReadRequests_VisibleToValidators(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(100) + + live := readLog(t, "0xaa", 500, 0) + dead := readLog(t, "0xbb", 50, 1) + + require.NoError(t, f.k.IngestReadRequests(f.ctx, receipt("0xTX", live, dead))) + + require.Equal(t, []string{live.Topics[1]}, pendingIDs(t, f), + "a request ingested already past its expiry is recorded but never offered") + require.True(t, f.k.HasUniversalRead(f.ctx, dead.Topics[1])) +} From bcc8805778975901c37d8c739130ce6856ec3377 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:31:24 +0530 Subject: [PATCH 40/54] feat(app): register ucallback EVM hook --- app/app.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/app.go b/app/app.go index fe626098..da3a3a46 100644 --- a/app/app.go +++ b/app/app.go @@ -799,7 +799,13 @@ func NewChainApp( ), ) - app.EVMKeeper.SetHooks(uexecutorkeeper.NewEVMHooks(app.UexecutorKeeper)) + // SetHooks panics if called twice, so every EVM post-tx consumer registers + // here. Hooks run in order and share a transaction: an error from any one of + // them reverts the whole EVM tx, including the work earlier hooks did. + app.EVMKeeper.SetHooks(evmkeeper.NewMultiEvmHooks( + uexecutorkeeper.NewEVMHooks(app.UexecutorKeeper), + ucallbackkeeper.NewEVMHooks(app.UcallbackKeeper), + )) // NOTE: we are adding all available EVM extensions. // Not all of them need to be enabled, which can be configured on a per-chain basis. From b3b5aa3174b166a28f9032c09a12c4f110c5b955 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:31:24 +0530 Subject: [PATCH 41/54] docs(ucallback): note 0xC2 deploy needed in upgrade handler --- UCALLBACK_IMPLEMENTATION.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/UCALLBACK_IMPLEMENTATION.md b/UCALLBACK_IMPLEMENTATION.md index 118de76b..0004bb8a 100644 --- a/UCALLBACK_IMPLEMENTATION.md +++ b/UCALLBACK_IMPLEMENTATION.md @@ -525,8 +525,22 @@ app/upgrades//upgrade.go app/upgrades.go ``` -New store key → `StoreUpgrades.Added: []string{"ucallback"}`. Everything else is a no-op -`RunMigrations`. +New store key → `StoreUpgrades.Added: []string{"ucallback"}`. + +**Not a no-op `RunMigrations` — the handler must also deploy UniversalCallback at 0xC2.** +Verified against donut (chain 42101, height 20,791,174): `0x…C2`, `0xF2…C2` and `0xF1…C2` are all +empty — `code: 0x`, balance 0, nonce 0. Only the explicitly-named `SYSTEM_CONTRACTS` entries +(`0xAA 0xB0 0xB1 0xB2 0xBC 0xC0 0xC1`) are live; every `RESERVED_*` slot (`0xA0 0xA5 0xB3 0xC2 0xCF`) +is empty, because the deploy loop in `x/uregistry/keeper/genesis.go` runs at `InitGenesis` only and +donut's genesis predates the `init()` that added those reservations. + +So promoting `RESERVED_C2` → `UNIVERSAL_CALLBACK` is free on donut (nothing there either way), but the +real contract will not appear on its own — the handler has to deploy the admin + impl + proxy triple +explicitly, the way genesis would have. + +> Related, and worth raising with the team separately: the F-2026-17025 squatting defence is **not in +> effect on donut**. The A/B/C reserved slots are empty and claimable there. Pre-existing, but we are +> about to place a contract in that range. **Verify with a real upgrade simulation** from the current donut release to this branch — the established flow: start the old binary, submit `MsgSoftwareUpgrade` at a height well past the end of From 96deaf33844dc4d499e76a853f3f993b43f27345 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:50:53 +0530 Subject: [PATCH 42/54] feat(uvalidator): add read-result ballot type --- api/uvalidator/v1/ballot.pulsar.go | 48 +++++++++-------- proto/uvalidator/v1/ballot.proto | 1 + x/uvalidator/types/ballot.pb.go | 84 ++++++++++++++++-------------- 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/api/uvalidator/v1/ballot.pulsar.go b/api/uvalidator/v1/ballot.pulsar.go index 7cff2556..295b5dd8 100644 --- a/api/uvalidator/v1/ballot.pulsar.go +++ b/api/uvalidator/v1/ballot.pulsar.go @@ -1054,6 +1054,7 @@ const ( BallotObservationType_BALLOT_OBSERVATION_TYPE_OUTBOUND_TX BallotObservationType = 2 BallotObservationType_BALLOT_OBSERVATION_TYPE_TSS_KEY BallotObservationType = 3 BallotObservationType_BALLOT_OBSERVATION_TYPE_FUND_MIGRATION BallotObservationType = 4 + BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT BallotObservationType = 5 ) // Enum value maps for BallotObservationType. @@ -1064,6 +1065,7 @@ var ( 2: "BALLOT_OBSERVATION_TYPE_OUTBOUND_TX", 3: "BALLOT_OBSERVATION_TYPE_TSS_KEY", 4: "BALLOT_OBSERVATION_TYPE_FUND_MIGRATION", + 5: "BALLOT_OBSERVATION_TYPE_READ_RESULT", } BallotObservationType_value = map[string]int32{ "BALLOT_OBSERVATION_TYPE_UNSPECIFIED": 0, @@ -1071,6 +1073,7 @@ var ( "BALLOT_OBSERVATION_TYPE_OUTBOUND_TX": 2, "BALLOT_OBSERVATION_TYPE_TSS_KEY": 3, "BALLOT_OBSERVATION_TYPE_FUND_MIGRATION": 4, + "BALLOT_OBSERVATION_TYPE_READ_RESULT": 5, } ) @@ -1289,7 +1292,7 @@ var file_uvalidator_v1_ballot_proto_rawDesc = []byte{ 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x41, 0x4c, 0x4c, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, 0x1a, 0x04, - 0xa8, 0xa4, 0x1e, 0x01, 0x2a, 0xe8, 0x01, 0x0a, 0x15, 0x42, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x4f, + 0xa8, 0xa4, 0x1e, 0x01, 0x2a, 0x91, 0x02, 0x0a, 0x15, 0x42, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x42, 0x41, 0x4c, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x42, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, @@ -1303,26 +1306,29 @@ var file_uvalidator_v1_ballot_proto_rawDesc = []byte{ 0x59, 0x50, 0x45, 0x5f, 0x54, 0x53, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x03, 0x12, 0x2a, 0x0a, 0x26, 0x42, 0x41, 0x4c, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x42, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x55, 0x4e, 0x44, 0x5f, 0x4d, 0x49, - 0x47, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x1a, 0x04, 0xa8, 0xa4, 0x1e, 0x01, 0x2a, - 0x63, 0x0a, 0x0a, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, - 0x19, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x4e, 0x4f, 0x54, - 0x5f, 0x59, 0x45, 0x54, 0x5f, 0x56, 0x4f, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, - 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, - 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, - 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x1a, 0x04, - 0xa8, 0xa4, 0x1e, 0x01, 0x42, 0xba, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x42, 0x61, 0x6c, 0x6c, - 0x6f, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, - 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x75, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x76, - 0x31, 0x3b, 0x75, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x76, 0x31, 0xa2, 0x02, - 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0d, 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x19, 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0xea, 0x02, 0x0e, 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x3a, 0x3a, 0x56, - 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x47, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x27, 0x0a, 0x23, 0x42, 0x41, 0x4c, + 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x42, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, + 0x10, 0x05, 0x1a, 0x04, 0xa8, 0xa4, 0x1e, 0x01, 0x2a, 0x63, 0x0a, 0x0a, 0x56, 0x6f, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x19, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, + 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x59, 0x45, 0x54, 0x5f, 0x56, 0x4f, + 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, + 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x17, + 0x0a, 0x13, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, + 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x1a, 0x04, 0xa8, 0xa4, 0x1e, 0x01, 0x42, 0xba, 0x01, + 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x42, 0x61, 0x6c, 0x6c, 0x6f, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, + 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0d, + 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0d, + 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x19, + 0x55, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x55, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/proto/uvalidator/v1/ballot.proto b/proto/uvalidator/v1/ballot.proto index d1353e9e..63ed6971 100644 --- a/proto/uvalidator/v1/ballot.proto +++ b/proto/uvalidator/v1/ballot.proto @@ -30,6 +30,7 @@ enum BallotObservationType { BALLOT_OBSERVATION_TYPE_OUTBOUND_TX = 2; BALLOT_OBSERVATION_TYPE_TSS_KEY = 3; BALLOT_OBSERVATION_TYPE_FUND_MIGRATION = 4; + BALLOT_OBSERVATION_TYPE_READ_RESULT = 5; } // --------------------------- diff --git a/x/uvalidator/types/ballot.pb.go b/x/uvalidator/types/ballot.pb.go index 111e99fc..4fd07807 100644 --- a/x/uvalidator/types/ballot.pb.go +++ b/x/uvalidator/types/ballot.pb.go @@ -72,6 +72,7 @@ const ( BallotObservationType_BALLOT_OBSERVATION_TYPE_OUTBOUND_TX BallotObservationType = 2 BallotObservationType_BALLOT_OBSERVATION_TYPE_TSS_KEY BallotObservationType = 3 BallotObservationType_BALLOT_OBSERVATION_TYPE_FUND_MIGRATION BallotObservationType = 4 + BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT BallotObservationType = 5 ) var BallotObservationType_name = map[int32]string{ @@ -80,6 +81,7 @@ var BallotObservationType_name = map[int32]string{ 2: "BALLOT_OBSERVATION_TYPE_OUTBOUND_TX", 3: "BALLOT_OBSERVATION_TYPE_TSS_KEY", 4: "BALLOT_OBSERVATION_TYPE_FUND_MIGRATION", + 5: "BALLOT_OBSERVATION_TYPE_READ_RESULT", } var BallotObservationType_value = map[string]int32{ @@ -88,6 +90,7 @@ var BallotObservationType_value = map[string]int32{ "BALLOT_OBSERVATION_TYPE_OUTBOUND_TX": 2, "BALLOT_OBSERVATION_TYPE_TSS_KEY": 3, "BALLOT_OBSERVATION_TYPE_FUND_MIGRATION": 4, + "BALLOT_OBSERVATION_TYPE_READ_RESULT": 5, } func (x BallotObservationType) String() string { @@ -242,46 +245,47 @@ func init() { func init() { proto.RegisterFile("uvalidator/v1/ballot.proto", fileDescriptor_b9f9c8e0d3c818f3) } var fileDescriptor_b9f9c8e0d3c818f3 = []byte{ - // 616 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x94, 0xcf, 0x4e, 0xdb, 0x4a, - 0x14, 0xc6, 0x63, 0x07, 0x72, 0x2f, 0x73, 0xef, 0x0d, 0x66, 0x80, 0x5b, 0x93, 0xaa, 0x6e, 0x04, - 0x15, 0xa4, 0x48, 0xc4, 0x05, 0x16, 0x5d, 0xe7, 0xcf, 0x40, 0xdd, 0xa6, 0x76, 0xea, 0x19, 0x23, - 0xe8, 0x66, 0xe4, 0x24, 0xa3, 0xd8, 0xaa, 0xc9, 0x44, 0xf6, 0x24, 0x82, 0xb7, 0xe8, 0x1b, 0x74, - 0xd3, 0x45, 0x1f, 0xa5, 0x4b, 0x96, 0x5d, 0x56, 0xb0, 0xe9, 0x3b, 0x74, 0x53, 0x79, 0xcc, 0x9f, - 0x24, 0x85, 0x6e, 0xac, 0xa3, 0xef, 0xf7, 0x9d, 0xf3, 0xf9, 0x68, 0x3c, 0x06, 0xa5, 0xd1, 0xd8, - 0x8f, 0xc2, 0x9e, 0x2f, 0x78, 0x6c, 0x8e, 0x77, 0xcd, 0x8e, 0x1f, 0x45, 0x5c, 0x54, 0x87, 0x31, - 0x17, 0x1c, 0xfe, 0x77, 0xc7, 0xaa, 0xe3, 0xdd, 0xd2, 0x4a, 0x9f, 0xf7, 0xb9, 0x24, 0x66, 0x5a, - 0x65, 0xa6, 0xd2, 0x92, 0x7f, 0x1a, 0x0e, 0xb8, 0x29, 0x9f, 0x99, 0xb4, 0xfe, 0x53, 0x05, 0x85, - 0xba, 0x1c, 0x04, 0x8b, 0x40, 0x0d, 0x7b, 0xba, 0x52, 0x56, 0x2a, 0x0b, 0xae, 0x1a, 0xf6, 0x20, - 0x02, 0xff, 0x64, 0x11, 0x54, 0x9c, 0x0f, 0x99, 0xae, 0x96, 0x95, 0x4a, 0x71, 0xef, 0x59, 0x75, - 0x2a, 0xa8, 0x9a, 0xf5, 0x3a, 0x9d, 0x84, 0xc5, 0x63, 0x5f, 0x84, 0x7c, 0x40, 0xce, 0x87, 0xcc, - 0x05, 0x59, 0x63, 0x5a, 0xc3, 0x2d, 0xb0, 0xc8, 0xa2, 0xb0, 0x1f, 0x76, 0x22, 0x46, 0xc7, 0x5c, - 0xb0, 0x38, 0xd1, 0xf3, 0xe5, 0x7c, 0x65, 0xc1, 0x2d, 0xde, 0xc8, 0x47, 0x52, 0x85, 0x26, 0x98, - 0x4f, 0x79, 0xa2, 0xcf, 0x95, 0xf3, 0x95, 0xe2, 0xde, 0xda, 0x4c, 0x52, 0xea, 0x72, 0x59, 0x32, - 0x8a, 0x84, 0x9b, 0xf9, 0xe0, 0x73, 0xa0, 0x8d, 0xb9, 0x08, 0x07, 0x7d, 0x2a, 0x82, 0x98, 0x25, - 0x01, 0x8f, 0x7a, 0xfa, 0x7c, 0x59, 0xa9, 0xe4, 0xdd, 0xc5, 0x4c, 0x27, 0x37, 0x32, 0xdc, 0x07, - 0x85, 0x44, 0xf8, 0x62, 0x94, 0xe8, 0x05, 0xb9, 0xc6, 0xe3, 0x7b, 0xd7, 0xc0, 0xd2, 0xe2, 0x5e, - 0x5b, 0xe1, 0x0b, 0xb0, 0xd2, 0x89, 0x78, 0xf7, 0x03, 0x0d, 0x58, 0xd8, 0x0f, 0x04, 0xed, 0xc6, - 0xcc, 0x17, 0xac, 0xa7, 0xff, 0x25, 0x33, 0xa0, 0x64, 0xaf, 0x24, 0x6a, 0x64, 0x04, 0x56, 0xc1, - 0xf2, 0x54, 0x07, 0x3b, 0x1b, 0x86, 0xf1, 0xb9, 0xfe, 0xb7, 0x6c, 0x58, 0x9a, 0x68, 0x40, 0x12, - 0x6c, 0x7f, 0x52, 0xc0, 0xbf, 0x93, 0xd1, 0xf0, 0x09, 0x58, 0xab, 0xd7, 0x5a, 0x2d, 0x87, 0x50, - 0x4c, 0x6a, 0xc4, 0xc3, 0xd4, 0xb3, 0x71, 0x1b, 0x35, 0xac, 0x03, 0x0b, 0x35, 0xb5, 0x1c, 0x5c, - 0x03, 0xab, 0xd3, 0xb8, 0x8d, 0xec, 0xa6, 0x65, 0x1f, 0x6a, 0x0a, 0xd4, 0xc1, 0xca, 0x0c, 0xaa, - 0x61, 0x8c, 0x9a, 0x9a, 0x0a, 0x4b, 0xe0, 0xff, 0x69, 0xe2, 0xa2, 0xd7, 0xa8, 0x41, 0x50, 0x53, - 0xcb, 0xff, 0x3e, 0x10, 0x1d, 0xb7, 0x2d, 0x17, 0x35, 0xb5, 0xb9, 0xd2, 0xdc, 0x97, 0xcf, 0x86, - 0xb2, 0xfd, 0x43, 0x01, 0xab, 0xf7, 0x9e, 0x31, 0xdc, 0x02, 0x1b, 0xd7, 0xad, 0x4e, 0x1d, 0x23, - 0xf7, 0xa8, 0x46, 0x2c, 0xc7, 0xa6, 0xe4, 0xa4, 0x8d, 0x66, 0x5e, 0x7a, 0x13, 0xac, 0x3f, 0x64, - 0xb4, 0xec, 0xba, 0xe3, 0xd9, 0x4d, 0x4a, 0x8e, 0x35, 0xe5, 0x4f, 0x03, 0x1d, 0x8f, 0xdc, 0x1a, - 0x55, 0xb8, 0x01, 0x9e, 0x3e, 0x64, 0x24, 0x18, 0xd3, 0x37, 0xe8, 0x44, 0xcb, 0xc3, 0x6d, 0xb0, - 0xf9, 0x90, 0xe9, 0x20, 0x9d, 0xf4, 0xd6, 0x3a, 0x74, 0xa5, 0x76, 0xbb, 0x6a, 0x17, 0x80, 0xbb, - 0x6f, 0x2c, 0x3d, 0x89, 0x23, 0x87, 0x20, 0xea, 0x22, 0xec, 0xb5, 0x08, 0xb5, 0x1d, 0x42, 0x4f, - 0x10, 0xa1, 0xa9, 0x96, 0x2e, 0xf5, 0x08, 0x2c, 0x4f, 0x62, 0xec, 0x35, 0x1a, 0x08, 0x63, 0x4d, - 0x99, 0x05, 0x07, 0x35, 0xab, 0xe5, 0xb9, 0x48, 0x53, 0xb3, 0x90, 0xfa, 0xbb, 0xaf, 0x97, 0x86, - 0x72, 0x71, 0x69, 0x28, 0xdf, 0x2f, 0x0d, 0xe5, 0xe3, 0x95, 0x91, 0xbb, 0xb8, 0x32, 0x72, 0xdf, - 0xae, 0x8c, 0xdc, 0xfb, 0x97, 0xfd, 0x50, 0x04, 0xa3, 0x4e, 0xb5, 0xcb, 0x4f, 0xcd, 0xe1, 0x28, - 0x09, 0xba, 0x81, 0x1f, 0x0e, 0x64, 0xb5, 0x23, 0xcb, 0x9d, 0x01, 0xef, 0x31, 0xf3, 0xcc, 0x9c, - 0xf8, 0x09, 0xa4, 0xf7, 0x32, 0xe9, 0x14, 0xe4, 0x4d, 0xde, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff, - 0x4e, 0x9b, 0x9c, 0x99, 0x1f, 0x04, 0x00, 0x00, + // 626 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x94, 0xc1, 0x4e, 0xdb, 0x4a, + 0x14, 0x86, 0x63, 0x27, 0xe4, 0x5e, 0xe6, 0xde, 0x1b, 0xcc, 0x00, 0xb7, 0x26, 0x55, 0xdd, 0x08, + 0x2a, 0x48, 0x91, 0x88, 0x0b, 0x2c, 0xba, 0x4e, 0xe2, 0x81, 0xba, 0x4d, 0xed, 0x74, 0x3c, 0x46, + 0xd0, 0xcd, 0xc8, 0x49, 0xac, 0xd8, 0xaa, 0xc9, 0x44, 0xf6, 0x24, 0x82, 0xb7, 0x68, 0x9f, 0xa0, + 0x9b, 0x2e, 0xfa, 0x28, 0x5d, 0xb2, 0xec, 0xb2, 0x82, 0xc7, 0xe8, 0xa6, 0xf2, 0x38, 0x94, 0x24, + 0x05, 0x36, 0xd1, 0xd1, 0xff, 0xfd, 0xe7, 0xfc, 0x39, 0x1e, 0x8f, 0x41, 0x79, 0x34, 0xf6, 0xa2, + 0xb0, 0xe7, 0x71, 0x16, 0xeb, 0xe3, 0x3d, 0xbd, 0xe3, 0x45, 0x11, 0xe3, 0xb5, 0x61, 0xcc, 0x38, + 0x83, 0xff, 0xdd, 0xb2, 0xda, 0x78, 0xaf, 0xbc, 0xda, 0x67, 0x7d, 0x26, 0x88, 0x9e, 0x56, 0x99, + 0xa9, 0xbc, 0xec, 0x9d, 0x85, 0x03, 0xa6, 0x8b, 0xdf, 0x4c, 0xda, 0xf8, 0x29, 0x83, 0x62, 0x43, + 0x0c, 0x82, 0x25, 0x20, 0x87, 0x3d, 0x55, 0xaa, 0x48, 0xd5, 0x45, 0x2c, 0x87, 0x3d, 0x88, 0xc0, + 0x3f, 0x59, 0x04, 0xe5, 0x17, 0x43, 0x5f, 0x95, 0x2b, 0x52, 0xb5, 0xb4, 0xff, 0xac, 0x36, 0x13, + 0x54, 0xcb, 0x7a, 0xed, 0x4e, 0xe2, 0xc7, 0x63, 0x8f, 0x87, 0x6c, 0x40, 0x2e, 0x86, 0x3e, 0x06, + 0x59, 0x63, 0x5a, 0xc3, 0x6d, 0xb0, 0xe4, 0x47, 0x61, 0x3f, 0xec, 0x44, 0x3e, 0x1d, 0x33, 0xee, + 0xc7, 0x89, 0x9a, 0xaf, 0xe4, 0xab, 0x8b, 0xb8, 0x74, 0x23, 0x1f, 0x0b, 0x15, 0xea, 0x60, 0x21, + 0xe5, 0x89, 0x5a, 0xa8, 0xe4, 0xab, 0xa5, 0xfd, 0xf5, 0xb9, 0xa4, 0xd4, 0x85, 0xfd, 0x64, 0x14, + 0x71, 0x9c, 0xf9, 0xe0, 0x73, 0xa0, 0x8c, 0x19, 0x0f, 0x07, 0x7d, 0xca, 0x83, 0xd8, 0x4f, 0x02, + 0x16, 0xf5, 0xd4, 0x85, 0x8a, 0x54, 0xcd, 0xe3, 0xa5, 0x4c, 0x27, 0x37, 0x32, 0x3c, 0x00, 0xc5, + 0x84, 0x7b, 0x7c, 0x94, 0xa8, 0x45, 0xb1, 0xc6, 0xe3, 0x3b, 0xd7, 0x70, 0x84, 0x05, 0x4f, 0xac, + 0xf0, 0x05, 0x58, 0xed, 0x44, 0xac, 0xfb, 0x81, 0x06, 0x7e, 0xd8, 0x0f, 0x38, 0xed, 0xc6, 0xbe, + 0xc7, 0xfd, 0x9e, 0xfa, 0x97, 0xc8, 0x80, 0x82, 0xbd, 0x12, 0xa8, 0x99, 0x11, 0x58, 0x03, 0x2b, + 0x33, 0x1d, 0xfe, 0xf9, 0x30, 0x8c, 0x2f, 0xd4, 0xbf, 0x45, 0xc3, 0xf2, 0x54, 0x03, 0x12, 0x60, + 0xe7, 0xb3, 0x04, 0xfe, 0x9d, 0x8e, 0x86, 0x4f, 0xc0, 0x7a, 0xa3, 0xde, 0x6a, 0xd9, 0x84, 0x3a, + 0xa4, 0x4e, 0x5c, 0x87, 0xba, 0x96, 0xd3, 0x46, 0x4d, 0xf3, 0xd0, 0x44, 0x86, 0x92, 0x83, 0xeb, + 0x60, 0x6d, 0x16, 0xb7, 0x91, 0x65, 0x98, 0xd6, 0x91, 0x22, 0x41, 0x15, 0xac, 0xce, 0xa1, 0xba, + 0xe3, 0x20, 0x43, 0x91, 0x61, 0x19, 0xfc, 0x3f, 0x4b, 0x30, 0x7a, 0x8d, 0x9a, 0x04, 0x19, 0x4a, + 0xfe, 0xcf, 0x81, 0xe8, 0xa4, 0x6d, 0x62, 0x64, 0x28, 0x85, 0x72, 0xe1, 0xeb, 0x17, 0x4d, 0xda, + 0xf9, 0x24, 0x83, 0xb5, 0x3b, 0xcf, 0x18, 0x6e, 0x83, 0xcd, 0x49, 0xab, 0xdd, 0x70, 0x10, 0x3e, + 0xae, 0x13, 0xd3, 0xb6, 0x28, 0x39, 0x6d, 0xa3, 0xb9, 0x3f, 0xbd, 0x05, 0x36, 0xee, 0x33, 0x9a, + 0x56, 0xc3, 0x76, 0x2d, 0x83, 0x92, 0x13, 0x45, 0x7a, 0x68, 0xa0, 0xed, 0x92, 0xdf, 0x46, 0x19, + 0x6e, 0x82, 0xa7, 0xf7, 0x19, 0x89, 0xe3, 0xd0, 0x37, 0xe8, 0x54, 0xc9, 0xc3, 0x1d, 0xb0, 0x75, + 0x9f, 0xe9, 0x30, 0x9d, 0xf4, 0xd6, 0x3c, 0xc2, 0x42, 0x53, 0x0a, 0x0f, 0x25, 0x63, 0x54, 0x37, + 0x28, 0x46, 0x8e, 0xdb, 0x22, 0xca, 0xc2, 0xe4, 0x99, 0x74, 0x01, 0xb8, 0x7d, 0x19, 0xd3, 0x23, + 0x3b, 0xb6, 0x09, 0x9a, 0x98, 0xa8, 0x65, 0x13, 0x7a, 0x8a, 0x08, 0x4d, 0xb5, 0x74, 0xfb, 0x47, + 0x60, 0x65, 0x1a, 0x3b, 0x6e, 0xb3, 0x89, 0x1c, 0x47, 0x91, 0xe6, 0xc1, 0x61, 0xdd, 0x6c, 0xb9, + 0x18, 0x29, 0x72, 0x16, 0xd2, 0x78, 0xf7, 0xed, 0x4a, 0x93, 0x2e, 0xaf, 0x34, 0xe9, 0xc7, 0x95, + 0x26, 0x7d, 0xbc, 0xd6, 0x72, 0x97, 0xd7, 0x5a, 0xee, 0xfb, 0xb5, 0x96, 0x7b, 0xff, 0xb2, 0x1f, + 0xf2, 0x60, 0xd4, 0xa9, 0x75, 0xd9, 0x99, 0x3e, 0x1c, 0x25, 0x41, 0x37, 0xf0, 0xc2, 0x81, 0xa8, + 0x76, 0x45, 0xb9, 0x3b, 0x60, 0x3d, 0x5f, 0x3f, 0xd7, 0xa7, 0xbe, 0x16, 0xe9, 0x05, 0x4e, 0x3a, + 0x45, 0x71, 0xe5, 0x0f, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x45, 0x8d, 0x9b, 0x0c, 0x48, 0x04, + 0x00, 0x00, } func (m *Ballot) Marshal() (dAtA []byte, err error) { From ce6156ca2dfbd0d6b8b127f9e09eed5a8b458d57 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:51:31 +0530 Subject: [PATCH 43/54] feat(ucallback): derive read ballot keys --- x/ucallback/types/ballot.go | 95 +++++++++++++++++++++++++++++++ x/ucallback/types/ballot_test.go | 96 ++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 x/ucallback/types/ballot.go create mode 100644 x/ucallback/types/ballot_test.go diff --git a/x/ucallback/types/ballot.go b/x/ucallback/types/ballot.go new file mode 100644 index 00000000..eeac825c --- /dev/null +++ b/x/ucallback/types/ballot.go @@ -0,0 +1,95 @@ +package types + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "strings" + + "cosmossdk.io/collections" +) + +// ReadBallotDomain separates read-result ballot keys from every other ballot +// namespace on the chain, so a digest collision across modules is not possible. +var ReadBallotDomain = collections.NewPrefix(1) + +// VotesThresholdNumerator / VotesThresholdDenominator give the >2/3 quorum used +// chain-wide. Mirrors x/uexecutor's constants so read ballots finalize on the same +// threshold as inbound and outbound ones. +const ( + VotesThresholdNumerator = 2 + VotesThresholdDenominator = 3 +) + +// DefaultExpiryAfterBlocks is the ballot-level expiry passed to VoteOnBallot. +// +// Set high enough to be inert (~19 years at 6s blocks), matching x/uexecutor. A +// read request already has its own deadline — ReadRequest.ExpiryBlockHeight, set +// by the app and enforced by the contract. Giving the ballot a second, shorter +// clock would let it die while its request is still live, stranding a record that +// can neither fulfil nor expire until the real deadline arrives. +const DefaultExpiryAfterBlocks = 100_000_000 + +// GetReadBallotKey derives the ballot a (requestId, observation) pair votes on. +// +// The ballot model is binary: validators vote SUCCESS or FAILURE on a key that +// already encodes the observation. Agreement is therefore expressed by arriving at +// the same key — two validators reporting different result data produce different +// ballots, and neither reaches quorum until enough validators agree. +// +// Two consequences follow, and both are load-bearing: +// +// 1. Every field that callers must agree on has to be in this digest. Omitting one +// would let validators finalize a ballot while disagreeing about it. +// +// 2. Nothing validator-local may be in it. This is why ReadResult carries no error +// message: free-text error strings differ per validator, so including one would +// scatter honest validators across distinct ballots and quorum would never form. +// x/uexecutor's outbound key does hash an ErrorMsg (keys.go:158) — we deliberately +// do not follow it there. +// +// Aggregates are excluded, see readResultFields. +func GetReadBallotKey(requestID string, result *ReadResult) (string, error) { + if requestID == "" { + return "", fmt.Errorf("cannot derive ballot key: empty request id") + } + if result == nil { + return "", fmt.Errorf("cannot derive ballot key: nil result") + } + + parts := append([]string{strings.ToLower(requestID)}, readResultFields(result)...) + return hashFields(ReadBallotDomain, parts...), nil +} + +// readResultFields renders the consensus-relevant part of an observation. +// +// `aggregates` is deliberately absent. It is reserved for v2 MEDIAN mode, where +// validators submit differing per-field values that are reduced afterwards — the +// opposite of the identical-observation model this key assumes. Hashing it now +// would be harmless (it is always empty in v1) but would silently become +// consensus-breaking the moment v2 populates it: the same read would map to a +// different ballot before and after the upgrade. Excluding it from the start keeps +// v2 a purely additive change. +func readResultFields(r *ReadResult) []string { + return []string{ + fmt.Sprintf("%d", int32(r.Status)), + hex.EncodeToString(r.ResultData), + fmt.Sprintf("%d", r.ObservedBlockHeight), + hex.EncodeToString(r.ObservedBlockHash), + } +} + +// hashFields builds a domain-separated digest over pre-hashed parts, so a value +// containing the ":" join character cannot be made to impersonate a field boundary. +// Same construction as x/uexecutor/types/keys.go:83. +func hashFields(domain collections.Prefix, parts ...string) string { + hashed := make([]string, 0, len(parts)+1) + d := sha256.Sum256(domain.Bytes()) + hashed = append(hashed, hex.EncodeToString(d[:])) + for _, p := range parts { + sum := sha256.Sum256([]byte(p)) + hashed = append(hashed, hex.EncodeToString(sum[:])) + } + final := sha256.Sum256([]byte(strings.Join(hashed, ":"))) + return hex.EncodeToString(final[:]) +} diff --git a/x/ucallback/types/ballot_test.go b/x/ucallback/types/ballot_test.go new file mode 100644 index 00000000..260f5f54 --- /dev/null +++ b/x/ucallback/types/ballot_test.go @@ -0,0 +1,96 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +func result() *types.ReadResult { + return &types.ReadResult{ + Status: types.ReadStatus_READ_STATUS_SUCCESS, + ResultData: []byte{0xde, 0xad}, + ObservedBlockHeight: 8_000_000, + ObservedBlockHash: []byte{0xbe, 0xef}, + } +} + +func keyOf(t *testing.T, id string, r *types.ReadResult) string { + t.Helper() + k, err := types.GetReadBallotKey(id, r) + require.NoError(t, err) + return k +} + +// Identical observations must converge, or quorum can never form. +func TestGetReadBallotKey_IdenticalObservationsAgree(t *testing.T) { + require.Equal(t, keyOf(t, "0xaa", result()), keyOf(t, "0xaa", result())) +} + +// Every consensus-relevant field must move the key. If one did not, validators +// could finalize a ballot while disagreeing about that field. +func TestGetReadBallotKey_EveryFieldIsBinding(t *testing.T) { + base := keyOf(t, "0xaa", result()) + + for name, mutate := range map[string]func(*types.ReadResult){ + "status": func(r *types.ReadResult) { r.Status = types.ReadStatus_READ_STATUS_ERROR }, + "result_data": func(r *types.ReadResult) { r.ResultData = []byte{0x01} }, + "block_height": func(r *types.ReadResult) { r.ObservedBlockHeight = 8_000_001 }, + "block_hash": func(r *types.ReadResult) { r.ObservedBlockHash = []byte{0x02} }, + } { + t.Run(name, func(t *testing.T) { + r := result() + mutate(r) + require.NotEqual(t, base, keyOf(t, "0xaa", r), + "%s must change the ballot key", name) + }) + } + + // and the request id itself + require.NotEqual(t, base, keyOf(t, "0xbb", result())) +} + +// Aggregates are reserved for v2 MEDIAN and must NOT participate. Hashing them now +// would make the v2 rollout consensus-breaking: the same read would map to a +// different ballot before and after aggregates start being populated. +func TestGetReadBallotKey_ExcludesAggregates(t *testing.T) { + withAgg := result() + withAgg.Aggregates = []*types.AggregateValue{ + {ExtractIndex: 0, Mode: 1, Value: []byte{0x09}}, + } + + require.Equal(t, keyOf(t, "0xaa", result()), keyOf(t, "0xaa", withAgg), + "aggregates must not affect the ballot key") +} + +// Field boundaries must not be forgeable by embedding the join character. +func TestGetReadBallotKey_FieldsCannotBleed(t *testing.T) { + a := result() + a.ResultData = []byte("A:B") + b := result() + b.ResultData = []byte("A") + b.ObservedBlockHash = []byte("B") + + require.NotEqual(t, keyOf(t, "0xaa", a), keyOf(t, "0xaa", b)) +} + +// Request ids differing only in case are the same request. +func TestGetReadBallotKey_RequestIDCaseInsensitive(t *testing.T) { + require.Equal(t, keyOf(t, "0xAABB", result()), keyOf(t, "0xaabb", result())) +} + +func TestGetReadBallotKey_Rejects(t *testing.T) { + _, err := types.GetReadBallotKey("", result()) + require.Error(t, err) + + _, err = types.GetReadBallotKey("0xaa", nil) + require.Error(t, err) +} + +// Ballot expiry must stay inert, so the request's own deadline is the only clock. +func TestDefaultExpiryAfterBlocks_IsInert(t *testing.T) { + require.Equal(t, 100_000_000, types.DefaultExpiryAfterBlocks, + "a shorter ballot expiry would let a ballot die while its request is live") +} From de48b0ee1b331612be3910ecc1f06f9f8fa26eec Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:52:21 +0530 Subject: [PATCH 44/54] feat(ucallback): inject uvalidator keeper --- app/app.go | 7 +- x/ucallback/depinject.go | 5 +- x/ucallback/keeper/keeper.go | 6 +- x/ucallback/keeper/keeper_test.go | 4 +- x/ucallback/keeper/uvalidator_fake_test.go | 115 +++++++++++++++++++++ x/ucallback/types/expected_keepers.go | 30 ++++++ 6 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 x/ucallback/keeper/uvalidator_fake_test.go create mode 100644 x/ucallback/types/expected_keepers.go diff --git a/app/app.go b/app/app.go index da3a3a46..120d350c 100644 --- a/app/app.go +++ b/app/app.go @@ -684,12 +684,17 @@ func NewChainApp( // If evidence needs to be handled for the app, set routes in router here and seal app.EvidenceKeeper = *evidenceKeeper - // Create the ucallback Keeper + // Create the ucallback Keeper. + // + // UvalidatorKeeper is constructed further down, so this takes a pointer to the + // field rather than its value — same pattern as UexecutorKeeper below. The + // pointer is stable; the value it refers to is populated before any tx runs. app.UcallbackKeeper = ucallbackkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(keys[ucallbacktypes.StoreKey]), logger, authtypes.NewModuleAddress(govtypes.ModuleName).String(), + &app.UvalidatorKeeper, ) app.FeeMarketKeeper = feemarketkeeper.NewKeeper( diff --git a/x/ucallback/depinject.go b/x/ucallback/depinject.go index 53ce9a93..26aa352a 100755 --- a/x/ucallback/depinject.go +++ b/x/ucallback/depinject.go @@ -18,6 +18,7 @@ import ( modulev1 "github.com/pushchain/push-chain-node/api/ucallback/module/v1" "github.com/pushchain/push-chain-node/x/ucallback/keeper" + "github.com/pushchain/push-chain-node/x/ucallback/types" ) var _ appmodule.AppModule = AppModule{} @@ -44,6 +45,8 @@ type ModuleInputs struct { StakingKeeper stakingkeeper.Keeper SlashingKeeper slashingkeeper.Keeper + + UvalidatorKeeper types.UValidatorKeeper } type ModuleOutputs struct { @@ -56,7 +59,7 @@ type ModuleOutputs struct { func ProvideModule(in ModuleInputs) ModuleOutputs { govAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String() - k := keeper.NewKeeper(in.Cdc, in.StoreService, log.NewLogger(os.Stderr), govAddr) + k := keeper.NewKeeper(in.Cdc, in.StoreService, log.NewLogger(os.Stderr), govAddr, in.UvalidatorKeeper) m := NewAppModule(in.Cdc, k) return ModuleOutputs{Module: m, Keeper: k, Out: depinject.Out{}} diff --git a/x/ucallback/keeper/keeper.go b/x/ucallback/keeper/keeper.go index 9c4db40b..2a76416c 100755 --- a/x/ucallback/keeper/keeper.go +++ b/x/ucallback/keeper/keeper.go @@ -36,6 +36,8 @@ type Keeper struct { // Push transaction can be listed together. ReadsByTxHash collections.KeySet[collections.Pair[string, string]] + uvalidatorKeeper types.UValidatorKeeper + authority string } @@ -45,6 +47,7 @@ func NewKeeper( storeService storetypes.KVStoreService, logger log.Logger, authority string, + uvalidatorKeeper types.UValidatorKeeper, ) Keeper { logger = logger.With(log.ModuleKey, "x/"+types.ModuleName) @@ -73,7 +76,8 @@ func NewKeeper( collections.PairKeyCodec(collections.StringKey, collections.StringKey), ), - authority: authority, + uvalidatorKeeper: uvalidatorKeeper, + authority: authority, } schema, err := sb.Build() diff --git a/x/ucallback/keeper/keeper_test.go b/x/ucallback/keeper/keeper_test.go index 7c1d711a..8c742f7e 100755 --- a/x/ucallback/keeper/keeper_test.go +++ b/x/ucallback/keeper/keeper_test.go @@ -47,6 +47,7 @@ type testFixture struct { k keeper.Keeper msgServer types.MsgServer queryServer types.QueryServer + uvalidator *fakeUValidator appModule *module.AppModule accountkeeper authkeeper.AccountKeeper @@ -86,7 +87,8 @@ func SetupTest(t *testing.T) *testFixture { registerBaseSDKModules(logger, f, encCfg, keys, accountAddressCodec, validatorAddressCodec, consensusAddressCodec) // Setup Keeper. - f.k = keeper.NewKeeper(encCfg.Codec, runtime.NewKVStoreService(keys[types.ModuleName]), logger, f.govModAddr) + f.uvalidator = newFakeUValidator() + f.k = keeper.NewKeeper(encCfg.Codec, runtime.NewKVStoreService(keys[types.ModuleName]), logger, f.govModAddr, f.uvalidator) f.msgServer = keeper.NewMsgServerImpl(f.k) f.queryServer = keeper.NewQuerier(f.k) f.appModule = module.NewAppModule(encCfg.Codec, f.k) diff --git a/x/ucallback/keeper/uvalidator_fake_test.go b/x/ucallback/keeper/uvalidator_fake_test.go new file mode 100644 index 00000000..2bdb7c83 --- /dev/null +++ b/x/ucallback/keeper/uvalidator_fake_test.go @@ -0,0 +1,115 @@ +package keeper_test + +import ( + "context" + "fmt" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + uvalidatortypes "github.com/pushchain/push-chain-node/x/uvalidator/types" +) + +// fakeUValidator is an in-memory stand-in for x/uvalidator. +// +// It tallies votes for real rather than returning a canned isFinalized, because +// the behaviour under test is precisely that two validators reporting different +// observations land on different ballots and neither reaches quorum. A stub that +// ignored the ballot key could not distinguish that from success. +type fakeUValidator struct { + voters []string + bonded map[string]bool + tombstoned map[string]bool + + ballots map[string]*fakeBallot + + // set to force an error out of the corresponding call + votersErr error + voteErr error +} + +type fakeBallot struct { + observationType uvalidatortypes.BallotObservationType + votes map[string]uvalidatortypes.VoteResult + finalized bool + expiryBlocks int64 +} + +var _ types.UValidatorKeeper = (*fakeUValidator)(nil) + +func newFakeUValidator(voters ...string) *fakeUValidator { + f := &fakeUValidator{ + voters: voters, + bonded: map[string]bool{}, + tombstoned: map[string]bool{}, + ballots: map[string]*fakeBallot{}, + } + for _, v := range voters { + f.bonded[v] = true + } + return f +} + +func (f *fakeUValidator) IsBondedUniversalValidator(_ context.Context, v string) (bool, error) { + return f.bonded[v], nil +} + +func (f *fakeUValidator) IsTombstonedUniversalValidator(_ context.Context, v string) (bool, error) { + return f.tombstoned[v], nil +} + +func (f *fakeUValidator) GetEligibleVoters(_ context.Context) ([]uvalidatortypes.UniversalValidator, error) { + if f.votersErr != nil { + return nil, f.votersErr + } + out := make([]uvalidatortypes.UniversalValidator, 0, len(f.voters)) + for _, v := range f.voters { + out = append(out, uvalidatortypes.UniversalValidator{ + IdentifyInfo: &uvalidatortypes.IdentityInfo{CoreValidatorAddress: v}, + }) + } + return out, nil +} + +func (f *fakeUValidator) VoteOnBallot( + _ context.Context, + id string, + ballotType uvalidatortypes.BallotObservationType, + voter string, + voteResult uvalidatortypes.VoteResult, + _ []string, + votesNeeded int64, + expiryAfterBlocks int64, +) (uvalidatortypes.Ballot, bool, bool, error) { + if f.voteErr != nil { + return uvalidatortypes.Ballot{}, false, false, f.voteErr + } + + b, existed := f.ballots[id] + if !existed { + b = &fakeBallot{ + observationType: ballotType, + votes: map[string]uvalidatortypes.VoteResult{}, + expiryBlocks: expiryAfterBlocks, + } + f.ballots[id] = b + } + + if b.finalized { + return uvalidatortypes.Ballot{Id: id}, true, false, nil + } + if _, dup := b.votes[voter]; dup { + return uvalidatortypes.Ballot{Id: id}, false, false, + fmt.Errorf("validator %s already voted on ballot %s", voter, id) + } + + b.votes[voter] = voteResult + b.finalized = int64(len(b.votes)) >= votesNeeded + + return uvalidatortypes.Ballot{Id: id}, b.finalized, !existed, nil +} + +// ballotCount reports how many distinct ballots have been opened — the signal that +// validators diverged on what they observed. +func (f *fakeUValidator) ballotCount() int { return len(f.ballots) } + +// errTest is a sentinel for injecting failures into the fake. +var errTest = fmt.Errorf("injected test failure") diff --git a/x/ucallback/types/expected_keepers.go b/x/ucallback/types/expected_keepers.go new file mode 100644 index 00000000..3c6e8058 --- /dev/null +++ b/x/ucallback/types/expected_keepers.go @@ -0,0 +1,30 @@ +package types + +import ( + "context" + + uvalidatortypes "github.com/pushchain/push-chain-node/x/uvalidator/types" +) + +// UValidatorKeeper is the slice of x/uvalidator that x/ucallback needs to run a +// read-result ballot. Narrower than x/uexecutor's equivalent — read voting needs +// the voter set, the ballot primitive, and the two eligibility checks, nothing more. +type UValidatorKeeper interface { + IsBondedUniversalValidator(ctx context.Context, universalValidator string) (bool, error) + IsTombstonedUniversalValidator(ctx context.Context, universalValidator string) (bool, error) + GetEligibleVoters(ctx context.Context) ([]uvalidatortypes.UniversalValidator, error) + VoteOnBallot( + ctx context.Context, + id string, + ballotType uvalidatortypes.BallotObservationType, + voter string, + voteResult uvalidatortypes.VoteResult, + voters []string, + votesNeeded int64, + expiryAfterBlocks int64, + ) ( + ballot uvalidatortypes.Ballot, + isFinalized bool, + isNew bool, + err error) +} From 19be403ae8ad092fe9396ab7c3b385de6870e2dd Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:53:08 +0530 Subject: [PATCH 45/54] feat(ucallback): vote on read results --- x/ucallback/keeper/voting.go | 149 +++++++++++++++++++ x/ucallback/keeper/voting_test.go | 235 ++++++++++++++++++++++++++++++ 2 files changed, 384 insertions(+) create mode 100644 x/ucallback/keeper/voting.go create mode 100644 x/ucallback/keeper/voting_test.go diff --git a/x/ucallback/keeper/voting.go b/x/ucallback/keeper/voting.go new file mode 100644 index 00000000..b116c2e3 --- /dev/null +++ b/x/ucallback/keeper/voting.go @@ -0,0 +1,149 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/pushchain/push-chain-node/x/ucallback/types" + uvalidatortypes "github.com/pushchain/push-chain-node/x/uvalidator/types" +) + +// VoteOnReadBallot casts one validator's vote on the ballot for (requestID, result) +// and reports whether that vote carried it to quorum. +// +// Mirrors x/uexecutor's VoteOnOutboundBallot: same >2/3 threshold, same eligible +// voter set, same inert ballot expiry. +func (k Keeper) VoteOnReadBallot( + ctx context.Context, + universalValidator sdk.ValAddress, + requestID string, + result *types.ReadResult, +) (ballotKey string, isFinalized bool, isNew bool, err error) { + ballotKey, err = types.GetReadBallotKey(requestID, result) + if err != nil { + return "", false, false, err + } + + voters, err := k.uvalidatorKeeper.GetEligibleVoters(ctx) + if err != nil { + return "", false, false, err + } + if len(voters) == 0 { + return "", false, false, fmt.Errorf("no eligible universal validators") + } + + // votesNeeded = floor(2/3 * n) + 1, i.e. a strict >2/3 majority, matching + // tendermint and every other ballot on this chain. + votesNeeded := (types.VotesThresholdNumerator*len(voters))/types.VotesThresholdDenominator + 1 + + voterAddrs := make([]string, len(voters)) + for i, v := range voters { + voterAddrs[i] = v.IdentifyInfo.CoreValidatorAddress + } + + k.Logger().Debug("voting on read ballot", + "ballot_key", ballotKey, + "request_id", requestID, + "validator", universalValidator.String(), + "total_validators", len(voters), + "votes_needed", votesNeeded, + ) + + _, isFinalized, isNew, err = k.uvalidatorKeeper.VoteOnBallot( + ctx, + ballotKey, + uvalidatortypes.BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT, + universalValidator.String(), + // Always SUCCESS: disagreement is expressed by landing on a different + // ballot key, not by voting FAILURE on a shared one. A FAILURE vote here + // would mean "this exact observation is wrong", which no validator is in a + // position to assert — it only knows what it observed itself. + uvalidatortypes.VoteResult_VOTE_RESULT_SUCCESS, + voterAddrs, + int64(votesNeeded), + int64(types.DefaultExpiryAfterBlocks), + ) + if err != nil { + return "", false, false, err + } + + if isNew { + k.Logger().Debug("read ballot created", "ballot_key", ballotKey, "request_id", requestID) + } + if isFinalized { + k.Logger().Info("read ballot finalized", "ballot_key", ballotKey, "request_id", requestID) + } + + return ballotKey, isFinalized, isNew, nil +} + +// VoteReadResult records a universal validator's observation of a read request. +// +// Reaching quorum here does NOT fulfil the request — it only settles what was +// observed. The fulfilment EVM call is driven by the ballot terminal hook (C7), so +// that it runs exactly once no matter which validator's vote happened to be the +// deciding one. +func (k Keeper) VoteReadResult( + ctx context.Context, + universalValidator sdk.ValAddress, + requestID string, + result *types.ReadResult, +) (bool, error) { + if result == nil { + return false, fmt.Errorf("read result is required") + } + + ur, found := k.GetUniversalRead(ctx, requestID) + if !found { + return false, fmt.Errorf("read request not found: %s", requestID) + } + + // Only unsettled requests accept votes. Without this a validator could keep + // voting on a request that already fulfilled, creating ballots that the + // terminal hook would then try to act on a second time. + if isSettled(ur.Status) { + return false, fmt.Errorf("read request %s is already %s", requestID, ur.Status) + } + + // Reject votes on a request whose deadline has passed. AllPendingReadRequests + // already withholds these, so an honest validator will not be voting on one — + // but the query is a convenience, not the enforcement point. + sdkCtx := sdk.UnwrapSDKContext(ctx) + if ur.Request != nil && ur.Request.ExpiryBlockHeight <= uint64(sdkCtx.BlockHeight()) { + return false, fmt.Errorf("read request %s expired at height %d", + requestID, ur.Request.ExpiryBlockHeight) + } + + // Cache the vote so a failure partway through leaves no half-written ballot. + tmpCtx, commit := sdkCtx.CacheContext() + + ballotKey, isFinalized, _, err := k.VoteOnReadBallot(tmpCtx, universalValidator, requestID, result) + if err != nil { + return false, err + } + + // The record follows the ballot that reached quorum, not the first ballot + // opened. Until one finalizes, ballot_key points at whichever observation this + // validator's vote most recently landed on. + ur.Status = types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING + ur.BallotKey = ballotKey + if isFinalized { + ur.Result = result + } + if err := k.SetUniversalRead(tmpCtx, ur); err != nil { + return false, err + } + + commit() + + k.Logger().Info("read result vote recorded", + "request_id", requestID, + "validator", universalValidator.String(), + "ballot_key", ballotKey, + "finalized", isFinalized, + ) + + return isFinalized, nil +} diff --git a/x/ucallback/keeper/voting_test.go b/x/ucallback/keeper/voting_test.go new file mode 100644 index 00000000..4dab533b --- /dev/null +++ b/x/ucallback/keeper/voting_test.go @@ -0,0 +1,235 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/pushchain/push-chain-node/x/ucallback/types" +) + +func obs(data byte) *types.ReadResult { + return &types.ReadResult{ + Status: types.ReadStatus_READ_STATUS_SUCCESS, + ResultData: []byte{data}, + ObservedBlockHeight: 8_000_000, + ObservedBlockHash: []byte{0xbe, 0xef}, + } +} + +// seedVoters configures n eligible validators and returns their val addresses. +func seedVoters(t *testing.T, f *testFixture, n int) []sdk.ValAddress { + t.Helper() + addrs := make([]sdk.ValAddress, n) + names := make([]string, n) + for i := 0; i < n; i++ { + addrs[i] = sdk.ValAddress(f.addrs[i%len(f.addrs)]) + // keep them distinct even when recycling the base accounts + names[i] = addrs[i].String() + string(rune('a'+i)) + addrs[i] = sdk.ValAddress(names[i]) + } + f.uvalidator.voters = names + for _, nm := range names { + f.uvalidator.bonded[nm] = true + } + return addrs +} + +func seedRead(t *testing.T, f *testFixture, id string, expiry uint64) { + t.Helper() + require.NoError(t, f.k.SetUniversalRead(f.ctx, + newRead(id, "0xTX", expiry, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING))) +} + +// A single vote below quorum moves the request to VOTING and attaches a ballot, +// but does not settle it. +func TestVoteReadResult_FirstVoteDoesNotFinalize(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + finalized, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", obs(0x01)) + require.NoError(t, err) + require.False(t, finalized, "1 of 4 is below the >2/3 threshold") + + ur, found := f.k.GetUniversalRead(f.ctx, "0xaa") + require.True(t, found) + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING, ur.Status) + require.NotEmpty(t, ur.BallotKey) + require.Nil(t, ur.Result, "result is only attached once the ballot finalizes") + + // still offered to validators — the rest have not voted yet + require.Equal(t, []string{"0xaa"}, pendingIDs(t, f)) +} + +// Agreement on the same observation reaches quorum at floor(2/3n)+1. +func TestVoteReadResult_QuorumFinalizes(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) // votesNeeded = (2*4)/3 + 1 = 3 + seedRead(t, f, "0xaa", 500) + + for i := 0; i < 2; i++ { + finalized, err := f.k.VoteReadResult(f.ctx, v[i], "0xaa", obs(0x01)) + require.NoError(t, err) + require.False(t, finalized, "vote %d must not finalize", i+1) + } + + finalized, err := f.k.VoteReadResult(f.ctx, v[2], "0xaa", obs(0x01)) + require.NoError(t, err) + require.True(t, finalized, "third of four carries the ballot") + + ur, found := f.k.GetUniversalRead(f.ctx, "0xaa") + require.True(t, found) + require.NotNil(t, ur.Result, "the winning observation is attached") + require.Equal(t, []byte{0x01}, ur.Result.ResultData) + + require.Equal(t, 1, f.uvalidator.ballotCount(), "agreement means one ballot") +} + +// Divergent observations open separate ballots and neither reaches quorum. This is +// the core property of the design: agreement is expressed by arriving at the same +// key, so disagreement simply fails to accumulate. +func TestVoteReadResult_DivergentObservationsSplitBallots(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + for i, data := range []byte{0x01, 0x02, 0x03} { + finalized, err := f.k.VoteReadResult(f.ctx, v[i], "0xaa", obs(data)) + require.NoError(t, err) + require.False(t, finalized, "observation %d must not finalize alone", i) + } + + require.Equal(t, 3, f.uvalidator.ballotCount(), + "three distinct observations must produce three ballots") + + ur, _ := f.k.GetUniversalRead(f.ctx, "0xaa") + require.Nil(t, ur.Result, "no observation won, so none is recorded") + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_VOTING, ur.Status) +} + +// A minority that diverges cannot stop the majority from finalizing. +func TestVoteReadResult_MinorityDivergenceDoesNotBlock(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", obs(0xff)) // the outlier + require.NoError(t, err) + + for i := 1; i <= 2; i++ { + _, err := f.k.VoteReadResult(f.ctx, v[i], "0xaa", obs(0x01)) + require.NoError(t, err) + } + finalized, err := f.k.VoteReadResult(f.ctx, v[3], "0xaa", obs(0x01)) + require.NoError(t, err) + require.True(t, finalized) + + ur, _ := f.k.GetUniversalRead(f.ctx, "0xaa") + require.Equal(t, []byte{0x01}, ur.Result.ResultData, "the majority observation wins") +} + +func TestVoteReadResult_RejectsUnknownRequest(t *testing.T) { + f := SetupTest(t) + v := seedVoters(t, f, 4) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xmissing", obs(0x01)) + require.ErrorContains(t, err, "not found") +} + +// Once settled, further votes must be refused — otherwise the terminal hook could +// be driven a second time. +func TestVoteReadResult_RejectsSettled(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + + for _, st := range []types.UniversalReadStatus{ + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FULFILLED, + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_EXPIRED, + types.UniversalReadStatus_UNIVERSAL_READ_STATUS_FAILED, + } { + id := "0x" + st.String() + require.NoError(t, f.k.SetUniversalRead(f.ctx, newRead(id, "0xTX", 500, st))) + _, err := f.k.VoteReadResult(f.ctx, v[0], id, obs(0x01)) + require.ErrorContains(t, err, "already", "status %s must reject votes", st) + } +} + +// Past its deadline a request stops accepting votes, independently of whether the +// sweeper has retired it yet. +func TestVoteReadResult_RejectsExpired(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(100) + v := seedVoters(t, f, 4) + + seedRead(t, f, "0xpast", 50) + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xpast", obs(0x01)) + require.ErrorContains(t, err, "expired") + + // exactly at the expiry height is already too late, matching the query filter + seedRead(t, f, "0xnow", 100) + _, err = f.k.VoteReadResult(f.ctx, v[0], "0xnow", obs(0x01)) + require.ErrorContains(t, err, "expired") + + seedRead(t, f, "0xlive", 101) + _, err = f.k.VoteReadResult(f.ctx, v[0], "0xlive", obs(0x01)) + require.NoError(t, err) +} + +func TestVoteReadResult_RejectsNilResult(t *testing.T) { + f := SetupTest(t) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", nil) + require.Error(t, err) +} + +// A failure inside voting must leave no trace — the request stays exactly as it was. +func TestVoteReadResult_FailureIsAtomic(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + f.uvalidator.voteErr = errTest + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", obs(0x01)) + require.Error(t, err) + + ur, found := f.k.GetUniversalRead(f.ctx, "0xaa") + require.True(t, found) + require.Equal(t, types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, ur.Status, + "a failed vote must not advance the request") + require.Empty(t, ur.BallotKey) +} + +func TestVoteReadResult_RejectsWhenNoVoters(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + seedRead(t, f, "0xaa", 500) + + _, err := f.k.VoteReadResult(f.ctx, sdk.ValAddress("nobody"), "0xaa", obs(0x01)) + require.ErrorContains(t, err, "no eligible") +} + +// The ballot the record points at is the one the terminal hook will resolve back. +func TestVoteReadResult_BallotResolvesBackToRequest(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", obs(0x01)) + require.NoError(t, err) + + ur, _ := f.k.GetUniversalRead(f.ctx, "0xaa") + back, found := f.k.GetUniversalReadByBallot(f.ctx, ur.BallotKey) + require.True(t, found, "the terminal hook must be able to find this request") + require.Equal(t, "0xaa", back.Id) +} From e9bb91f18b31c7161412bd38af683531008a8e99 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 12:53:49 +0530 Subject: [PATCH 46/54] feat(ucallback): add MsgVoteReadResult --- api/ucallback/v1/tx.pulsar.go | 1217 ++++++++++++++++++++++++++++-- api/ucallback/v1/tx_grpc.pb.go | 43 +- proto/ucallback/v1/tx.proto | 33 + x/ucallback/autocli.go | 8 + x/ucallback/keeper/msg_server.go | 41 + x/ucallback/types/tx.pb.go | 547 +++++++++++++- 6 files changed, 1821 insertions(+), 68 deletions(-) diff --git a/api/ucallback/v1/tx.pulsar.go b/api/ucallback/v1/tx.pulsar.go index 0f28dd7b..321b1fa9 100644 --- a/api/ucallback/v1/tx.pulsar.go +++ b/api/ucallback/v1/tx.pulsar.go @@ -2,6 +2,7 @@ package ucallbackv1 import ( + _ "cosmossdk.io/api/amino" _ "cosmossdk.io/api/cosmos/msg/v1" fmt "fmt" _ "github.com/cosmos/cosmos-proto" @@ -870,6 +871,979 @@ func (x *fastReflection_MsgUpdateParamsResponse) ProtoMethods() *protoiface.Meth } } +var ( + md_MsgVoteReadResult protoreflect.MessageDescriptor + fd_MsgVoteReadResult_signer protoreflect.FieldDescriptor + fd_MsgVoteReadResult_request_id protoreflect.FieldDescriptor + fd_MsgVoteReadResult_result protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_tx_proto_init() + md_MsgVoteReadResult = File_ucallback_v1_tx_proto.Messages().ByName("MsgVoteReadResult") + fd_MsgVoteReadResult_signer = md_MsgVoteReadResult.Fields().ByName("signer") + fd_MsgVoteReadResult_request_id = md_MsgVoteReadResult.Fields().ByName("request_id") + fd_MsgVoteReadResult_result = md_MsgVoteReadResult.Fields().ByName("result") +} + +var _ protoreflect.Message = (*fastReflection_MsgVoteReadResult)(nil) + +type fastReflection_MsgVoteReadResult MsgVoteReadResult + +func (x *MsgVoteReadResult) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgVoteReadResult)(x) +} + +func (x *MsgVoteReadResult) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_tx_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgVoteReadResult_messageType fastReflection_MsgVoteReadResult_messageType +var _ protoreflect.MessageType = fastReflection_MsgVoteReadResult_messageType{} + +type fastReflection_MsgVoteReadResult_messageType struct{} + +func (x fastReflection_MsgVoteReadResult_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgVoteReadResult)(nil) +} +func (x fastReflection_MsgVoteReadResult_messageType) New() protoreflect.Message { + return new(fastReflection_MsgVoteReadResult) +} +func (x fastReflection_MsgVoteReadResult_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgVoteReadResult +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgVoteReadResult) Descriptor() protoreflect.MessageDescriptor { + return md_MsgVoteReadResult +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgVoteReadResult) Type() protoreflect.MessageType { + return _fastReflection_MsgVoteReadResult_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgVoteReadResult) New() protoreflect.Message { + return new(fastReflection_MsgVoteReadResult) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgVoteReadResult) Interface() protoreflect.ProtoMessage { + return (*MsgVoteReadResult)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgVoteReadResult) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Signer != "" { + value := protoreflect.ValueOfString(x.Signer) + if !f(fd_MsgVoteReadResult_signer, value) { + return + } + } + if x.RequestId != "" { + value := protoreflect.ValueOfString(x.RequestId) + if !f(fd_MsgVoteReadResult_request_id, value) { + return + } + } + if x.Result != nil { + value := protoreflect.ValueOfMessage(x.Result.ProtoReflect()) + if !f(fd_MsgVoteReadResult_result, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgVoteReadResult) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResult.signer": + return x.Signer != "" + case "ucallback.v1.MsgVoteReadResult.request_id": + return x.RequestId != "" + case "ucallback.v1.MsgVoteReadResult.result": + return x.Result != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResult) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResult.signer": + x.Signer = "" + case "ucallback.v1.MsgVoteReadResult.request_id": + x.RequestId = "" + case "ucallback.v1.MsgVoteReadResult.result": + x.Result = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgVoteReadResult) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.MsgVoteReadResult.signer": + value := x.Signer + return protoreflect.ValueOfString(value) + case "ucallback.v1.MsgVoteReadResult.request_id": + value := x.RequestId + return protoreflect.ValueOfString(value) + case "ucallback.v1.MsgVoteReadResult.result": + value := x.Result + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResult) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResult.signer": + x.Signer = value.Interface().(string) + case "ucallback.v1.MsgVoteReadResult.request_id": + x.RequestId = value.Interface().(string) + case "ucallback.v1.MsgVoteReadResult.result": + x.Result = value.Message().Interface().(*ReadResult) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResult) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResult.result": + if x.Result == nil { + x.Result = new(ReadResult) + } + return protoreflect.ValueOfMessage(x.Result.ProtoReflect()) + case "ucallback.v1.MsgVoteReadResult.signer": + panic(fmt.Errorf("field signer of message ucallback.v1.MsgVoteReadResult is not mutable")) + case "ucallback.v1.MsgVoteReadResult.request_id": + panic(fmt.Errorf("field request_id of message ucallback.v1.MsgVoteReadResult is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgVoteReadResult) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResult.signer": + return protoreflect.ValueOfString("") + case "ucallback.v1.MsgVoteReadResult.request_id": + return protoreflect.ValueOfString("") + case "ucallback.v1.MsgVoteReadResult.result": + m := new(ReadResult) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResult")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResult does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgVoteReadResult) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.MsgVoteReadResult", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgVoteReadResult) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResult) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgVoteReadResult) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgVoteReadResult) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgVoteReadResult) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Signer) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.RequestId) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.Result != nil { + l = options.Size(x.Result) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgVoteReadResult) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Result != nil { + encoded, err := options.Marshal(x.Result) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x1a + } + if len(x.RequestId) > 0 { + i -= len(x.RequestId) + copy(dAtA[i:], x.RequestId) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.RequestId))) + i-- + dAtA[i] = 0x12 + } + if len(x.Signer) > 0 { + i -= len(x.Signer) + copy(dAtA[i:], x.Signer) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Signer))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgVoteReadResult) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgVoteReadResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgVoteReadResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Result == nil { + x.Result = &ReadResult{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Result); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_MsgVoteReadResultResponse protoreflect.MessageDescriptor + fd_MsgVoteReadResultResponse_finalized protoreflect.FieldDescriptor +) + +func init() { + file_ucallback_v1_tx_proto_init() + md_MsgVoteReadResultResponse = File_ucallback_v1_tx_proto.Messages().ByName("MsgVoteReadResultResponse") + fd_MsgVoteReadResultResponse_finalized = md_MsgVoteReadResultResponse.Fields().ByName("finalized") +} + +var _ protoreflect.Message = (*fastReflection_MsgVoteReadResultResponse)(nil) + +type fastReflection_MsgVoteReadResultResponse MsgVoteReadResultResponse + +func (x *MsgVoteReadResultResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgVoteReadResultResponse)(x) +} + +func (x *MsgVoteReadResultResponse) slowProtoReflect() protoreflect.Message { + mi := &file_ucallback_v1_tx_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgVoteReadResultResponse_messageType fastReflection_MsgVoteReadResultResponse_messageType +var _ protoreflect.MessageType = fastReflection_MsgVoteReadResultResponse_messageType{} + +type fastReflection_MsgVoteReadResultResponse_messageType struct{} + +func (x fastReflection_MsgVoteReadResultResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgVoteReadResultResponse)(nil) +} +func (x fastReflection_MsgVoteReadResultResponse_messageType) New() protoreflect.Message { + return new(fastReflection_MsgVoteReadResultResponse) +} +func (x fastReflection_MsgVoteReadResultResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgVoteReadResultResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgVoteReadResultResponse) Descriptor() protoreflect.MessageDescriptor { + return md_MsgVoteReadResultResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgVoteReadResultResponse) Type() protoreflect.MessageType { + return _fastReflection_MsgVoteReadResultResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgVoteReadResultResponse) New() protoreflect.Message { + return new(fastReflection_MsgVoteReadResultResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgVoteReadResultResponse) Interface() protoreflect.ProtoMessage { + return (*MsgVoteReadResultResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgVoteReadResultResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Finalized != false { + value := protoreflect.ValueOfBool(x.Finalized) + if !f(fd_MsgVoteReadResultResponse_finalized, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgVoteReadResultResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + return x.Finalized != false + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResultResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + x.Finalized = false + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgVoteReadResultResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + value := x.Finalized + return protoreflect.ValueOfBool(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResultResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + x.Finalized = value.Bool() + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResultResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + panic(fmt.Errorf("field finalized of message ucallback.v1.MsgVoteReadResultResponse is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgVoteReadResultResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "ucallback.v1.MsgVoteReadResultResponse.finalized": + return protoreflect.ValueOfBool(false) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: ucallback.v1.MsgVoteReadResultResponse")) + } + panic(fmt.Errorf("message ucallback.v1.MsgVoteReadResultResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgVoteReadResultResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in ucallback.v1.MsgVoteReadResultResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgVoteReadResultResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgVoteReadResultResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgVoteReadResultResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgVoteReadResultResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgVoteReadResultResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.Finalized { + n += 2 + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgVoteReadResultResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.Finalized { + i-- + if x.Finalized { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgVoteReadResultResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgVoteReadResultResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgVoteReadResultResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Finalized", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + x.Finalized = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.0 @@ -963,6 +1937,104 @@ func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { return file_ucallback_v1_tx_proto_rawDescGZIP(), []int{1} } +// MsgVoteReadResult is broadcast by a universal validator that has executed a +// read request against the destination chain. +// +// The ballot the vote lands on is derived from (request_id, result), so two +// validators reporting the same observation converge on one ballot and any +// disagreement produces a distinct ballot that never reaches quorum. Nothing +// validator-local may appear in `result` for that reason — notably there is no +// error message field. +type MsgVoteReadResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // signer is the Cosmos address of the voting universal validator. + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // request_id identifies the read request being voted on. + RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + // result is the observation. Every field participates in the ballot key. + Result *ReadResult `protobuf:"bytes,3,opt,name=result,proto3" json:"result,omitempty"` +} + +func (x *MsgVoteReadResult) Reset() { + *x = MsgVoteReadResult{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_tx_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgVoteReadResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgVoteReadResult) ProtoMessage() {} + +// Deprecated: Use MsgVoteReadResult.ProtoReflect.Descriptor instead. +func (*MsgVoteReadResult) Descriptor() ([]byte, []int) { + return file_ucallback_v1_tx_proto_rawDescGZIP(), []int{2} +} + +func (x *MsgVoteReadResult) GetSigner() string { + if x != nil { + return x.Signer + } + return "" +} + +func (x *MsgVoteReadResult) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *MsgVoteReadResult) GetResult() *ReadResult { + if x != nil { + return x.Result + } + return nil +} + +type MsgVoteReadResultResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // finalized reports whether this vote carried the ballot to quorum. + Finalized bool `protobuf:"varint,1,opt,name=finalized,proto3" json:"finalized,omitempty"` +} + +func (x *MsgVoteReadResultResponse) Reset() { + *x = MsgVoteReadResultResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ucallback_v1_tx_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgVoteReadResultResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgVoteReadResultResponse) ProtoMessage() {} + +// Deprecated: Use MsgVoteReadResultResponse.ProtoReflect.Descriptor instead. +func (*MsgVoteReadResultResponse) Descriptor() ([]byte, []int) { + return file_ucallback_v1_tx_proto_rawDescGZIP(), []int{3} +} + +func (x *MsgVoteReadResultResponse) GetFinalized() bool { + if x != nil { + return x.Finalized + } + return false +} + var File_ucallback_v1_tx_proto protoreflect.FileDescriptor var file_ucallback_v1_tx_proto_rawDesc = []byte{ @@ -971,39 +2043,63 @@ var file_ucallback_v1_tx_proto_rawDesc = []byte{ 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x6e, - 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x0f, - 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, - 0x36, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, - 0xde, 0x1f, 0x00, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x0e, 0x82, 0xe7, 0xb0, - 0x2a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x4d, - 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x62, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x54, 0x0a, - 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1d, 0x2e, - 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x25, 0x2e, 0x75, - 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, + 0x65, 0x73, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x75, 0x63, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x6d, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x6d, 0x69, + 0x6e, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x0f, 0x4d, 0x73, 0x67, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x09, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, + 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x0e, 0x82, 0xe7, 0xb0, 0x2a, 0x09, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x1a, 0x05, 0x80, 0xe7, 0xb0, 0x2a, 0x01, 0x42, 0xaf, 0x01, 0x0a, 0x10, 0x63, - 0x6f, 0x6d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, - 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, - 0x31, 0x3b, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, - 0x55, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, - 0x56, 0x31, 0xca, 0x02, 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x18, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, - 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x4d, 0x73, 0x67, 0x56, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, + 0x69, 0x6e, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x75, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3a, 0x2b, 0x82, 0xe7, + 0xb0, 0x2a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x8a, 0xe7, 0xb0, 0x2a, 0x1b, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x4d, 0x73, 0x67, 0x56, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x39, 0x0a, 0x19, 0x4d, 0x73, 0x67, + 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x7a, 0x65, 0x64, 0x32, 0xbe, 0x01, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x5a, 0x0a, 0x0e, + 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, + 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, + 0x67, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x1a, + 0x27, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x73, 0x67, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1d, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x25, 0x2e, 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0x05, + 0x80, 0xe7, 0xb0, 0x2a, 0x01, 0x42, 0xaf, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x70, 0x75, 0x73, 0x68, + 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x75, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x63, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, + 0x0c, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, + 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x55, + 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x55, 0x63, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1018,21 +2114,27 @@ func file_ucallback_v1_tx_proto_rawDescGZIP() []byte { return file_ucallback_v1_tx_proto_rawDescData } -var file_ucallback_v1_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ucallback_v1_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_ucallback_v1_tx_proto_goTypes = []interface{}{ - (*MsgUpdateParams)(nil), // 0: ucallback.v1.MsgUpdateParams - (*MsgUpdateParamsResponse)(nil), // 1: ucallback.v1.MsgUpdateParamsResponse - (*Params)(nil), // 2: ucallback.v1.Params + (*MsgUpdateParams)(nil), // 0: ucallback.v1.MsgUpdateParams + (*MsgUpdateParamsResponse)(nil), // 1: ucallback.v1.MsgUpdateParamsResponse + (*MsgVoteReadResult)(nil), // 2: ucallback.v1.MsgVoteReadResult + (*MsgVoteReadResultResponse)(nil), // 3: ucallback.v1.MsgVoteReadResultResponse + (*Params)(nil), // 4: ucallback.v1.Params + (*ReadResult)(nil), // 5: ucallback.v1.ReadResult } var file_ucallback_v1_tx_proto_depIdxs = []int32{ - 2, // 0: ucallback.v1.MsgUpdateParams.params:type_name -> ucallback.v1.Params - 0, // 1: ucallback.v1.Msg.UpdateParams:input_type -> ucallback.v1.MsgUpdateParams - 1, // 2: ucallback.v1.Msg.UpdateParams:output_type -> ucallback.v1.MsgUpdateParamsResponse - 2, // [2:3] is the sub-list for method output_type - 1, // [1:2] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 4, // 0: ucallback.v1.MsgUpdateParams.params:type_name -> ucallback.v1.Params + 5, // 1: ucallback.v1.MsgVoteReadResult.result:type_name -> ucallback.v1.ReadResult + 2, // 2: ucallback.v1.Msg.VoteReadResult:input_type -> ucallback.v1.MsgVoteReadResult + 0, // 3: ucallback.v1.Msg.UpdateParams:input_type -> ucallback.v1.MsgUpdateParams + 3, // 4: ucallback.v1.Msg.VoteReadResult:output_type -> ucallback.v1.MsgVoteReadResultResponse + 1, // 5: ucallback.v1.Msg.UpdateParams:output_type -> ucallback.v1.MsgUpdateParamsResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_ucallback_v1_tx_proto_init() } @@ -1041,6 +2143,7 @@ func file_ucallback_v1_tx_proto_init() { return } file_ucallback_v1_genesis_proto_init() + file_ucallback_v1_types_proto_init() if !protoimpl.UnsafeEnabled { file_ucallback_v1_tx_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MsgUpdateParams); i { @@ -1066,6 +2169,30 @@ func file_ucallback_v1_tx_proto_init() { return nil } } + file_ucallback_v1_tx_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgVoteReadResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ucallback_v1_tx_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgVoteReadResultResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1073,7 +2200,7 @@ func file_ucallback_v1_tx_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ucallback_v1_tx_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/api/ucallback/v1/tx_grpc.pb.go b/api/ucallback/v1/tx_grpc.pb.go index 6834c819..ed533eb9 100644 --- a/api/ucallback/v1/tx_grpc.pb.go +++ b/api/ucallback/v1/tx_grpc.pb.go @@ -19,13 +19,17 @@ import ( const _ = grpc.SupportPackageIsVersion7 const ( - Msg_UpdateParams_FullMethodName = "/ucallback.v1.Msg/UpdateParams" + Msg_VoteReadResult_FullMethodName = "/ucallback.v1.Msg/VoteReadResult" + Msg_UpdateParams_FullMethodName = "/ucallback.v1.Msg/UpdateParams" ) // MsgClient is the client API for Msg service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type MsgClient interface { + // VoteReadResult submits one universal validator's observation of a read + // request's outcome on the destination chain. + VoteReadResult(ctx context.Context, in *MsgVoteReadResult, opts ...grpc.CallOption) (*MsgVoteReadResultResponse, error) // UpdateParams defines a governance operation for updating the parameters. // // Since: cosmos-sdk 0.47 @@ -40,6 +44,15 @@ func NewMsgClient(cc grpc.ClientConnInterface) MsgClient { return &msgClient{cc} } +func (c *msgClient) VoteReadResult(ctx context.Context, in *MsgVoteReadResult, opts ...grpc.CallOption) (*MsgVoteReadResultResponse, error) { + out := new(MsgVoteReadResultResponse) + err := c.cc.Invoke(ctx, Msg_VoteReadResult_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { out := new(MsgUpdateParamsResponse) err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...) @@ -53,6 +66,9 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts // All implementations must embed UnimplementedMsgServer // for forward compatibility type MsgServer interface { + // VoteReadResult submits one universal validator's observation of a read + // request's outcome on the destination chain. + VoteReadResult(context.Context, *MsgVoteReadResult) (*MsgVoteReadResultResponse, error) // UpdateParams defines a governance operation for updating the parameters. // // Since: cosmos-sdk 0.47 @@ -64,6 +80,9 @@ type MsgServer interface { type UnimplementedMsgServer struct { } +func (UnimplementedMsgServer) VoteReadResult(context.Context, *MsgVoteReadResult) (*MsgVoteReadResultResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VoteReadResult not implemented") +} func (UnimplementedMsgServer) UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") } @@ -80,6 +99,24 @@ func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) { s.RegisterService(&Msg_ServiceDesc, srv) } +func _Msg_VoteReadResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVoteReadResult) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VoteReadResult(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Msg_VoteReadResult_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VoteReadResult(ctx, req.(*MsgVoteReadResult)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgUpdateParams) if err := dec(in); err != nil { @@ -105,6 +142,10 @@ var Msg_ServiceDesc = grpc.ServiceDesc{ ServiceName: "ucallback.v1.Msg", HandlerType: (*MsgServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "VoteReadResult", + Handler: _Msg_VoteReadResult_Handler, + }, { MethodName: "UpdateParams", Handler: _Msg_UpdateParams_Handler, diff --git a/proto/ucallback/v1/tx.proto b/proto/ucallback/v1/tx.proto index cccc87d3..be9bfa5e 100755 --- a/proto/ucallback/v1/tx.proto +++ b/proto/ucallback/v1/tx.proto @@ -3,8 +3,10 @@ package ucallback.v1; import "cosmos/msg/v1/msg.proto"; import "ucallback/v1/genesis.proto"; +import "ucallback/v1/types.proto"; import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; +import "amino/amino.proto"; option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; @@ -12,6 +14,10 @@ option go_package = "github.com/pushchain/push-chain-node/x/ucallback/types"; service Msg { option (cosmos.msg.v1.service) = true; + // VoteReadResult submits one universal validator's observation of a read + // request's outcome on the destination chain. + rpc VoteReadResult(MsgVoteReadResult) returns (MsgVoteReadResultResponse); + // UpdateParams defines a governance operation for updating the parameters. // // Since: cosmos-sdk 0.47 @@ -38,3 +44,30 @@ message MsgUpdateParams { // // Since: cosmos-sdk 0.47 message MsgUpdateParamsResponse {} + +// MsgVoteReadResult is broadcast by a universal validator that has executed a +// read request against the destination chain. +// +// The ballot the vote lands on is derived from (request_id, result), so two +// validators reporting the same observation converge on one ballot and any +// disagreement produces a distinct ballot that never reaches quorum. Nothing +// validator-local may appear in `result` for that reason — notably there is no +// error message field. +message MsgVoteReadResult { + option (amino.name) = "ucallback/MsgVoteReadResult"; + option (cosmos.msg.v1.signer) = "signer"; + + // signer is the Cosmos address of the voting universal validator. + string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // request_id identifies the read request being voted on. + string request_id = 2; + + // result is the observation. Every field participates in the ballot key. + ReadResult result = 3; +} + +message MsgVoteReadResultResponse { + // finalized reports whether this vote carried the ballot to quorum. + bool finalized = 1; +} diff --git a/x/ucallback/autocli.go b/x/ucallback/autocli.go index a8848f53..fb13da76 100755 --- a/x/ucallback/autocli.go +++ b/x/ucallback/autocli.go @@ -42,6 +42,14 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { RpcMethod: "UpdateParams", Skip: false, // set to true if authority gated }, + { + RpcMethod: "VoteReadResult", + Use: "vote-read-result ", + Short: "Vote on the observed outcome of a read request", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "request_id"}, + }, + }, }, }, } diff --git a/x/ucallback/keeper/msg_server.go b/x/ucallback/keeper/msg_server.go index 57521b3c..81fde083 100755 --- a/x/ucallback/keeper/msg_server.go +++ b/x/ucallback/keeper/msg_server.go @@ -2,7 +2,9 @@ package keeper import ( "context" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "cosmossdk.io/errors" @@ -27,3 +29,42 @@ func (ms msgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams return nil, ms.k.Params.Set(ctx, msg.Params) } + +// VoteReadResult implements types.MsgServer. +// +// Eligibility is checked here rather than in the keeper: bonded-and-not-tombstoned +// is a property of the signer, and the same two guards front x/uexecutor's vote +// handlers. A validator that has been slashed out must not keep steering ballots. +func (ms msgServer) VoteReadResult(ctx context.Context, msg *types.MsgVoteReadResult) (*types.MsgVoteReadResultResponse, error) { + if msg.Result == nil { + return nil, fmt.Errorf("result is required") + } + + signerAccAddr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, fmt.Errorf("invalid signer address: %w", err) + } + + isBonded, err := ms.k.uvalidatorKeeper.IsBondedUniversalValidator(ctx, msg.Signer) + if err != nil { + return nil, fmt.Errorf("failed to check bonded status for signer %s: %w", msg.Signer, err) + } + if !isBonded { + return nil, fmt.Errorf("universal validator for signer %s is not bonded", msg.Signer) + } + + isTombstoned, err := ms.k.uvalidatorKeeper.IsTombstonedUniversalValidator(ctx, msg.Signer) + if err != nil { + return nil, fmt.Errorf("failed to check tombstoned status for signer %s: %w", msg.Signer, err) + } + if isTombstoned { + return nil, fmt.Errorf("universal validator for signer %s is tombstoned", msg.Signer) + } + + finalized, err := ms.k.VoteReadResult(ctx, sdk.ValAddress(signerAccAddr), msg.RequestId, msg.Result) + if err != nil { + return nil, err + } + + return &types.MsgVoteReadResultResponse{Finalized: finalized}, nil +} diff --git a/x/ucallback/types/tx.pb.go b/x/ucallback/types/tx.pb.go index dc232bcd..927ada05 100644 --- a/x/ucallback/types/tx.pb.go +++ b/x/ucallback/types/tx.pb.go @@ -8,6 +8,7 @@ import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" @@ -129,36 +130,163 @@ func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo +// MsgVoteReadResult is broadcast by a universal validator that has executed a +// read request against the destination chain. +// +// The ballot the vote lands on is derived from (request_id, result), so two +// validators reporting the same observation converge on one ballot and any +// disagreement produces a distinct ballot that never reaches quorum. Nothing +// validator-local may appear in `result` for that reason — notably there is no +// error message field. +type MsgVoteReadResult struct { + // signer is the Cosmos address of the voting universal validator. + Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"` + // request_id identifies the read request being voted on. + RequestId string `protobuf:"bytes,2,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + // result is the observation. Every field participates in the ballot key. + Result *ReadResult `protobuf:"bytes,3,opt,name=result,proto3" json:"result,omitempty"` +} + +func (m *MsgVoteReadResult) Reset() { *m = MsgVoteReadResult{} } +func (m *MsgVoteReadResult) String() string { return proto.CompactTextString(m) } +func (*MsgVoteReadResult) ProtoMessage() {} +func (*MsgVoteReadResult) Descriptor() ([]byte, []int) { + return fileDescriptor_9cc90e16cf6966ee, []int{2} +} +func (m *MsgVoteReadResult) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteReadResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteReadResult.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteReadResult) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteReadResult.Merge(m, src) +} +func (m *MsgVoteReadResult) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteReadResult) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteReadResult.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteReadResult proto.InternalMessageInfo + +func (m *MsgVoteReadResult) GetSigner() string { + if m != nil { + return m.Signer + } + return "" +} + +func (m *MsgVoteReadResult) GetRequestId() string { + if m != nil { + return m.RequestId + } + return "" +} + +func (m *MsgVoteReadResult) GetResult() *ReadResult { + if m != nil { + return m.Result + } + return nil +} + +type MsgVoteReadResultResponse struct { + // finalized reports whether this vote carried the ballot to quorum. + Finalized bool `protobuf:"varint,1,opt,name=finalized,proto3" json:"finalized,omitempty"` +} + +func (m *MsgVoteReadResultResponse) Reset() { *m = MsgVoteReadResultResponse{} } +func (m *MsgVoteReadResultResponse) String() string { return proto.CompactTextString(m) } +func (*MsgVoteReadResultResponse) ProtoMessage() {} +func (*MsgVoteReadResultResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_9cc90e16cf6966ee, []int{3} +} +func (m *MsgVoteReadResultResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteReadResultResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteReadResultResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteReadResultResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteReadResultResponse.Merge(m, src) +} +func (m *MsgVoteReadResultResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteReadResultResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteReadResultResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteReadResultResponse proto.InternalMessageInfo + +func (m *MsgVoteReadResultResponse) GetFinalized() bool { + if m != nil { + return m.Finalized + } + return false +} + func init() { proto.RegisterType((*MsgUpdateParams)(nil), "ucallback.v1.MsgUpdateParams") proto.RegisterType((*MsgUpdateParamsResponse)(nil), "ucallback.v1.MsgUpdateParamsResponse") + proto.RegisterType((*MsgVoteReadResult)(nil), "ucallback.v1.MsgVoteReadResult") + proto.RegisterType((*MsgVoteReadResultResponse)(nil), "ucallback.v1.MsgVoteReadResultResponse") } func init() { proto.RegisterFile("ucallback/v1/tx.proto", fileDescriptor_9cc90e16cf6966ee) } var fileDescriptor_9cc90e16cf6966ee = []byte{ - // 331 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x4d, 0x4e, 0xcc, - 0xc9, 0x49, 0x4a, 0x4c, 0xce, 0xd6, 0x2f, 0x33, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, - 0xc9, 0x17, 0xe2, 0x81, 0x0b, 0xeb, 0x95, 0x19, 0x4a, 0x89, 0x27, 0xe7, 0x17, 0xe7, 0xe6, 0x17, - 0xeb, 0xe7, 0x16, 0xa7, 0x83, 0x54, 0xe5, 0x16, 0xa7, 0x43, 0x94, 0x49, 0x49, 0xa1, 0xe8, 0x4e, - 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0x86, 0xca, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x99, 0xfa, - 0x20, 0x16, 0x54, 0x54, 0x12, 0x62, 0x54, 0x3c, 0x44, 0x02, 0xc2, 0x81, 0x48, 0x29, 0xf5, 0x32, - 0x72, 0xf1, 0xfb, 0x16, 0xa7, 0x87, 0x16, 0xa4, 0x24, 0x96, 0xa4, 0x06, 0x24, 0x16, 0x25, 0xe6, - 0x16, 0x0b, 0x99, 0x71, 0x71, 0x26, 0x96, 0x96, 0x64, 0xe4, 0x17, 0x65, 0x96, 0x54, 0x4a, 0x30, - 0x2a, 0x30, 0x6a, 0x70, 0x3a, 0x49, 0x5c, 0xda, 0xa2, 0x2b, 0x02, 0xd5, 0xe8, 0x98, 0x92, 0x52, - 0x94, 0x5a, 0x5c, 0x1c, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x1e, 0x84, 0x50, 0x2a, 0x64, 0xc4, 0xc5, - 0x56, 0x00, 0x36, 0x41, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0xd9, 0x43, 0x7a, - 0x10, 0xd3, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xaa, 0xb4, 0xe2, 0x6b, 0x7a, 0xbe, - 0x41, 0x0b, 0x61, 0x86, 0x92, 0x24, 0x97, 0x38, 0x9a, 0x73, 0x82, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, - 0x8a, 0x53, 0x8d, 0x92, 0xb8, 0x98, 0x7d, 0x8b, 0xd3, 0x85, 0x42, 0xb8, 0x78, 0x50, 0x5c, 0x2b, - 0x8b, 0x6a, 0x0b, 0x9a, 0x6e, 0x29, 0x55, 0xbc, 0xd2, 0x30, 0xc3, 0xa5, 0x58, 0x1b, 0x9e, 0x6f, - 0xd0, 0x62, 0x74, 0x0a, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, - 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xb3, - 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0x82, 0xd2, 0xe2, 0x8c, 0xe4, - 0x8c, 0xc4, 0xcc, 0x3c, 0x30, 0x4b, 0x17, 0xcc, 0xd4, 0xcd, 0xcb, 0x4f, 0x49, 0xd5, 0xaf, 0xd0, - 0x47, 0x44, 0x4e, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0x38, 0x9c, 0x8d, 0x01, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x0a, 0x23, 0xf0, 0x0e, 0xf4, 0x01, 0x00, 0x00, + // 480 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x53, 0x3f, 0x6f, 0xd3, 0x40, + 0x1c, 0xcd, 0x51, 0x88, 0xf0, 0x51, 0x15, 0xd5, 0x0a, 0xaa, 0x63, 0xa8, 0x5b, 0x45, 0x42, 0x54, + 0x41, 0xb1, 0x69, 0x90, 0x2a, 0xd1, 0x8d, 0x6c, 0x0c, 0x91, 0x2a, 0xf3, 0x67, 0xe8, 0x52, 0x5d, + 0xec, 0xe3, 0x72, 0x22, 0xf6, 0x99, 0xfb, 0x9d, 0xab, 0x96, 0x09, 0x31, 0x22, 0x21, 0xf1, 0x51, + 0x32, 0x30, 0xb3, 0xb0, 0x74, 0xac, 0x98, 0x98, 0x10, 0x4a, 0x86, 0x7c, 0x0d, 0x94, 0xf3, 0xa5, + 0xae, 0x1b, 0xa9, 0x5d, 0xac, 0x9f, 0xdf, 0x7b, 0xf7, 0xee, 0xbd, 0xf3, 0x19, 0x3f, 0xc8, 0x23, + 0x32, 0x1a, 0x0d, 0x48, 0xf4, 0x21, 0x38, 0xde, 0x0d, 0xd4, 0x89, 0x9f, 0x49, 0xa1, 0x84, 0xbd, + 0x7a, 0x01, 0xfb, 0xc7, 0xbb, 0xee, 0x46, 0x24, 0x20, 0x11, 0x10, 0x24, 0xc0, 0xe6, 0xaa, 0x04, + 0x58, 0x21, 0x73, 0xdd, 0xca, 0x6a, 0x46, 0x53, 0x0a, 0x1c, 0x0c, 0xe7, 0x54, 0x9d, 0x4f, 0x33, + 0xba, 0x60, 0x1a, 0x4c, 0x30, 0xa1, 0xc7, 0x60, 0x3e, 0x19, 0xb4, 0x59, 0x6c, 0x72, 0x54, 0x10, + 0xc5, 0x8b, 0xa1, 0xd6, 0x49, 0xc2, 0x53, 0x11, 0xe8, 0x67, 0x01, 0xb5, 0xbe, 0x21, 0x7c, 0xbf, + 0x0f, 0xec, 0x6d, 0x16, 0x13, 0x45, 0x0f, 0x88, 0x24, 0x09, 0xd8, 0x7b, 0xd8, 0x22, 0xb9, 0x1a, + 0x0a, 0xc9, 0xd5, 0xa9, 0x83, 0xb6, 0xd1, 0x8e, 0xd5, 0x73, 0x7e, 0xff, 0xe8, 0x34, 0x8c, 0xd7, + 0xcb, 0x38, 0x96, 0x14, 0xe0, 0xb5, 0x92, 0x3c, 0x65, 0x61, 0x29, 0xb5, 0xbb, 0xb8, 0x9e, 0x69, + 0x07, 0xe7, 0xd6, 0x36, 0xda, 0xb9, 0xd7, 0x6d, 0xf8, 0x97, 0xdb, 0xfb, 0x85, 0x7b, 0xef, 0xf6, + 0xd9, 0xdf, 0xad, 0x5a, 0x68, 0x94, 0xfb, 0x6b, 0x5f, 0x66, 0xe3, 0x76, 0xe9, 0xd1, 0x6a, 0xe2, + 0x8d, 0x2b, 0x71, 0x42, 0x0a, 0x99, 0x48, 0x81, 0xb6, 0x7e, 0x21, 0xbc, 0xde, 0x07, 0xf6, 0x4e, + 0x28, 0x1a, 0x52, 0x12, 0x87, 0x14, 0xf2, 0x91, 0xb2, 0x9f, 0xe1, 0x3a, 0x70, 0x96, 0x52, 0x79, + 0x63, 0x52, 0xa3, 0xb3, 0x37, 0x31, 0x96, 0xf4, 0x63, 0x4e, 0x41, 0x1d, 0xf1, 0x58, 0x47, 0xb5, + 0x42, 0xcb, 0x20, 0xaf, 0xe2, 0xb9, 0xa1, 0xd4, 0xd6, 0xce, 0x8a, 0x6e, 0xe1, 0x54, 0x5b, 0x94, + 0x5b, 0x87, 0x46, 0xb7, 0xff, 0x74, 0xde, 0xc1, 0xb8, 0x7f, 0x9d, 0x8d, 0xdb, 0x0f, 0xcb, 0x2f, + 0xb6, 0x94, 0xb7, 0xf5, 0x02, 0x37, 0x97, 0xc0, 0x45, 0x45, 0xfb, 0x11, 0xb6, 0xde, 0xf3, 0x94, + 0x8c, 0xf8, 0x27, 0x1a, 0xeb, 0x3e, 0x77, 0xc3, 0x12, 0xe8, 0xfe, 0x44, 0x78, 0xa5, 0x0f, 0xcc, + 0x3e, 0xc4, 0x6b, 0x57, 0x0e, 0x61, 0xab, 0x9a, 0x71, 0x69, 0x03, 0xf7, 0xc9, 0x0d, 0x82, 0x8b, + 0x04, 0x6f, 0xf0, 0x6a, 0xe5, 0x2e, 0x6c, 0x2e, 0x2d, 0xbc, 0x4c, 0xbb, 0x8f, 0xaf, 0xa5, 0x17, + 0xae, 0xee, 0x9d, 0xcf, 0xb3, 0x71, 0x1b, 0xf5, 0x0e, 0xce, 0x26, 0x1e, 0x3a, 0x9f, 0x78, 0xe8, + 0xdf, 0xc4, 0x43, 0xdf, 0xa7, 0x5e, 0xed, 0x7c, 0xea, 0xd5, 0xfe, 0x4c, 0xbd, 0xda, 0xe1, 0x1e, + 0xe3, 0x6a, 0x98, 0x0f, 0xfc, 0x48, 0x24, 0x41, 0x96, 0xc3, 0x30, 0x1a, 0x12, 0x9e, 0xea, 0xa9, + 0xa3, 0xc7, 0x4e, 0x2a, 0x62, 0x1a, 0x9c, 0x04, 0xe5, 0xc9, 0xea, 0x1f, 0x61, 0x50, 0xd7, 0xb7, + 0xf8, 0xf9, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x27, 0x12, 0xf6, 0x0b, 0x7f, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -173,6 +301,9 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { + // VoteReadResult submits one universal validator's observation of a read + // request's outcome on the destination chain. + VoteReadResult(ctx context.Context, in *MsgVoteReadResult, opts ...grpc.CallOption) (*MsgVoteReadResultResponse, error) // UpdateParams defines a governance operation for updating the parameters. // // Since: cosmos-sdk 0.47 @@ -187,6 +318,15 @@ func NewMsgClient(cc grpc1.ClientConn) MsgClient { return &msgClient{cc} } +func (c *msgClient) VoteReadResult(ctx context.Context, in *MsgVoteReadResult, opts ...grpc.CallOption) (*MsgVoteReadResultResponse, error) { + out := new(MsgVoteReadResultResponse) + err := c.cc.Invoke(ctx, "/ucallback.v1.Msg/VoteReadResult", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { out := new(MsgUpdateParamsResponse) err := c.cc.Invoke(ctx, "/ucallback.v1.Msg/UpdateParams", in, out, opts...) @@ -198,6 +338,9 @@ func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts // MsgServer is the server API for Msg service. type MsgServer interface { + // VoteReadResult submits one universal validator's observation of a read + // request's outcome on the destination chain. + VoteReadResult(context.Context, *MsgVoteReadResult) (*MsgVoteReadResultResponse, error) // UpdateParams defines a governance operation for updating the parameters. // // Since: cosmos-sdk 0.47 @@ -208,6 +351,9 @@ type MsgServer interface { type UnimplementedMsgServer struct { } +func (*UnimplementedMsgServer) VoteReadResult(ctx context.Context, req *MsgVoteReadResult) (*MsgVoteReadResultResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VoteReadResult not implemented") +} func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") } @@ -216,6 +362,24 @@ func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) } +func _Msg_VoteReadResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVoteReadResult) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VoteReadResult(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ucallback.v1.Msg/VoteReadResult", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VoteReadResult(ctx, req.(*MsgVoteReadResult)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgUpdateParams) if err := dec(in); err != nil { @@ -238,6 +402,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "ucallback.v1.Msg", HandlerType: (*MsgServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "VoteReadResult", + Handler: _Msg_VoteReadResult_Handler, + }, { MethodName: "UpdateParams", Handler: _Msg_UpdateParams_Handler, @@ -310,6 +478,88 @@ func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *MsgVoteReadResult) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteReadResult) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteReadResult) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Result != nil { + { + size, err := m.Result.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.RequestId) > 0 { + i -= len(m.RequestId) + copy(dAtA[i:], m.RequestId) + i = encodeVarintTx(dAtA, i, uint64(len(m.RequestId))) + i-- + dAtA[i] = 0x12 + } + if len(m.Signer) > 0 { + i -= len(m.Signer) + copy(dAtA[i:], m.Signer) + i = encodeVarintTx(dAtA, i, uint64(len(m.Signer))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgVoteReadResultResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteReadResultResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteReadResultResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Finalized { + i-- + if m.Finalized { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -345,6 +595,39 @@ func (m *MsgUpdateParamsResponse) Size() (n int) { return n } +func (m *MsgVoteReadResult) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signer) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.RequestId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.Result != nil { + l = m.Result.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgVoteReadResultResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Finalized { + n += 2 + } + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -516,6 +799,226 @@ func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgVoteReadResult) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteReadResult: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteReadResult: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signer = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Result == nil { + m.Result = &ReadResult{} + } + if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVoteReadResultResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteReadResultResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteReadResultResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Finalized", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Finalized = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 From 82b7912255804538a033685e2e080f141cebba1b Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 6 Aug 2026 14:00:00 +0530 Subject: [PATCH 47/54] feat(ucallback): use request deadline as ballot expiry --- UCALLBACK_IMPLEMENTATION.md | 67 +++++++++++----------- x/ucallback/keeper/uvalidator_fake_test.go | 9 +++ x/ucallback/keeper/voting.go | 23 ++++++-- x/ucallback/keeper/voting_test.go | 54 +++++++++++++++++ x/ucallback/types/ballot.go | 29 +++++++--- x/ucallback/types/ballot_test.go | 40 +++++++++++-- 6 files changed, 174 insertions(+), 48 deletions(-) diff --git a/UCALLBACK_IMPLEMENTATION.md b/UCALLBACK_IMPLEMENTATION.md index 0004bb8a..2022d86b 100644 --- a/UCALLBACK_IMPLEMENTATION.md +++ b/UCALLBACK_IMPLEMENTATION.md @@ -27,39 +27,39 @@ split out of the skeleton, and the protocgen fix was unplanned: --- -## OPEN — expiry semantics, to confirm with the team +## RESOLVED — expiry semantics -**Not resolved. Do not treat the C8 sweeper design as settled until this is answered.** - -There are **two independent expiry clocks**, and the interaction between them is unspecified: +**Team decision: `expiryPushChainHeight` is the ballot expiry.** The two clocks are fused, not +independent — a read ballot expires exactly when its request does. | | clock | set by | enforced at | |---|---|---|---| -| **A** | `ReadSpec.expiryPushChainHeight` → our `ReadRequest.expiry_block_height` | the app, per request | `UniversalCallback.sol:121` on request, `:207` in `expireExternalRead` | -| **B** | `Ballot.block_height_expiry` | us, as arg 8 to `VoteOnBallot` | `x/uvalidator/keeper/ballot.go:344` | - -Questions, in the order they change the design: - -1. **Is late fulfilment intended?** `fulfillExternalCallback` has **no expiry check** — the only guard - is `fulfilledRequests`. A quorum reached long after `expiryHeight` still fulfils and still calls the - app's callback. So A is not a deadline on fulfilment; it is one side of a fulfil-vs-expire race. -2. **Does expiry need to be prompt at all?** `expireExternalRead` **refunds nothing**. Prompt sweeping - frees contract storage and moves our record off `PENDING` — nothing a user feels. If the answer is - "no", the sweeper does not need per-block cadence, and the case for keeping `PendingByExpiry` rests - only on "don't scan an unboundedly-growing map", not on frequency. -3. **Should B be disabled?** uexecutor passes `DefaultExpiryAfterBlocks = 100_000_000` (~19 yrs) with - *"Ballots should not expire without an escape hatch for stuck pending items."* If we copy that, A is - the only real deadline. If we don't, a ballot can die while its read is still live — leaving a record - that can neither fulfil nor expire until A fires. Two clocks on one lifecycle is how records get stuck. - -**Consequences that are parked on this**: sweeper cadence (every block vs every N) and the -inclusive/exclusive boundary at exactly `expiryHeight`. - -**No longer parked on it: `PendingByExpiry` itself.** C3 dropped the `ballotKey → requestId` index and -made the ballot terminal hook scan `PendingByExpiry` instead, so that set now has two consumers. It -stays whichever way the cadence question is answered. +| **A** | `ReadSpec.expiryPushChainHeight` → `ReadRequest.expiry_block_height` | the app, per request | `UniversalCallback.sol:121` on request, `:207` in `expireExternalRead` | +| **B** | `Ballot.block_height_expiry` | **derived from A** | `x/uvalidator/keeper/ballot.go:344` | ---- +x/uvalidator stores `BlockHeightExpiry = createdHeight + expiryAfterBlocks` +(`x/uvalidator/types/ballot.go:109`), so an absolute deadline has to be handed over as a delta — +`types.BallotExpiryAfterBlocks(expiryHeight, currentHeight)`, floored at 1 so a ballot is never born +expired. We do **not** copy x/uexecutor's inert `DefaultExpiryAfterBlocks = 100_000_000`. + +**What this changed downstream:** + +- An `EXPIRED` ballot now means *the request itself is over*, so the terminal hook retires it — + `expireExternalRead` + status `EXPIRED` — instead of leaving it for the sweeper. A request is + unvotable past its deadline anyway, so waiting would only delay closing it on the contract. +- `REJECTED` still leaves the request in flight; that is not a deadline. +- The C8 sweeper is **still needed**, for requests nobody ever voted on: no vote means no ballot, + which means no terminal hook ever fires. Both paths mark the record terminal, which removes it from + the in-flight set, so whichever runs first the other will not find it. + +Still open from the original three questions: + +1. **Is late fulfilment intended?** `fulfillExternalCallback` has no expiry check — only the + `fulfilledRequests` guard. With the clocks fused this is now narrower in practice (an expired + ballot retires the request promptly), but a quorum reached in the same block as expiry is still a + race between the two paths. +2. **`expireExternalRead` refunds nothing.** Unchanged and unaddressed: the funder's fee stays with + the protocol whichever way a request ends. ## Reference points in existing code @@ -376,7 +376,8 @@ func (k Keeper) VoteOnReadBallot(ctx, universalValidator sdk.ValAddress, uvalidatortypes.BallotObservationType_BALLOT_OBSERVATION_TYPE_READ_RESULT, universalValidator.String(), uvalidatortypes.VoteResult_VOTE_RESULT_SUCCESS, - voterAddrStrs, int64(votesNeeded), int64(types.DefaultExpiryAfterBlocks), + voterAddrStrs, int64(votesNeeded), + types.BallotExpiryAfterBlocks(req.ExpiryBlockHeight, ctx.BlockHeight()), ) // ballotKey is stored on the UniversalRead itself; there is no reverse index. // AfterBallotTerminal resolves it by scanning PendingByExpiry — see @@ -501,9 +502,11 @@ func (k Keeper) SweepExpired(ctx sdk.Context) error { Bound it per block — unbounded makes a fat EndBlocker, too low and a backlog never drains. -> **Blocked on the open expiry question at the top of this file.** Cadence, whether `PendingByExpiry` -> exists at all, and the boundary at exactly `expiryHeight` are all downstream of that answer. The -> sketch above assumes per-block; that assumption is the thing under review. +> **Narrower than originally planned.** With ballot expiry fused to the request deadline (see +> RESOLVED at the top), the terminal hook already retires any request that attracted at least one +> vote. The sweeper's remaining job is requests that were *never voted on* — no vote means no ballot, +> so no hook ever fires for them. Cadence is therefore not urgent: nothing user-visible depends on +> prompt expiry, and `expireExternalRead` refunds nothing either way. > The contract's `expireExternalRead` **transfers nothing** (verified: zero value-transfer statements), > so the funder's fee is trapped. That is a contracts bug, not ours — but our sweeper is what makes it diff --git a/x/ucallback/keeper/uvalidator_fake_test.go b/x/ucallback/keeper/uvalidator_fake_test.go index 2bdb7c83..257c84c8 100644 --- a/x/ucallback/keeper/uvalidator_fake_test.go +++ b/x/ucallback/keeper/uvalidator_fake_test.go @@ -111,5 +111,14 @@ func (f *fakeUValidator) VoteOnBallot( // validators diverged on what they observed. func (f *fakeUValidator) ballotCount() int { return len(f.ballots) } +// expiryOf returns the relative expiry a ballot was created with. +func (f *fakeUValidator) expiryOf(id string) int64 { + b, ok := f.ballots[id] + if !ok { + return -1 + } + return b.expiryBlocks +} + // errTest is a sentinel for injecting failures into the fake. var errTest = fmt.Errorf("injected test failure") diff --git a/x/ucallback/keeper/voting.go b/x/ucallback/keeper/voting.go index b116c2e3..042dc692 100644 --- a/x/ucallback/keeper/voting.go +++ b/x/ucallback/keeper/voting.go @@ -13,13 +13,16 @@ import ( // VoteOnReadBallot casts one validator's vote on the ballot for (requestID, result) // and reports whether that vote carried it to quorum. // -// Mirrors x/uexecutor's VoteOnOutboundBallot: same >2/3 threshold, same eligible -// voter set, same inert ballot expiry. +// Mirrors x/uexecutor's VoteOnOutboundBallot for the threshold and voter set, but +// not for expiry: the ballot is given the request's own deadline rather than +// uexecutor's inert 100M blocks, so the two cannot disagree about when the request +// is over. func (k Keeper) VoteOnReadBallot( ctx context.Context, universalValidator sdk.ValAddress, requestID string, result *types.ReadResult, + expiryHeight uint64, ) (ballotKey string, isFinalized bool, isNew bool, err error) { ballotKey, err = types.GetReadBallotKey(requestID, result) if err != nil { @@ -43,12 +46,17 @@ func (k Keeper) VoteOnReadBallot( voterAddrs[i] = v.IdentifyInfo.CoreValidatorAddress } + expiryAfterBlocks := types.BallotExpiryAfterBlocks( + expiryHeight, sdk.UnwrapSDKContext(ctx).BlockHeight()) + k.Logger().Debug("voting on read ballot", "ballot_key", ballotKey, "request_id", requestID, "validator", universalValidator.String(), "total_validators", len(voters), "votes_needed", votesNeeded, + "expiry_height", expiryHeight, + "expiry_after_blocks", expiryAfterBlocks, ) _, isFinalized, isNew, err = k.uvalidatorKeeper.VoteOnBallot( @@ -63,7 +71,7 @@ func (k Keeper) VoteOnReadBallot( uvalidatortypes.VoteResult_VOTE_RESULT_SUCCESS, voterAddrs, int64(votesNeeded), - int64(types.DefaultExpiryAfterBlocks), + expiryAfterBlocks, ) if err != nil { return "", false, false, err @@ -107,11 +115,15 @@ func (k Keeper) VoteReadResult( return false, fmt.Errorf("read request %s is already %s", requestID, ur.Status) } + if ur.Request == nil { + return false, fmt.Errorf("read request %s has no request body", requestID) + } + // Reject votes on a request whose deadline has passed. AllPendingReadRequests // already withholds these, so an honest validator will not be voting on one — // but the query is a convenience, not the enforcement point. sdkCtx := sdk.UnwrapSDKContext(ctx) - if ur.Request != nil && ur.Request.ExpiryBlockHeight <= uint64(sdkCtx.BlockHeight()) { + if ur.Request.ExpiryBlockHeight <= uint64(sdkCtx.BlockHeight()) { return false, fmt.Errorf("read request %s expired at height %d", requestID, ur.Request.ExpiryBlockHeight) } @@ -119,7 +131,8 @@ func (k Keeper) VoteReadResult( // Cache the vote so a failure partway through leaves no half-written ballot. tmpCtx, commit := sdkCtx.CacheContext() - ballotKey, isFinalized, _, err := k.VoteOnReadBallot(tmpCtx, universalValidator, requestID, result) + ballotKey, isFinalized, _, err := k.VoteOnReadBallot( + tmpCtx, universalValidator, requestID, result, ur.Request.ExpiryBlockHeight) if err != nil { return false, err } diff --git a/x/ucallback/keeper/voting_test.go b/x/ucallback/keeper/voting_test.go index 4dab533b..e323ab0b 100644 --- a/x/ucallback/keeper/voting_test.go +++ b/x/ucallback/keeper/voting_test.go @@ -233,3 +233,57 @@ func TestVoteReadResult_BallotResolvesBackToRequest(t *testing.T) { require.True(t, found, "the terminal hook must be able to find this request") require.Equal(t, "0xaa", back.Id) } + +// The ballot's deadline must be the request's own. x/uvalidator stores expiry as +// created + delta, so the delta handed to VoteOnBallot has to close exactly that +// gap — otherwise the two clocks disagree about when the request is over. +func TestVoteReadResult_BallotInheritsRequestDeadline(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(120) + v := seedVoters(t, f, 4) + seedRead(t, f, "0xaa", 500) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xaa", obs(0x01)) + require.NoError(t, err) + + ur, _ := f.k.GetUniversalRead(f.ctx, "0xaa") + delta := f.uvalidator.expiryOf(ur.BallotKey) + require.Equal(t, int64(380), delta, "500 - 120") + require.Equal(t, int64(500), f.ctx.BlockHeight()+delta, + "ballot expires exactly when the request does") +} + +// Requests with different deadlines must not share one expiry. +func TestVoteReadResult_DeadlineIsPerRequest(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(100) + v := seedVoters(t, f, 4) + + seedRead(t, f, "0xsoon", 150) + seedRead(t, f, "0xlate", 9_000) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xsoon", obs(0x01)) + require.NoError(t, err) + _, err = f.k.VoteReadResult(f.ctx, v[0], "0xlate", obs(0x01)) + require.NoError(t, err) + + soon, _ := f.k.GetUniversalRead(f.ctx, "0xsoon") + late, _ := f.k.GetUniversalRead(f.ctx, "0xlate") + + require.Equal(t, int64(50), f.uvalidator.expiryOf(soon.BallotKey)) + require.Equal(t, int64(8_900), f.uvalidator.expiryOf(late.BallotKey)) +} + +func TestVoteReadResult_RejectsMissingRequestBody(t *testing.T) { + f := SetupTest(t) + f.ctx = f.ctx.WithBlockHeight(10) + v := seedVoters(t, f, 4) + + require.NoError(t, f.k.SetUniversalRead(f.ctx, types.UniversalRead{ + Id: "0xnobody", + Status: types.UniversalReadStatus_UNIVERSAL_READ_STATUS_PENDING, + })) + + _, err := f.k.VoteReadResult(f.ctx, v[0], "0xnobody", obs(0x01)) + require.ErrorContains(t, err, "no request body") +} diff --git a/x/ucallback/types/ballot.go b/x/ucallback/types/ballot.go index eeac825c..f9adb790 100644 --- a/x/ucallback/types/ballot.go +++ b/x/ucallback/types/ballot.go @@ -21,14 +21,29 @@ const ( VotesThresholdDenominator = 3 ) -// DefaultExpiryAfterBlocks is the ballot-level expiry passed to VoteOnBallot. +// BallotExpiryAfterBlocks converts a read request's absolute deadline into the +// relative argument VoteOnBallot expects. // -// Set high enough to be inert (~19 years at 6s blocks), matching x/uexecutor. A -// read request already has its own deadline — ReadRequest.ExpiryBlockHeight, set -// by the app and enforced by the contract. Giving the ballot a second, shorter -// clock would let it die while its request is still live, stranding a record that -// can neither fulfil nor expire until the real deadline arrives. -const DefaultExpiryAfterBlocks = 100_000_000 +// x/uvalidator stores BlockHeightExpiry as createdHeight + expiryAfterBlocks +// (types/ballot.go:109), so an absolute target has to be expressed as a delta from +// the height the ballot is created at. +// +// The two clocks are deliberately fused: the ballot expires exactly when the +// request does. x/uexecutor instead passes an inert 100M-block expiry to keep +// ballots alive indefinitely, but a read has a real deadline of its own — set by +// the app, enforced by the contract at UniversalCallback.sol:207 — and a ballot +// that outlived it could only ever finalize into a request no longer worth +// fulfilling. +// +// Returns at least 1 so a ballot is never created already expired. Callers reject +// past-deadline requests before reaching here; this is the backstop. +func BallotExpiryAfterBlocks(expiryHeight uint64, currentHeight int64) int64 { + delta := int64(expiryHeight) - currentHeight + if delta < 1 { + return 1 + } + return delta +} // GetReadBallotKey derives the ballot a (requestId, observation) pair votes on. // diff --git a/x/ucallback/types/ballot_test.go b/x/ucallback/types/ballot_test.go index 260f5f54..91efa879 100644 --- a/x/ucallback/types/ballot_test.go +++ b/x/ucallback/types/ballot_test.go @@ -89,8 +89,40 @@ func TestGetReadBallotKey_Rejects(t *testing.T) { require.Error(t, err) } -// Ballot expiry must stay inert, so the request's own deadline is the only clock. -func TestDefaultExpiryAfterBlocks_IsInert(t *testing.T) { - require.Equal(t, 100_000_000, types.DefaultExpiryAfterBlocks, - "a shorter ballot expiry would let a ballot die while its request is live") +// The ballot's deadline must land exactly on the request's, since x/uvalidator +// stores expiry as created + delta while the request carries an absolute height. +func TestBallotExpiryAfterBlocks_LandsOnRequestDeadline(t *testing.T) { + for _, tc := range []struct { + name string + expiry uint64 + current int64 + want int64 + }{ + {"future deadline", 500, 100, 400}, + {"next block", 101, 100, 1}, + {"from genesis", 900_000, 0, 900_000}, + } { + t.Run(tc.name, func(t *testing.T) { + got := types.BallotExpiryAfterBlocks(tc.expiry, tc.current) + require.Equal(t, tc.want, got) + require.Equal(t, int64(tc.expiry), tc.current+got, + "created + delta must equal the request's own deadline") + }) + } +} + +// A ballot must never be born already expired, even if the caller slipped a +// past-deadline request through. +func TestBallotExpiryAfterBlocks_NeverBornExpired(t *testing.T) { + for _, tc := range []struct { + expiry uint64 + current int64 + }{ + {100, 100}, // exactly at the deadline + {50, 100}, // past it + {0, 100}, // unset + } { + require.Equal(t, int64(1), types.BallotExpiryAfterBlocks(tc.expiry, tc.current), + "expiry=%d current=%d", tc.expiry, tc.current) + } } From 37a00b4777561ff38ba0554b109f02f278f85a65 Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 13:37:06 +0530 Subject: [PATCH 48/54] feat(uclient): build MsgVoteReadResult vote in signer Wire VoteReadResult to a voteReadResult builder that broadcasts MsgVoteReadResult through the AuthZ vote path, and register the ucallback message interface for encoding. Covered by TestVoteReadResult. --- universalClient/pushsigner/pushsigner.go | 19 +++--------- universalClient/pushsigner/pushsigner_test.go | 31 +++++++++++++++++++ universalClient/pushsigner/vote.go | 19 ++++++++++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/universalClient/pushsigner/pushsigner.go b/universalClient/pushsigner/pushsigner.go index f7f9dbe1..af14c153 100644 --- a/universalClient/pushsigner/pushsigner.go +++ b/universalClient/pushsigner/pushsigner.go @@ -2,7 +2,6 @@ package pushsigner import ( "context" - "errors" "fmt" "strings" "sync" @@ -25,7 +24,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/pushcore" "github.com/pushchain/push-chain-node/universalClient/pushsigner/keys" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -134,20 +133,9 @@ func (s *Signer) VoteFundMigration(ctx context.Context, migrationID uint64, txHa return voteFundMigration(ctx, s, s.log, s.granter, migrationID, txHash, success) } -// ErrVoteReadNotAvailable is returned until the core-side vote msg exists. -var ErrVoteReadNotAvailable = errors.New("pushsigner: MsgVoteReadResult not available yet (blocked on core)") - // VoteReadResult votes on an external read observation. -// -// TODO(core): blocked on uexecutortypes.MsgVoteReadResult -// (proto/uexecutor/v1/tx.proto). Once it lands: -// - add a voteReadResult builder in vote.go (Signer: granter, RequestId, -// Status, ResultData, ObservedBlockHeight, ObservedBlockHash) and route -// through vote() like voteInbound does; -// - ensure the validator AuthZ grant set includes the new msg type URL -// (grant_verifier.go + core-side grant creation). -func (s *Signer) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { - return "", ErrVoteReadNotAvailable +func (s *Signer) VoteReadResult(ctx context.Context, requestID string, result *ucallbacktypes.ReadResult) (string, error) { + return voteReadResult(ctx, s, s.log, s.granter, requestID, result) } // signAndBroadcastAuthZTx signs and broadcasts an AuthZ transaction @@ -429,6 +417,7 @@ func createClientContext(kr cosmoskeyring.Keyring, chainID string) client.Contex stakingtypes.RegisterInterfaces(interfaceRegistry) govtypes.RegisterInterfaces(interfaceRegistry) uexecutortypes.RegisterInterfaces(interfaceRegistry) + ucallbacktypes.RegisterInterfaces(interfaceRegistry) cdc := codec.NewProtoCodec(interfaceRegistry) txConfig := authtx.NewTxConfig(cdc, []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT}) diff --git a/universalClient/pushsigner/pushsigner_test.go b/universalClient/pushsigner/pushsigner_test.go index 331a407e..d80dbea9 100644 --- a/universalClient/pushsigner/pushsigner_test.go +++ b/universalClient/pushsigner/pushsigner_test.go @@ -18,6 +18,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/config" "github.com/pushchain/push-chain-node/universalClient/pushcore" "github.com/pushchain/push-chain-node/universalClient/pushsigner/keys" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -625,6 +626,36 @@ func TestVoteFundMigrationFailure(t *testing.T) { assert.Equal(t, "VOTE_OK", txHash) } +func TestVoteReadResult(t *testing.T) { + result := &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, + ResultData: []byte{0xaa}, + ObservedBlockHeight: 100, + } + + t.Run("successful vote", func(t *testing.T) { + signer := createTestSigner(t, successMock(t)) + txHash, err := signer.VoteReadResult(context.Background(), "0xreq1", result) + require.NoError(t, err) + assert.Equal(t, "VOTE_OK", txHash) + }) + + t.Run("error observation votes too", func(t *testing.T) { + signer := createTestSigner(t, successMock(t)) + errResult := &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_ERROR} + txHash, err := signer.VoteReadResult(context.Background(), "0xreq2", errResult) + require.NoError(t, err) + assert.Equal(t, "VOTE_OK", txHash) + }) + + t.Run("broadcast failure", func(t *testing.T) { + signer := createTestSigner(t, failMock(t)) + _, err := signer.VoteReadResult(context.Background(), "0xreq1", result) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to broadcast vote") + }) +} + func TestVoteOnChainRejection(t *testing.T) { mock := &mockChainClient{ getAccountFn: func(ctx context.Context, address string) (*authtypes.QueryAccountResponse, error) { diff --git a/universalClient/pushsigner/vote.go b/universalClient/pushsigner/vote.go index 3312f463..593375c5 100644 --- a/universalClient/pushsigner/vote.go +++ b/universalClient/pushsigner/vote.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/rs/zerolog" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" ) @@ -105,6 +106,24 @@ func waitForTxConfirmation(ctx context.Context, client chainClient, txHash strin } } +// voteReadResult votes on an external read observation +func voteReadResult( + ctx context.Context, + signer *Signer, + log zerolog.Logger, + granter string, + requestID string, + result *ucallbacktypes.ReadResult, +) (string, error) { + msg := &ucallbacktypes.MsgVoteReadResult{ + Signer: granter, + RequestId: requestID, + Result: result, + } + memo := fmt.Sprintf("Vote read result: %s", requestID) + return vote(ctx, signer, log, msg, memo) +} + // voteInbound votes on an inbound transaction func voteInbound( ctx context.Context, From 0cd269edf1c17232bdae941a14d3b122c2d5aa8d Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 13:37:12 +0530 Subject: [PATCH 49/54] feat(uclient): require MsgVoteReadResult authz grant Add /ucallback.v1.MsgVoteReadResult to the validator's required grant set so read-result voting is verified at startup like every other vote. --- universalClient/pushsigner/grant_verifier.go | 1 + universalClient/pushsigner/grant_verifier_test.go | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/universalClient/pushsigner/grant_verifier.go b/universalClient/pushsigner/grant_verifier.go index a4e92b4d..ef507f39 100644 --- a/universalClient/pushsigner/grant_verifier.go +++ b/universalClient/pushsigner/grant_verifier.go @@ -26,6 +26,7 @@ var requiredMsgGrants = []string{ "/uexecutor.v1.MsgVoteOutbound", "/utss.v1.MsgVoteTssKeyProcess", "/utss.v1.MsgVoteFundMigration", + "/ucallback.v1.MsgVoteReadResult", } // GrantInfo represents information about a single AuthZ grant. diff --git a/universalClient/pushsigner/grant_verifier_test.go b/universalClient/pushsigner/grant_verifier_test.go index 2c0ec770..ecb67089 100644 --- a/universalClient/pushsigner/grant_verifier_test.go +++ b/universalClient/pushsigner/grant_verifier_test.go @@ -28,6 +28,7 @@ func TestVerifyGrants(t *testing.T) { {Granter: granter, MessageType: "/uexecutor.v1.MsgVoteOutbound", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteTssKeyProcess", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteFundMigration", Expiration: &futureTime}, + {Granter: granter, MessageType: "/ucallback.v1.MsgVoteReadResult", Expiration: &futureTime}, } msgs, err := verifyGrants(grants, granter) @@ -47,6 +48,7 @@ func TestVerifyGrants(t *testing.T) { {Granter: granter, MessageType: "/uexecutor.v1.MsgVoteOutbound", Expiration: nil}, {Granter: granter, MessageType: "/utss.v1.MsgVoteTssKeyProcess", Expiration: nil}, {Granter: granter, MessageType: "/utss.v1.MsgVoteFundMigration", Expiration: nil}, + {Granter: granter, MessageType: "/ucallback.v1.MsgVoteReadResult", Expiration: nil}, } msgs, err := verifyGrants(grants, granter) @@ -115,6 +117,7 @@ func TestVerifyGrants(t *testing.T) { {Granter: granter, MessageType: "/uexecutor.v1.MsgVoteOutbound", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteTssKeyProcess", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteFundMigration", Expiration: &futureTime}, + {Granter: granter, MessageType: "/ucallback.v1.MsgVoteReadResult", Expiration: &futureTime}, } msgs, err := verifyGrants(grants, granter) @@ -129,6 +132,7 @@ func TestVerifyGrants(t *testing.T) { {Granter: granter, MessageType: "/uexecutor.v1.MsgVoteOutbound", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteTssKeyProcess", Expiration: &futureTime}, {Granter: granter, MessageType: "/utss.v1.MsgVoteFundMigration", Expiration: &futureTime}, + {Granter: granter, MessageType: "/ucallback.v1.MsgVoteReadResult", Expiration: &futureTime}, {Granter: granter, MessageType: "/some.other.v1.MsgNotRequired", Expiration: &futureTime}, // Extra grant } From 9ffac495557ba64658ec226b03793d6252595540 Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 13:41:23 +0530 Subject: [PATCH 50/54] feat(uclient): query pending read requests from x/ucallback Add a ucallback query client to pushcore and wire GetAllPendingReadRequests to AllPendingReadRequests, unwrapping UniversalRead.Request from the response. Covered by TestClient_GetAllPendingReadRequests. --- universalClient/pushcore/pushCore.go | 41 +++++++---- universalClient/pushcore/pushCore_test.go | 89 ++++++++++++++++++++++- 2 files changed, 112 insertions(+), 18 deletions(-) diff --git a/universalClient/pushcore/pushCore.go b/universalClient/pushcore/pushCore.go index 3de4dda0..600c4836 100644 --- a/universalClient/pushcore/pushCore.go +++ b/universalClient/pushcore/pushCore.go @@ -16,7 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" @@ -35,6 +35,7 @@ type Client struct { uvalidatorClients []uvalidatortypes.QueryClient // Universal validator query clients utssClients []utsstypes.QueryClient // TSS query clients uexecutorClients []uexecutortypes.QueryClient // Executor query clients (for gas price queries) + ucallbackClients []ucallbacktypes.QueryClient // Callback query clients (for pending read requests) cmtClients []cmtservice.ServiceClient // CometBFT service clients txClients []tx.ServiceClient // Transaction service clients authzClients []authz.QueryClient // AuthZ query clients @@ -66,6 +67,7 @@ func New(urls []string, logger zerolog.Logger) (*Client, error) { c.uvalidatorClients = append(c.uvalidatorClients, uvalidatortypes.NewQueryClient(conn)) c.utssClients = append(c.utssClients, utsstypes.NewQueryClient(conn)) c.uexecutorClients = append(c.uexecutorClients, uexecutortypes.NewQueryClient(conn)) + c.ucallbackClients = append(c.ucallbackClients, ucallbacktypes.NewQueryClient(conn)) c.cmtClients = append(c.cmtClients, cmtservice.NewServiceClient(conn)) c.txClients = append(c.txClients, tx.NewServiceClient(conn)) c.authzClients = append(c.authzClients, authz.NewQueryClient(conn)) @@ -94,6 +96,7 @@ func (c *Client) Close() error { c.uvalidatorClients = nil c.utssClients = nil c.uexecutorClients = nil + c.ucallbackClients = nil c.cmtClients = nil c.txClients = nil c.authzClients = nil @@ -368,19 +371,29 @@ func (c *Client) GetAllPendingOutbounds(ctx context.Context) ([]*uexecutortypes. return resp.Entries, resp.Outbounds, nil } -// ErrReadQueriesNotAvailable is returned until the core-side pending-read query -// exists. Callers treat it as "feature not live yet", not as a failure. -var ErrReadQueriesNotAvailable = errors.New("pushcore: pending read requests query not available yet (blocked on core)") - -// GetAllPendingReadRequests retrieves pending external read requests from Push Chain. -// -// TODO(core): blocked on x/uexecutor Query/PendingReadRequests -// (proto/uexecutor/v1/query.proto). Once it lands, mirror GetAllPendingOutbounds: -// call c.uexecutorClients[idx].AllPendingReadRequests with retryWithRoundRobin, -// map uexecutortypes.ReadRequest -> uread.ReadRequest (or drop the local type -// entirely), and delete ErrReadQueriesNotAvailable. -func (c *Client) GetAllPendingReadRequests(ctx context.Context) ([]*uread.ReadRequest, error) { - return nil, ErrReadQueriesNotAvailable +// GetAllPendingReadRequests retrieves up to the first 1000 pending external read +// requests from Push Chain. The query already withholds requests past their expiry +// height, so validators never take on work that can no longer be fulfilled in time. +func (c *Client) GetAllPendingReadRequests(ctx context.Context) ([]*ucallbacktypes.ReadRequest, error) { + return retryWithRoundRobin( + len(c.ucallbackClients), + &c.rr, + func(idx int) ([]*ucallbacktypes.ReadRequest, error) { + resp, err := c.ucallbackClients[idx].AllPendingReadRequests(ctx, &ucallbacktypes.QueryAllPendingReadRequestsRequest{ + Pagination: &query.PageRequest{Limit: 1000}, + }) + if err != nil { + return nil, err + } + requests := make([]*ucallbacktypes.ReadRequest, 0, len(resp.Reads)) + for i := range resp.Reads { + requests = append(requests, resp.Reads[i].Request) + } + return requests, nil + }, + "GetAllPendingReadRequests", + c.logger, + ) } // createGRPCConnection creates a gRPC connection with appropriate transport security. diff --git a/universalClient/pushcore/pushCore_test.go b/universalClient/pushcore/pushCore_test.go index 323372b4..964540f7 100644 --- a/universalClient/pushcore/pushCore_test.go +++ b/universalClient/pushcore/pushCore_test.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/authz" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" uregistrytypes "github.com/pushchain/push-chain-node/x/uregistry/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" @@ -823,6 +824,73 @@ func TestClient_GetAllPendingOutbounds(t *testing.T) { }) } +func TestClient_GetAllPendingReadRequests(t *testing.T) { + logger := zerolog.Nop() + ctx := context.Background() + + t.Run("no endpoints configured", func(t *testing.T) { + client := &Client{ + logger: logger, + ucallbackClients: []ucallbacktypes.QueryClient{}, + } + + reqs, err := client.GetAllPendingReadRequests(ctx) + require.Error(t, err) + assert.Contains(t, err.Error(), "no endpoints configured") + assert.Nil(t, reqs) + }) + + t.Run("successful query maps UniversalRead.Request", func(t *testing.T) { + mockClient := &mockUCallbackQueryClient{ + allPendingReadsResp: &ucallbacktypes.QueryAllPendingReadRequestsResponse{ + Reads: []ucallbacktypes.UniversalRead{ + {Id: "0xr1", Request: &ucallbacktypes.ReadRequest{RequestId: "0xr1", DestinationChain: "eip155:1"}}, + {Id: "0xr2", Request: &ucallbacktypes.ReadRequest{RequestId: "0xr2", DestinationChain: "web2:https"}}, + }, + }, + } + + client := &Client{ + logger: logger, + ucallbackClients: []ucallbacktypes.QueryClient{mockClient}, + } + + reqs, err := client.GetAllPendingReadRequests(ctx) + require.NoError(t, err) + require.Len(t, reqs, 2) + assert.Equal(t, "0xr1", reqs[0].RequestId) + assert.Equal(t, "web2:https", reqs[1].DestinationChain) + }) + + t.Run("empty response", func(t *testing.T) { + mockClient := &mockUCallbackQueryClient{ + allPendingReadsResp: &ucallbacktypes.QueryAllPendingReadRequestsResponse{}, + } + + client := &Client{ + logger: logger, + ucallbackClients: []ucallbacktypes.QueryClient{mockClient}, + } + + reqs, err := client.GetAllPendingReadRequests(ctx) + require.NoError(t, err) + assert.Empty(t, reqs) + }) + + t.Run("all endpoints fail", func(t *testing.T) { + client := &Client{ + logger: logger, + ucallbackClients: []ucallbacktypes.QueryClient{ + &mockUCallbackQueryClient{err: assert.AnError}, + }, + } + + reqs, err := client.GetAllPendingReadRequests(ctx) + require.Error(t, err) + assert.Nil(t, reqs) + }) +} + func TestClient_GetGasPrice_NilResponse(t *testing.T) { logger := zerolog.Nop() mockClient := &mockUExecutorQueryClient{ @@ -965,10 +1033,10 @@ func (m *mockUValidatorQueryClient) UniversalValidator(ctx context.Context, req type mockUTSSQueryClient struct { utsstypes.QueryClient - currentKeyResp *utsstypes.QueryCurrentKeyResponse - pendingTssEventsResp *utsstypes.QueryAllPendingTssEventsResponse - pendingFundMigrationsResp *utsstypes.QueryPendingFundMigrationsResponse - err error + currentKeyResp *utsstypes.QueryCurrentKeyResponse + pendingTssEventsResp *utsstypes.QueryAllPendingTssEventsResponse + pendingFundMigrationsResp *utsstypes.QueryPendingFundMigrationsResponse + err error } func (m *mockUTSSQueryClient) CurrentKey(ctx context.Context, req *utsstypes.QueryCurrentKeyRequest, opts ...grpc.CallOption) (*utsstypes.QueryCurrentKeyResponse, error) { @@ -1084,3 +1152,16 @@ func (m *mockAuthAccountQueryClient) Account(ctx context.Context, req *authtypes } return m.accountResp, nil } + +type mockUCallbackQueryClient struct { + ucallbacktypes.QueryClient + allPendingReadsResp *ucallbacktypes.QueryAllPendingReadRequestsResponse + err error +} + +func (m *mockUCallbackQueryClient) AllPendingReadRequests(ctx context.Context, req *ucallbacktypes.QueryAllPendingReadRequestsRequest, opts ...grpc.CallOption) (*ucallbacktypes.QueryAllPendingReadRequestsResponse, error) { + if m.err != nil { + return nil, m.err + } + return m.allPendingReadsResp, nil +} From 5e9393d1970a8ded0c862a8063bb2b5f00ba13a9 Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 16:37:51 +0530 Subject: [PATCH 51/54] feat(uclient): adopt x/ucallback read types in pushwatcher Swap the temporary uread types for the generated ucallback types across the read listener, parser, and event processor. Key the read event on the on-chain requestId directly (a unique incrementing nonce) instead of hashing it. Adds TestConvertReadRequestEvent. --- universalClient/pushwatcher/event_listener.go | 6 +-- universalClient/pushwatcher/event_parser.go | 8 +-- .../pushwatcher/event_parser_test.go | 52 ++++++++++++++++++- .../pushwatcher/read_event_processor.go | 11 ++-- .../pushwatcher/read_event_processor_test.go | 45 ++++++++-------- 5 files changed, 83 insertions(+), 39 deletions(-) diff --git a/universalClient/pushwatcher/event_listener.go b/universalClient/pushwatcher/event_listener.go index 54dbb19c..511c7ee2 100644 --- a/universalClient/pushwatcher/event_listener.go +++ b/universalClient/pushwatcher/event_listener.go @@ -243,10 +243,6 @@ func (el *EventListener) pollFundMigrationEvents(ctx context.Context) int { func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { requests, err := el.pushCore.GetAllPendingReadRequests(ctx) if err != nil { - if errors.Is(err, pushcore.ErrReadQueriesNotAvailable) { - // TODO(core): remove once Query/PendingReadRequests lands. - return 0 - } el.logger.Error().Err(err).Msg("failed to fetch pending read requests") return 0 } @@ -255,7 +251,7 @@ func (el *EventListener) pollReadRequestEvents(ctx context.Context) int { for _, req := range requests { event, err := convertReadRequestEvent(req) if err != nil { - el.logger.Warn().Err(err).Str("request_id", req.RequestID).Msg("failed to convert read request") + el.logger.Warn().Err(err).Str("request_id", req.RequestId).Msg("failed to convert read request") continue } diff --git a/universalClient/pushwatcher/event_parser.go b/universalClient/pushwatcher/event_parser.go index 1abb4a4f..476f7090 100644 --- a/universalClient/pushwatcher/event_parser.go +++ b/universalClient/pushwatcher/event_parser.go @@ -7,7 +7,7 @@ import ( "fmt" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" ) @@ -96,8 +96,8 @@ func convertFundMigrationEvent(migration *utsstypes.FundMigration) (*store.Event } // convertReadRequestEvent converts a pending external read request to a store.Event. -func convertReadRequestEvent(req *uread.ReadRequest) (*store.Event, error) { - if req == nil || req.RequestID == "" { +func convertReadRequestEvent(req *ucallbacktypes.ReadRequest) (*store.Event, error) { + if req == nil || req.RequestId == "" { return nil, fmt.Errorf("read request is nil or missing request id") } @@ -107,7 +107,7 @@ func convertReadRequestEvent(req *uread.ReadRequest) (*store.Event, error) { } return &store.Event{ - EventID: hashEventID(store.EventTypeReadRequest, req.RequestID), + EventID: req.RequestId, // globally unique on-chain nonce; no hashing needed BlockHeight: req.CreatedAtHeight, ExpiryBlockHeight: req.ExpiryBlockHeight, Type: store.EventTypeReadRequest, diff --git a/universalClient/pushwatcher/event_parser_test.go b/universalClient/pushwatcher/event_parser_test.go index acd38a7a..fae4c123 100644 --- a/universalClient/pushwatcher/event_parser_test.go +++ b/universalClient/pushwatcher/event_parser_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/pushchain/push-chain-node/universalClient/store" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uexecutortypes "github.com/pushchain/push-chain-node/x/uexecutor/types" utsstypes "github.com/pushchain/push-chain-node/x/utss/types" ) @@ -397,6 +398,56 @@ func TestConvertFundMigrationEvent(t *testing.T) { }) } +func TestConvertReadRequestEvent(t *testing.T) { + t.Run("nil request returns error", func(t *testing.T) { + result, err := convertReadRequestEvent(nil) + require.Error(t, err) + assert.Nil(t, result) + assert.Contains(t, err.Error(), "read request is nil or missing request id") + }) + + t.Run("empty request id returns error", func(t *testing.T) { + result, err := convertReadRequestEvent(&ucallbacktypes.ReadRequest{RequestId: ""}) + require.Error(t, err) + assert.Nil(t, result) + assert.Contains(t, err.Error(), "read request is nil or missing request id") + }) + + t.Run("valid request converts correctly", func(t *testing.T) { + req := &ucallbacktypes.ReadRequest{ + RequestId: "0x00000000000000000000000000000000000000000000000000000000000000a1", + DestinationChain: "eip155:11155111", + Owner: []byte{0x01, 0x02}, + Query: []byte{0xde, 0xad}, + MinConfirmations: 3, + DestinationBlockHeight: 500, + ExpiryBlockHeight: 900, + CreatedAtHeight: 420, + } + + result, err := convertReadRequestEvent(req) + require.NoError(t, err) + require.NotNil(t, result) + + // EventID is the on-chain requestId verbatim (no hashing) + assert.Equal(t, req.RequestId, result.EventID) + assert.Equal(t, store.EventTypeReadRequest, result.Type) + assert.Equal(t, store.StatusConfirmed, result.Status) + assert.Equal(t, store.ConfirmationInstant, result.ConfirmationType) + assert.Equal(t, uint64(420), result.BlockHeight, "block height is the request's created-at height") + assert.Equal(t, uint64(900), result.ExpiryBlockHeight, "expiry height must be stamped for the processor's expiry check") + + // EventData round-trips back to the request + var decoded ucallbacktypes.ReadRequest + require.NoError(t, json.Unmarshal(result.EventData, &decoded)) + assert.Equal(t, req.RequestId, decoded.RequestId) + assert.Equal(t, req.DestinationChain, decoded.DestinationChain) + assert.Equal(t, req.Query, decoded.Query) + assert.Equal(t, req.MinConfirmations, decoded.MinConfirmations) + assert.Equal(t, req.DestinationBlockHeight, decoded.DestinationBlockHeight) + }) +} + func TestHashEventID(t *testing.T) { t.Run("deterministic output", func(t *testing.T) { id1 := hashEventID("keygen", "123") @@ -421,4 +472,3 @@ func TestHashEventID(t *testing.T) { assert.Len(t, id, 64) // sha256 = 32 bytes = 64 hex chars }) } - diff --git a/universalClient/pushwatcher/read_event_processor.go b/universalClient/pushwatcher/read_event_processor.go index 9d99edd7..c6f4e63b 100644 --- a/universalClient/pushwatcher/read_event_processor.go +++ b/universalClient/pushwatcher/read_event_processor.go @@ -10,7 +10,7 @@ import ( "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/externalchains/web2" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" "github.com/rs/zerolog" ) @@ -23,7 +23,7 @@ type ChainResolver interface { // readVoter submits a read observation vote to Push Chain. // Satisfied by *pushsigner.Signer. type readVoter interface { - VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) + VoteReadResult(ctx context.Context, requestID string, result *ucallbacktypes.ReadResult) (string, error) } // ReadEventProcessor handles READ_REQUEST events: it executes each request on @@ -69,13 +69,13 @@ func (p *ReadEventProcessor) HandleEvent(ctx context.Context, event *store.Event return nil } - var req uread.ReadRequest + var req ucallbacktypes.ReadRequest if err := json.Unmarshal(event.EventData, &req); err != nil { p.markReverted(event.EventID) return err } - log := p.logger.With().Str("request_id", req.RequestID).Logger() + log := p.logger.With().Str("request_id", req.RequestId).Logger() handler, err := p.resolveHandler(req.DestinationChain) if err != nil { @@ -90,9 +90,8 @@ func (p *ReadEventProcessor) HandleEvent(ctx context.Context, event *store.Event return nil } - voteTxHash, err := p.voter.VoteReadResult(ctx, req.RequestID, result) + voteTxHash, err := p.voter.VoteReadResult(ctx, req.RequestId, result) if err != nil { - // TODO(core): ErrVoteReadNotAvailable falls through here until MsgVoteReadResult lands. log.Warn().Err(err).Msg("failed to vote read result; will retry") return nil } diff --git a/universalClient/pushwatcher/read_event_processor_test.go b/universalClient/pushwatcher/read_event_processor_test.go index f10cfcad..5240143d 100644 --- a/universalClient/pushwatcher/read_event_processor_test.go +++ b/universalClient/pushwatcher/read_event_processor_test.go @@ -11,28 +11,28 @@ import ( "github.com/pushchain/push-chain-node/universalClient/externalchains/common" "github.com/pushchain/push-chain-node/universalClient/store" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) type fakeReadVoter struct { - votes map[string]*uread.ReadResult + votes map[string]*ucallbacktypes.ReadResult txHash string err error } -func (f *fakeReadVoter) VoteReadResult(ctx context.Context, requestID string, result *uread.ReadResult) (string, error) { +func (f *fakeReadVoter) VoteReadResult(ctx context.Context, requestID string, result *ucallbacktypes.ReadResult) (string, error) { if f.err != nil { return "", f.err } if f.votes == nil { - f.votes = make(map[string]*uread.ReadResult) + f.votes = make(map[string]*ucallbacktypes.ReadResult) } f.votes[requestID] = result return f.txHash, nil } type fakeDestClient struct { - result *uread.ReadResult + result *ucallbacktypes.ReadResult err error notStarted bool } @@ -49,7 +49,7 @@ func (f *fakeDestClient) GetReadRequestHandler() (common.ReadRequestHandler, err } return f, nil } -func (f *fakeDestClient) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { +func (f *fakeDestClient) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadRequest) (*ucallbacktypes.ReadResult, error) { return f.result, f.err } @@ -64,9 +64,9 @@ func (f *fakeChainResolver) GetClient(chainID string) (common.ChainClient, error return f.client, nil } -func testReadRequest() *uread.ReadRequest { - return &uread.ReadRequest{ - RequestID: "0xabc123", +func testReadRequest() *ucallbacktypes.ReadRequest { + return &ucallbacktypes.ReadRequest{ + RequestId: "0xabc123", DestinationChain: "eip155:11155111", Query: []byte{0x01}, MinConfirmations: 1, @@ -83,7 +83,7 @@ func newTestReadEventProcessor(t *testing.T, voter readVoter, destClient common. return p, common.NewChainStore(database) } -func seedReadRequest(t *testing.T, cs *common.ChainStore, req *uread.ReadRequest) *store.Event { +func seedReadRequest(t *testing.T, cs *common.ChainStore, req *ucallbacktypes.ReadRequest) *store.Event { t.Helper() event, err := convertReadRequestEvent(req) require.NoError(t, err) @@ -102,8 +102,8 @@ func assertStatus(t *testing.T, cs *common.ChainStore, eventID, status string) { func TestReadEventProcessor_SuccessFlow(t *testing.T) { req := testReadRequest() - result := &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + result := &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: []byte{0xaa}, ObservedBlockHeight: 100, } @@ -113,15 +113,15 @@ func TestReadEventProcessor_SuccessFlow(t *testing.T) { require.NoError(t, p.HandleEvent(context.Background(), event)) - require.Contains(t, voter.votes, req.RequestID) - assert.Equal(t, result, voter.votes[req.RequestID]) + require.Contains(t, voter.votes, req.RequestId) + assert.Equal(t, result, voter.votes[req.RequestId]) assertStatus(t, cs, event.EventID, store.StatusCompleted) } func TestReadEventProcessor_VoteFailureKeepsConfirmed(t *testing.T) { req := testReadRequest() voter := &fakeReadVoter{err: fmt.Errorf("MsgVoteReadResult not available")} - p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS}}) event := seedReadRequest(t, cs, req) require.NoError(t, p.HandleEvent(context.Background(), event)) @@ -167,7 +167,7 @@ func TestReadEventProcessor_HandlerUnavailableRetries(t *testing.T) { func TestReadEventProcessor_CorruptEventReverted(t *testing.T) { voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS}}) event := &store.Event{ EventID: "corrupt-read", @@ -190,7 +190,7 @@ func TestReadEventProcessor_ExpiredMarkedReverted(t *testing.T) { req := testReadRequest() req.ExpiryBlockHeight = 50 voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS}}) require.NoError(t, cs.UpdateChainHeight(100)) // push chain past expiry event := seedReadRequest(t, cs, req) @@ -204,21 +204,20 @@ func TestReadEventProcessor_NotExpiredProcessesNormally(t *testing.T) { req := testReadRequest() req.ExpiryBlockHeight = 200 voter := &fakeReadVoter{txHash: "VOTE_TX"} - p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &uread.ReadResult{Status: uread.ReadStatusSuccess}}) + p, cs := newTestReadEventProcessor(t, voter, &fakeDestClient{result: &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS}}) require.NoError(t, cs.UpdateChainHeight(100)) event := seedReadRequest(t, cs, req) require.NoError(t, p.HandleEvent(context.Background(), event)) - require.Contains(t, voter.votes, req.RequestID) + require.Contains(t, voter.votes, req.RequestId) assertStatus(t, cs, event.EventID, store.StatusCompleted) } - func TestReadEventProcessor_Web2Dispatch(t *testing.T) { req := testReadRequest() req.DestinationChain = "web2:https" - result := &uread.ReadResult{Status: uread.ReadStatusSuccess, ResultData: []byte{0xbb}} + result := &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: []byte{0xbb}} t.Run("dispatches to web2 handler", func(t *testing.T) { database := newTestDB(t) @@ -231,8 +230,8 @@ func TestReadEventProcessor_Web2Dispatch(t *testing.T) { require.NoError(t, p.HandleEvent(context.Background(), event)) - require.Contains(t, voter.votes, req.RequestID) - assert.Equal(t, result, voter.votes[req.RequestID]) + require.Contains(t, voter.votes, req.RequestId) + assert.Equal(t, result, voter.votes[req.RequestId]) assertStatus(t, cs, event.EventID, store.StatusCompleted) }) From a25c9a451d495eb68cea73e22963daa5b5fef4bb Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 16:50:07 +0530 Subject: [PATCH 52/54] refactor(uclient): swap read executors to x/ucallback types Adopt the generated ucallback ReadRequest/ReadResult across the evm, svm, and web2 read executors and the shared ReadRequestHandler interface, with a common NewReadErrorResult helper for votable ERROR observations. --- .../externalchains/common/types.go | 12 +++++- .../externalchains/evm/read_executor.go | 24 ++++++------ .../externalchains/evm/read_executor_test.go | 24 ++++++------ .../externalchains/svm/read_executor.go | 34 ++++++++--------- .../externalchains/svm/read_executor_test.go | 28 +++++++------- .../externalchains/web2/read_executor.go | 27 ++++++------- .../externalchains/web2/read_executor_test.go | 38 +++++++++---------- 7 files changed, 98 insertions(+), 89 deletions(-) diff --git a/universalClient/externalchains/common/types.go b/universalClient/externalchains/common/types.go index 0dfc59be..4628b1fc 100644 --- a/universalClient/externalchains/common/types.go +++ b/universalClient/externalchains/common/types.go @@ -5,7 +5,7 @@ import ( "fmt" "math/big" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" uetypes "github.com/pushchain/push-chain-node/x/uexecutor/types" ) @@ -117,5 +117,13 @@ type TxBuilder interface { // ReadRequestHandler executes a read request on one destination chain. // Consumed by the push watcher's read processor. type ReadRequestHandler interface { - ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) + ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadRequest) (*ucallbacktypes.ReadResult, error) +} + +// NewReadErrorResult builds an ERROR observation. ResultData stays empty so every +// validator voting ERROR converges on the same ballot regardless of local error +// text; core's ReadResult has no error field for that reason, so err is logged by +// the caller, not carried on the vote. +func NewReadErrorResult(err error) *ucallbacktypes.ReadResult { + return &ucallbacktypes.ReadResult{Status: ucallbacktypes.ReadStatus_READ_STATUS_ERROR} } diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go index 046e2911..b8c95aa3 100644 --- a/universalClient/externalchains/evm/read_executor.go +++ b/universalClient/externalchains/evm/read_executor.go @@ -6,17 +6,17 @@ import ( "math/big" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) // ExecuteRead implements common.ChainReader for EVM chains. // All validators must produce byte-identical results, so every query runs at the // height pinned in the request; execution is gated until that height has // min_confirmations confirmations so a reorg cannot invalidate the read. -func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { +func (c *Client) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadRequest) (*ucallbacktypes.ReadResult, error) { env, err := decodeEvmQueryEnvelope(req.Query) if err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } height := req.DestinationBlockHeight @@ -24,7 +24,7 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea height = env.BlockNumber } if height == 0 { - return uread.NewErrorResult(fmt.Errorf("read request has no target height")), nil + return common.NewReadErrorResult(fmt.Errorf("read request has no target height")), nil } if err := c.gateHeightConfirmed(ctx, height, uint64(req.MinConfirmations)); err != nil { @@ -42,7 +42,7 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea case evmQueryAccountBalance: target, decErr := decodeAccountBalancePayload(env.Payload) if decErr != nil { - return uread.NewErrorResult(decErr), nil + return common.NewReadErrorResult(decErr), nil } balance, rpcErr := c.rpcClient.GetBalanceAt(ctx, target, blockNum) if rpcErr != nil { @@ -53,19 +53,19 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea case evmQueryContractCall: target, callData, decErr := decodeContractCallPayload(env.Payload) if decErr != nil { - return uread.NewErrorResult(decErr), nil + return common.NewReadErrorResult(decErr), nil } ret, rpcErr := c.rpcClient.CallContract(ctx, target, callData, blockNum) if rpcErr != nil { // eth_call reverts are deterministic at a pinned height — observable as ERROR. - return uread.NewErrorResult(rpcErr), nil + return common.NewReadErrorResult(rpcErr), nil } resultData = ret case evmQueryStorageSlot: target, slot, decErr := decodeStorageSlotPayload(env.Payload) if decErr != nil { - return uread.NewErrorResult(decErr), nil + return common.NewReadErrorResult(decErr), nil } value, rpcErr := c.rpcClient.GetStorageAt(ctx, target, slot, blockNum) if rpcErr != nil { @@ -76,14 +76,14 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea resultData = slotValue[:] default: - return uread.NewErrorResult(fmt.Errorf("unknown EvmQueryType %d", env.QueryType)), nil + return common.NewReadErrorResult(fmt.Errorf("unknown EvmQueryType %d", env.QueryType)), nil } if err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } - return &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + return &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: resultData, ObservedBlockHeight: height, ObservedBlockHash: header.Hash().Bytes(), diff --git a/universalClient/externalchains/evm/read_executor_test.go b/universalClient/externalchains/evm/read_executor_test.go index 17efb17c..7e56fe4b 100644 --- a/universalClient/externalchains/evm/read_executor_test.go +++ b/universalClient/externalchains/evm/read_executor_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) // fakeHeader is a minimal valid block header JSON accepted by types.Header. @@ -90,10 +90,10 @@ func newReadTestClient(t *testing.T, results map[string]any, faults map[string]r } } -func evmReadRequest(t *testing.T, queryType uint8, blockNumber uint64, payload []byte) *uread.ReadRequest { +func evmReadRequest(t *testing.T, queryType uint8, blockNumber uint64, payload []byte) *ucallbacktypes.ReadRequest { t.Helper() - return &uread.ReadRequest{ - RequestID: "0xreq1", + return &ucallbacktypes.ReadRequest{ + RequestId: "0xreq1", DestinationChain: "eip155:11155111", Query: packEvmEnvelope(t, queryType, 0, blockNumber, payload), MinConfirmations: 1, @@ -113,7 +113,7 @@ func TestExecuteRead_AccountBalance(t *testing.T) { result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload)) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, big.NewInt(1_000_000), new(big.Int).SetBytes(result.ResultData)) assert.Equal(t, uint64(100), result.ObservedBlockHeight) assert.Len(t, result.ObservedBlockHash, 32) @@ -132,7 +132,7 @@ func TestExecuteRead_ContractCall(t *testing.T) { result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryContractCall), 0, payload)) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, []byte{0xca, 0xfe, 0xba, 0xbe}, result.ResultData) }) @@ -145,7 +145,7 @@ func TestExecuteRead_ContractCall(t *testing.T) { result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryContractCall), 0, payload)) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) assert.Empty(t, result.ResultData) }) } @@ -162,7 +162,7 @@ func TestExecuteRead_StorageSlot(t *testing.T) { result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryStorageSlot), 0, payload)) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) require.Len(t, result.ResultData, 32) assert.Equal(t, big.NewInt(7), new(big.Int).SetBytes(result.ResultData)) } @@ -170,13 +170,13 @@ func TestExecuteRead_StorageSlot(t *testing.T) { func TestExecuteRead_InvalidEnvelope(t *testing.T) { client := newReadTestClient(t, nil, nil) - result, err := client.ExecuteRead(context.Background(), &uread.ReadRequest{ - RequestID: "0xreq1", + result, err := client.ExecuteRead(context.Background(), &ucallbacktypes.ReadRequest{ + RequestId: "0xreq1", Query: []byte{0x01, 0x02}, DestinationBlockHeight: 100, }) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) } func TestExecuteRead_RPCFailureIsTransient(t *testing.T) { @@ -223,7 +223,7 @@ func TestExecuteRead_MissingHeightIsVotableError(t *testing.T) { result, err := client.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) } func TestExecuteRead_ConfirmationGate(t *testing.T) { diff --git a/universalClient/externalchains/svm/read_executor.go b/universalClient/externalchains/svm/read_executor.go index d5fc7085..42e7c62e 100644 --- a/universalClient/externalchains/svm/read_executor.go +++ b/universalClient/externalchains/svm/read_executor.go @@ -9,7 +9,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) // splTokenAmountOffset is the byte offset of the u64 amount in an SPL token account. @@ -21,14 +21,14 @@ const splTokenAmountOffset = 64 // commitment with minContextSlot as a staleness floor. ObservedBlockHeight (the // context slot) may differ across validators; core's ballot key covers the // result value only, never the observed slot. -func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { +func (c *Client) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadRequest) (*ucallbacktypes.ReadResult, error) { env, err := decodeSolanaQueryEnvelope(req.Query) if err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } if len(req.Owner) != solana.PublicKeyLength { - return uread.NewErrorResult(fmt.Errorf("owner must be a 32-byte pubkey, got %d bytes", len(req.Owner))), nil + return common.NewReadErrorResult(fmt.Errorf("owner must be a 32-byte pubkey, got %d bytes", len(req.Owner))), nil } account := solana.PublicKeyFromBytes(req.Owner) @@ -45,10 +45,10 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea } resultData, encErr := common.EncodeUint256Result(new(big.Int).SetUint64(balance)) if encErr != nil { - return uread.NewErrorResult(encErr), nil + return common.NewReadErrorResult(encErr), nil } - return &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + return &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: resultData, ObservedBlockHeight: slot, }, nil @@ -59,21 +59,21 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea return nil, rpcErr } if !found { - return uread.NewErrorResult(fmt.Errorf("token account %s not found", account)), nil + return common.NewReadErrorResult(fmt.Errorf("token account %s not found", account)), nil } if !owner.Equals(solana.TokenProgramID) && !owner.Equals(solana.Token2022ProgramID) { - return uread.NewErrorResult(fmt.Errorf("account %s is not owned by a token program", account)), nil + return common.NewReadErrorResult(fmt.Errorf("account %s is not owned by a token program", account)), nil } if len(data) < splTokenAmountOffset+8 { - return uread.NewErrorResult(fmt.Errorf("token account data too short: %d bytes", len(data))), nil + return common.NewReadErrorResult(fmt.Errorf("token account data too short: %d bytes", len(data))), nil } amount := binary.LittleEndian.Uint64(data[splTokenAmountOffset : splTokenAmountOffset+8]) resultData, encErr := common.EncodeUint256Result(new(big.Int).SetUint64(amount)) if encErr != nil { - return uread.NewErrorResult(encErr), nil + return common.NewReadErrorResult(encErr), nil } - return &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + return &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: resultData, ObservedBlockHeight: slot, }, nil @@ -84,15 +84,15 @@ func (c *Client) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*urea return nil, rpcErr } if !found { - return uread.NewErrorResult(fmt.Errorf("account %s not found", account)), nil + return common.NewReadErrorResult(fmt.Errorf("account %s not found", account)), nil } - return &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + return &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: data, ObservedBlockHeight: slot, }, nil default: - return uread.NewErrorResult(fmt.Errorf("unknown SolanaQueryType %d", env.QueryType)), nil + return common.NewReadErrorResult(fmt.Errorf("unknown SolanaQueryType %d", env.QueryType)), nil } } diff --git a/universalClient/externalchains/svm/read_executor_test.go b/universalClient/externalchains/svm/read_executor_test.go index 17331ec9..b48271af 100644 --- a/universalClient/externalchains/svm/read_executor_test.go +++ b/universalClient/externalchains/svm/read_executor_test.go @@ -16,7 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) // accountInfoResult builds a getAccountInfo result with base64 data. @@ -62,7 +62,7 @@ func newReadTestClient(t *testing.T, results map[string]any) *Client { } } -func svmReadRequest(t *testing.T, queryType uint8, minSlot uint64, owner []byte) *uread.ReadRequest { +func svmReadRequest(t *testing.T, queryType uint8, minSlot uint64, owner []byte) *ucallbacktypes.ReadRequest { t.Helper() query, err := svmEnvelopeArgs.Pack(rawSvmEnvelope{ QueryType: queryType, @@ -71,8 +71,8 @@ func svmReadRequest(t *testing.T, queryType uint8, minSlot uint64, owner []byte) }{minSlot}, }) require.NoError(t, err) - return &uread.ReadRequest{ - RequestID: "0xreq1", + return &ucallbacktypes.ReadRequest{ + RequestId: "0xreq1", DestinationChain: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", Owner: owner, Query: query, @@ -96,7 +96,7 @@ func TestExecuteRead_LamportBalance(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryLamportBalance), 800, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, big.NewInt(5_000_000), new(big.Int).SetBytes(result.ResultData)) assert.Equal(t, uint64(900), result.ObservedBlockHeight) }) @@ -131,7 +131,7 @@ func TestExecuteRead_SPLTokenAccount(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 800, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, big.NewInt(777), new(big.Int).SetBytes(result.ResultData)) assert.Equal(t, uint64(900), result.ObservedBlockHeight) }) @@ -143,7 +143,7 @@ func TestExecuteRead_SPLTokenAccount(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("truncated account data is a votable ERROR", func(t *testing.T) { @@ -153,7 +153,7 @@ func TestExecuteRead_SPLTokenAccount(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("missing account is a votable ERROR", func(t *testing.T) { @@ -166,7 +166,7 @@ func TestExecuteRead_SPLTokenAccount(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQuerySPLTokenAccount), 0, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) } @@ -180,7 +180,7 @@ func TestExecuteRead_RawAccountData(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryRawAccountData), 0, account.Bytes())) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusSuccess, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, raw, result.ResultData) assert.Equal(t, uint64(900), result.ObservedBlockHeight) } @@ -191,13 +191,13 @@ func TestExecuteRead_InvalidInputs(t *testing.T) { t.Run("invalid envelope is a votable ERROR", func(t *testing.T) { client := newReadTestClient(t, nil) - result, err := client.ExecuteRead(context.Background(), &uread.ReadRequest{ - RequestID: "0xreq1", + result, err := client.ExecuteRead(context.Background(), &ucallbacktypes.ReadRequest{ + RequestId: "0xreq1", Owner: account.Bytes(), Query: []byte{0x01}, }) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("owner not 32 bytes is a votable ERROR", func(t *testing.T) { @@ -205,6 +205,6 @@ func TestExecuteRead_InvalidInputs(t *testing.T) { result, err := client.ExecuteRead(context.Background(), svmReadRequest(t, uint8(solanaQueryLamportBalance), 0, []byte{0x01, 0x02})) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) } diff --git a/universalClient/externalchains/web2/read_executor.go b/universalClient/externalchains/web2/read_executor.go index f8063fd5..e3d925d4 100644 --- a/universalClient/externalchains/web2/read_executor.go +++ b/universalClient/externalchains/web2/read_executor.go @@ -21,7 +21,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/rs/zerolog" - "github.com/pushchain/push-chain-node/universalClient/uread" + "github.com/pushchain/push-chain-node/universalClient/externalchains/common" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) const ( @@ -141,14 +142,14 @@ func isDisallowedIP(ip net.IP) bool { // declared fields, and abi-encodes them in extract order. Deterministic // failures (bad envelope, non-JSON response, missing path, 4xx) are votable // ERROR observations; transport failures and 5xx are transient errors. -func (e *Executor) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*uread.ReadResult, error) { +func (e *Executor) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadRequest) (*ucallbacktypes.ReadResult, error) { env, err := decodeWeb2QueryEnvelope(req.Query) if err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } if err := e.validateEnvelope(env); err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } body, errResult, err := e.fetch(ctx, env) @@ -161,12 +162,12 @@ func (e *Executor) ExecuteRead(ctx context.Context, req *uread.ReadRequest) (*ur resultData, err := extractAndEncode(body, env.Extract) if err != nil { - return uread.NewErrorResult(err), nil + return common.NewReadErrorResult(err), nil } // web2 has no block height or hash; the ballot covers result data only - return &uread.ReadResult{ - Status: uread.ReadStatusSuccess, + return &ucallbacktypes.ReadResult{ + Status: ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, ResultData: resultData, }, nil } @@ -185,7 +186,7 @@ func (e *Executor) validateEnvelope(env *web2QueryEnvelope) error { // fetch performs the HTTP request. Returns (body, nil, nil) on success, // (nil, errorResult, nil) on deterministic failure, (nil, nil, err) on // transient failure. -func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, *uread.ReadResult, error) { +func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, *ucallbacktypes.ReadResult, error) { timeout := defaultTimeout if env.TimeoutMs > 0 { timeout = min(time.Duration(env.TimeoutMs)*time.Millisecond, maxTimeout) @@ -202,13 +203,13 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * httpReq, err := http.NewRequestWithContext(reqCtx, method, env.URL, reqBody) if err != nil { - return nil, uread.NewErrorResult(fmt.Errorf("invalid request: %w", err)), nil + return nil, common.NewReadErrorResult(fmt.Errorf("invalid request: %w", err)), nil } if len(env.Headers) > 0 { var headers map[string]string if err := json.Unmarshal(env.Headers, &headers); err != nil { - return nil, uread.NewErrorResult(fmt.Errorf("invalid headers encoding: %w", err)), nil + return nil, common.NewReadErrorResult(fmt.Errorf("invalid headers encoding: %w", err)), nil } for name, value := range headers { httpReq.Header.Set(name, value) @@ -220,7 +221,7 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * // A guard rejection is the same for every validator: votable ERROR. // Any other transport error may be transient. if errors.Is(err, errBlockedRequest) { - return nil, uread.NewErrorResult(fmt.Errorf("request blocked: %w", err)), nil + return nil, common.NewReadErrorResult(fmt.Errorf("request blocked: %w", err)), nil } return nil, nil, fmt.Errorf("request failed: %w", err) // transient } @@ -232,7 +233,7 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * return nil, nil, fmt.Errorf("endpoint returned status %d", resp.StatusCode) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return nil, uread.NewErrorResult(fmt.Errorf("endpoint returned status %d", resp.StatusCode)), nil + return nil, common.NewReadErrorResult(fmt.Errorf("endpoint returned status %d", resp.StatusCode)), nil } body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1)) @@ -240,7 +241,7 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * return nil, nil, fmt.Errorf("failed to read response: %w", err) // transient } if len(body) > maxResponseBytes { - return nil, uread.NewErrorResult(fmt.Errorf("response exceeds %d bytes", maxResponseBytes)), nil + return nil, common.NewReadErrorResult(fmt.Errorf("response exceeds %d bytes", maxResponseBytes)), nil } return body, nil, nil diff --git a/universalClient/externalchains/web2/read_executor_test.go b/universalClient/externalchains/web2/read_executor_test.go index d13c8022..c2a711ca 100644 --- a/universalClient/externalchains/web2/read_executor_test.go +++ b/universalClient/externalchains/web2/read_executor_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/pushchain/push-chain-node/universalClient/uread" + ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" ) func packEnvelope(t *testing.T, env rawWeb2Envelope) []byte { @@ -46,10 +46,10 @@ func jsonHandler(t *testing.T, wantMethod string, response any) http.HandlerFunc } } -func web2Request(t *testing.T, env rawWeb2Envelope) *uread.ReadRequest { +func web2Request(t *testing.T, env rawWeb2Envelope) *ucallbacktypes.ReadRequest { t.Helper() - return &uread.ReadRequest{ - RequestID: "0xreq1", + return &ucallbacktypes.ReadRequest{ + RequestId: "0xreq1", DestinationChain: "web2:https", Query: packEnvelope(t, env), } @@ -74,7 +74,7 @@ func TestExecuteRead_GetIdenticalFields(t *testing.T) { result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - require.Equal(t, uread.ReadStatusSuccess, result.Status) + require.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Zero(t, result.ObservedBlockHeight) stringTy, _ := abi.NewType("string", "", nil) @@ -100,7 +100,7 @@ func TestExecuteRead_DecimalScaling(t *testing.T) { result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - require.Equal(t, uread.ReadStatusSuccess, result.Status) + require.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, big.NewInt(351244710000), new(big.Int).SetBytes(result.ResultData)) } @@ -127,7 +127,7 @@ func TestExecuteRead_PostBody(t *testing.T) { result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - require.Equal(t, uread.ReadStatusSuccess, result.Status) + require.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) assert.Equal(t, big.NewInt(18), new(big.Int).SetBytes(result.ResultData)) } @@ -144,7 +144,7 @@ func TestExecuteRead_ArrayIndexPath(t *testing.T) { result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - require.Equal(t, uread.ReadStatusSuccess, result.Status) + require.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_SUCCESS, result.Status) boolTy, _ := abi.NewType("bool", "", nil) vals, err := abi.Arguments{{Type: boolTy}}.Unpack(result.ResultData) @@ -155,9 +155,9 @@ func TestExecuteRead_ArrayIndexPath(t *testing.T) { func TestExecuteRead_VotableErrors(t *testing.T) { t.Run("invalid envelope", func(t *testing.T) { e := NewExecutor(zerolog.Nop()) - result, err := e.ExecuteRead(context.Background(), &uread.ReadRequest{Query: []byte{0x01}}) + result, err := e.ExecuteRead(context.Background(), &ucallbacktypes.ReadRequest{Query: []byte{0x01}}) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("non-https url", func(t *testing.T) { @@ -169,7 +169,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("non-identical mode not supported", func(t *testing.T) { @@ -183,7 +183,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("GET with body", func(t *testing.T) { @@ -196,7 +196,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("missing path", func(t *testing.T) { @@ -208,7 +208,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("type mismatch", func(t *testing.T) { @@ -220,7 +220,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("non-JSON response", func(t *testing.T) { @@ -234,7 +234,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) t.Run("404 status", func(t *testing.T) { @@ -248,7 +248,7 @@ func TestExecuteRead_VotableErrors(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) } @@ -305,7 +305,7 @@ func TestExecuteRead_SSRFGuard(t *testing.T) { }) result, err := e.ExecuteRead(context.Background(), req) require.NoError(t, err) // deterministic, not transient - assert.Equal(t, uread.ReadStatusError, result.Status) + assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) }) } } @@ -359,7 +359,7 @@ func TestScaledInteger(t *testing.T) { {"3512.4471", 8, "351244710000"}, {"100", 0, "100"}, {"0.5", 2, "50"}, - {"1.999", 0, "1"}, // truncates + {"1.999", 0, "1"}, // truncates {"-2.5", 1, "-25"}, } for _, tc := range cases { From c301bdaec323a6c401fd20243fe3aa25c10a253f Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 16:50:14 +0530 Subject: [PATCH 53/54] refactor(uclient): remove temporary uread package The read path now uses the generated x/ucallback types, so the uread proto-mirror is no longer referenced. --- universalClient/uread/types.go | 46 ---------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 universalClient/uread/types.go diff --git a/universalClient/uread/types.go b/universalClient/uread/types.go deleted file mode 100644 index bc706cb3..00000000 --- a/universalClient/uread/types.go +++ /dev/null @@ -1,46 +0,0 @@ -// Package uread is a TEMPORARY package: it mirrors the read-request proto types -// x/uexecutor will generate (proto/uexecutor/v1/read_request.proto + tx.proto). -// -// TODO(core): once core lands, replace every uread.* reference with the -// generated uexecutortypes equivalents and delete this package. -package uread - -// ReadRequest mirrors the pending read request tracked by x/uexecutor. -type ReadRequest struct { - RequestID string // uint256 as 0x-prefixed hex (from ReadRequested event) - DestinationChain string // CAIP-2, e.g. "eip155:1", "solana:mainnet-beta"; web2 uses "web2:https" - Owner []byte // ReadSpec.account.owner (20-byte addr / 32-byte pubkey) - Query []byte // chain-specific envelope, abi.encode(...) - MinConfirmations uint16 - DestinationBlockHeight uint64 // destination chain height the read is made at; not applicable for web2 - ExpiryBlockHeight uint64 // Push chain height at which the request expires - CreatedAtHeight uint64 // Push chain height at which the request was created -} - -// ReadStatus is the observed outcome a validator votes on. -type ReadStatus int32 - -const ( - ReadStatusSuccess ReadStatus = 1 - ReadStatusError ReadStatus = 2 -) - -// ReadResult is the canonical observation submitted via MsgVoteReadResult. -// All fields must be byte-identical across validators for quorum. -type ReadResult struct { - Status ReadStatus - ResultData []byte - ObservedBlockHeight uint64 // block number (EVM) or slot (SVM) - ObservedBlockHash []byte // 32 bytes; empty when the chain cannot pin one deterministically - ErrorMsg string // local diagnostic only — never part of the ballot -} - -// NewErrorResult builds an ERROR observation. ResultData stays empty so all -// validators voting ERROR converge on the same ballot regardless of local error text. -func NewErrorResult(err error) *ReadResult { - msg := "" - if err != nil { - msg = err.Error() - } - return &ReadResult{Status: ReadStatusError, ErrorMsg: msg} -} From ff2c8385d4436c54fd175c009c5d720592ee7073 Mon Sep 17 00:00:00 2001 From: aman035 Date: Fri, 7 Aug 2026 18:24:16 +0530 Subject: [PATCH 54/54] fix: retry transient errors --- .../externalchains/evm/read_executor.go | 29 ++++++++++- .../externalchains/evm/read_executor_test.go | 52 +++++++++++++++++++ .../externalchains/web2/read_executor.go | 9 ++-- .../externalchains/web2/read_executor_test.go | 23 ++++++++ 4 files changed, 108 insertions(+), 5 deletions(-) diff --git a/universalClient/externalchains/evm/read_executor.go b/universalClient/externalchains/evm/read_executor.go index b8c95aa3..c2f63acb 100644 --- a/universalClient/externalchains/evm/read_executor.go +++ b/universalClient/externalchains/evm/read_executor.go @@ -2,8 +2,12 @@ package evm import ( "context" + "errors" "fmt" "math/big" + "strings" + + "github.com/ethereum/go-ethereum/rpc" "github.com/pushchain/push-chain-node/universalClient/externalchains/common" ucallbacktypes "github.com/pushchain/push-chain-node/x/ucallback/types" @@ -57,8 +61,14 @@ func (c *Client) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadReques } ret, rpcErr := c.rpcClient.CallContract(ctx, target, callData, blockNum) if rpcErr != nil { - // eth_call reverts are deterministic at a pinned height — observable as ERROR. - return common.NewReadErrorResult(rpcErr), nil + // Only a genuine execution revert is deterministic at the pinned + // height and safe to vote. A transport error or a node-state error + // (e.g. missing trie node on a pruned node) is not deterministic and + // must be retried, never voted. + if isExecutionRevert(rpcErr) { + return common.NewReadErrorResult(rpcErr), nil + } + return nil, rpcErr } resultData = ret @@ -90,6 +100,21 @@ func (c *Client) ExecuteRead(ctx context.Context, req *ucallbacktypes.ReadReques }, nil } +// isExecutionRevert reports whether an eth_call error is a deterministic EVM +// revert (the node executed the call and it reverted) rather than a transient +// transport or node-state failure. Only a revert is safe to vote as ERROR. +func isExecutionRevert(err error) bool { + var dataErr rpc.DataError + if errors.As(err, &dataErr) && dataErr.ErrorData() != nil { + return true + } + var rpcErr rpc.Error + if errors.As(err, &rpcErr) && rpcErr.ErrorCode() == 3 { + return true + } + return strings.Contains(strings.ToLower(err.Error()), "execution reverted") +} + // gateHeightConfirmed blocks execution until the target height has at least // minConfirmations confirmations. An error is transient: the processor keeps // the event CONFIRMED and retries next tick. diff --git a/universalClient/externalchains/evm/read_executor_test.go b/universalClient/externalchains/evm/read_executor_test.go index 7e56fe4b..a8928fc9 100644 --- a/universalClient/externalchains/evm/read_executor_test.go +++ b/universalClient/externalchains/evm/read_executor_test.go @@ -148,6 +148,20 @@ func TestExecuteRead_ContractCall(t *testing.T) { assert.Equal(t, ucallbacktypes.ReadStatus_READ_STATUS_ERROR, result.Status) assert.Empty(t, result.ResultData) }) + + t.Run("non-revert rpc error is transient, not voted", func(t *testing.T) { + // a pruned/unsynced node (missing trie node) is node-specific, not a + // deterministic revert; it must retry, never produce an ERROR vote + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + }, map[string]rpcFault{ + "eth_call": {code: -32000, message: "missing trie node"}, + }) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryContractCall), 0, payload)) + require.Error(t, err) + assert.Nil(t, result) + }) } func TestExecuteRead_StorageSlot(t *testing.T) { @@ -193,6 +207,44 @@ func TestExecuteRead_RPCFailureIsTransient(t *testing.T) { assert.Nil(t, result) } +func TestExecuteRead_PrunedStateIsTransient(t *testing.T) { + // A pruned node can serve the header but not old state. This is node-specific, + // not deterministic (an archive node returns the real value), so it must + // retry/abstain, never produce an ERROR vote — otherwise a pruned majority + // could wrongly quorum an ERROR for an address that has a balance. + t.Run("account balance", func(t *testing.T) { + target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") + payload, err := addressArgs.Pack(target) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + }, map[string]rpcFault{ + "eth_getBalance": {code: -32000, message: "missing trie node"}, + }) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryAccountBalance), 0, payload)) + require.Error(t, err) + assert.Nil(t, result) + }) + + t.Run("storage slot", func(t *testing.T) { + target := ethcommon.HexToAddress("0x2222222222222222222222222222222222222222") + payload, err := addressBytes32Args.Pack(target, [32]byte{0x01}) + require.NoError(t, err) + + client := newReadTestClient(t, map[string]any{ + "eth_getBlockByNumber": fakeHeader(100), + }, map[string]rpcFault{ + "eth_getStorageAt": {code: -32000, message: "missing trie node"}, + }) + + result, err := client.ExecuteRead(context.Background(), evmReadRequest(t, uint8(evmQueryStorageSlot), 0, payload)) + require.Error(t, err) + assert.Nil(t, result) + }) +} + func TestExecuteRead_EnvelopeBlockNumberUsedWhenNotPinned(t *testing.T) { target := ethcommon.HexToAddress("0x1111111111111111111111111111111111111111") payload, err := addressArgs.Pack(target) diff --git a/universalClient/externalchains/web2/read_executor.go b/universalClient/externalchains/web2/read_executor.go index e3d925d4..b4acc0be 100644 --- a/universalClient/externalchains/web2/read_executor.go +++ b/universalClient/externalchains/web2/read_executor.go @@ -227,9 +227,12 @@ func (e *Executor) fetch(ctx context.Context, env *web2QueryEnvelope) ([]byte, * } defer func() { _ = resp.Body.Close() }() - // 4xx is a deterministic answer from the endpoint; 5xx is the endpoint - // having a bad moment - if resp.StatusCode >= 500 { + // 5xx, plus the transient 4xx codes 408 (Request Timeout) and 429 (Too Many + // Requests), mean "try again" — retry, never vote. Every other non-2xx is a + // deterministic answer from the endpoint and is votable. + if resp.StatusCode >= 500 || + resp.StatusCode == http.StatusRequestTimeout || + resp.StatusCode == http.StatusTooManyRequests { return nil, nil, fmt.Errorf("endpoint returned status %d", resp.StatusCode) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { diff --git a/universalClient/externalchains/web2/read_executor_test.go b/universalClient/externalchains/web2/read_executor_test.go index c2a711ca..f67f9031 100644 --- a/universalClient/externalchains/web2/read_executor_test.go +++ b/universalClient/externalchains/web2/read_executor_test.go @@ -280,6 +280,29 @@ func TestExecuteRead_TransientErrors(t *testing.T) { require.Error(t, err) assert.Nil(t, result) }) + + // 408 and 429 are retryable 4xx codes: retry, never vote. + for _, tc := range []struct { + name string + code int + }{ + {"408 request timeout", http.StatusRequestTimeout}, + {"429 too many requests", http.StatusTooManyRequests}, + } { + t.Run(tc.name, func(t *testing.T) { + e, url := newTestExecutor(t, func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(tc.code) + }) + req := web2Request(t, rawWeb2Envelope{ + Method: uint8(web2MethodGet), + Url: url, + Extract: []rawWeb2Extract{extractSpec("$.a", valueTypeString, 0)}, + }) + result, err := e.ExecuteRead(context.Background(), req) + require.Error(t, err) + assert.Nil(t, result) + }) + } } func TestExecuteRead_SSRFGuard(t *testing.T) {