From c5c7b4ea0f4e87a86fb1231701d55cfbe7ef75b7 Mon Sep 17 00:00:00 2001 From: Gary Malouf <982483+gmalouf@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:30:55 -0500 Subject: [PATCH] Incentives: Storing/ serving heartbeat transactions, synthetic transactions for payouts (#1635) * Add support for storing and serving heartbeat transaction types * Support generating synthetic payout transaction based off Payout being set in block header. * Introduce transaction fetch API test case for heartbeats. --- api/README.md | 2 +- api/converter_utils.go | 26 +- api/generate.sh | 18 - api/generated/common/routes.go | 377 +++++++------- api/generated/common/types.go | 49 ++ api/generated/v2/routes.go | 463 +++++++++--------- api/generated/v2/types.go | 52 ++ api/handlers_test.go | 13 +- api/indexer.oas2.json | 80 ++- api/indexer.oas3.yml | 93 +++- api/test_resources/heartbeat.response | 2 + api/test_resources/heartbeat.txn | Bin 0 -> 502 bytes e2e_tests/docker/indexer/Dockerfile | 2 +- go.mod | 2 +- go.sum | 4 +- idb/postgres/internal/writer/write_txn.go | 51 +- .../writer/write_txn_participation.go | 8 + idb/postgres/internal/writer/writer_test.go | 252 +++++++++- idb/postgres/postgres.go | 3 +- idb/txn_type_enum.go | 2 + 20 files changed, 1041 insertions(+), 458 deletions(-) delete mode 100755 api/generate.sh create mode 100644 api/test_resources/heartbeat.response create mode 100644 api/test_resources/heartbeat.txn diff --git a/api/README.md b/api/README.md index 3e2f2f2db..23c35b22f 100644 --- a/api/README.md +++ b/api/README.md @@ -9,7 +9,7 @@ The API is defined using [OpenAPI v2](https://swagger.io/specification/v2/) in * The Makefile will install our fork of **oapi-codegen**, use `make oapi-codegen` to install it directly. 1. Document your changes by editing **indexer.oas2.yml** -2. Regenerate the endpoints by running **generate.sh**. The sources at **generated/** will be updated. +2. Regenerate the endpoints by running **make generate** from the `api` directory. The sources at **generated/** will be updated. 3. Update the implementation in **handlers.go**. It is sometimes useful to consult **generated/routes.go** to make sure the handler properly implements **ServerInterface**. ## What codegen tool is used? diff --git a/api/converter_utils.go b/api/converter_utils.go index 8a854c231..971ec78d7 100644 --- a/api/converter_utils.go +++ b/api/converter_utils.go @@ -382,6 +382,7 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen var assetTransfer *generated.TransactionAssetTransfer var application *generated.TransactionApplication var stateProof *generated.TransactionStateProof + var heartbeat *generated.TransactionHeartbeat switch stxn.Txn.Type { case sdk.PaymentTx: @@ -585,6 +586,22 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen StateProofType: uint64Ptr(uint64(stxn.Txn.StateProofType)), } stateProof = &proofTxn + case sdk.HeartbeatTx: + hb := stxn.Txn.HeartbeatTxnFields + hbTxn := generated.TransactionHeartbeat{ + HbAddress: hb.HbAddress.String(), + HbKeyDilution: hb.HbKeyDilution, + HbProof: generated.HbProofFields{ + HbPk: byteSliceOmitZeroPtr(hb.HbProof.PK[:]), + HbPk1sig: byteSliceOmitZeroPtr(hb.HbProof.PK1Sig[:]), + HbPk2: byteSliceOmitZeroPtr(hb.HbProof.PK2[:]), + HbPk2sig: byteSliceOmitZeroPtr(hb.HbProof.PK2Sig[:]), + HbSig: byteSliceOmitZeroPtr(hb.HbProof.Sig[:]), + }, + HbSeed: hb.HbSeed[:], + HbVoteId: hb.HbVoteID[:], + } + heartbeat = &hbTxn } var localStateDelta *[]generated.AccountStateDelta @@ -635,9 +652,9 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen for _, t := range stxn.ApplyData.EvalDelta.InnerTxns { extra2 := extra if t.Txn.Type == sdk.ApplicationCallTx { - extra2.AssetID = uint64(t.ApplyData.ApplicationID) + extra2.AssetID = t.ApplyData.ApplicationID } else if t.Txn.Type == sdk.AssetConfigTx { - extra2.AssetID = uint64(t.ApplyData.ConfigAsset) + extra2.AssetID = t.ApplyData.ConfigAsset } else { extra2.AssetID = 0 } @@ -661,6 +678,7 @@ func signedTxnWithAdToTransaction(stxn *sdk.SignedTxnWithAD, extra rowData) (gen PaymentTransaction: payment, KeyregTransaction: keyreg, StateProofTransaction: stateProof, + HeartbeatTransaction: heartbeat, ClosingAmount: uint64Ptr(uint64(stxn.ClosingAmount)), ConfirmedRound: uint64Ptr(extra.Round), IntraRoundOffset: uint64Ptr(uint64(extra.Intra)), @@ -951,9 +969,9 @@ func (si *ServerImplementation) blockParamsToBlockFilter(params generated.Search func (si *ServerImplementation) maxAccountsErrorToAccountsErrorResponse(maxErr idb.MaxAPIResourcesPerAccountError) generated.ErrorResponse { addr := maxErr.Address.String() - max := uint64(si.opts.MaxAPIResourcesPerAccount) + maxResults := si.opts.MaxAPIResourcesPerAccount extraData := map[string]interface{}{ - "max-results": max, + "max-results": maxResults, "address": addr, "total-assets-opted-in": maxErr.TotalAssets, "total-created-assets": maxErr.TotalAssetParams, diff --git a/api/generate.sh b/api/generate.sh deleted file mode 100755 index 86dae2a06..000000000 --- a/api/generate.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootdir=`dirname $0` -pushd $rootdir - -# Convert v2 to v3 -curl -s -X POST "https://converter.swagger.io/api/convert" -H "accept: application/json" -H "Content-Type: application/json" -d @./indexer.oas2.json -o 3.json - -python3 -c "import json; import sys; json.dump(json.load(sys.stdin), sys.stdout, indent=2, sort_keys=True)" < 3.json > indexer.oas3.yml -rm 3.json - -echo "generating code." -oapi-codegen -package generated -type-mappings integer=uint64 -generate types -o generated/v2/types.go -exclude-tags=common indexer.oas3.yml -oapi-codegen -package generated -type-mappings integer=uint64 -generate server,spec -o generated/v2/routes.go -exclude-tags=common indexer.oas3.yml - -oapi-codegen -package common -type-mappings integer=uint64 -generate types -o generated/common/types.go -include-tags=common indexer.oas3.yml -oapi-codegen -package common -type-mappings integer=uint64 -generate server,spec -o generated/common/routes.go -include-tags=common indexer.oas3.yml diff --git a/api/generated/common/routes.go b/api/generated/common/routes.go index e70663e5b..714b8f0b8 100644 --- a/api/generated/common/routes.go +++ b/api/generated/common/routes.go @@ -72,191 +72,198 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "ZUqLlZCOqrd8tQKd9fjO/w8AAP//62VeisflAAA=", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/generated/common/types.go b/api/generated/common/types.go index a7c59f989..4a1826b45 100644 --- a/api/generated/common/types.go +++ b/api/generated/common/types.go @@ -36,6 +36,7 @@ const ( TransactionTxTypeAfrz TransactionTxType = "afrz" TransactionTxTypeAppl TransactionTxType = "appl" TransactionTxTypeAxfer TransactionTxType = "axfer" + TransactionTxTypeHb TransactionTxType = "hb" TransactionTxTypeKeyreg TransactionTxType = "keyreg" TransactionTxTypePay TransactionTxType = "pay" TransactionTxTypeStpf TransactionTxType = "stpf" @@ -61,6 +62,7 @@ const ( TxTypeAfrz TxType = "afrz" TxTypeAppl TxType = "appl" TxTypeAxfer TxType = "axfer" + TxTypeHb TxType = "hb" TxTypeKeyreg TxType = "keyreg" TxTypePay TxType = "pay" TxTypeStpf TxType = "stpf" @@ -561,6 +563,24 @@ type HashFactory struct { // * sha256 type Hashtype string +// HbProofFields \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. +type HbProofFields struct { + // HbPk \[p\] Public key of the heartbeat message. + HbPk *[]byte `json:"hb-pk,omitempty"` + + // HbPk1sig \[p1s\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2. + HbPk1sig *[]byte `json:"hb-pk1sig,omitempty"` + + // HbPk2 \[p2\] Key for new-style two-level ephemeral signature. + HbPk2 *[]byte `json:"hb-pk2,omitempty"` + + // HbPk2sig \[p2s\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier). + HbPk2sig *[]byte `json:"hb-pk2sig,omitempty"` + + // HbSig \[s\] Signature of the heartbeat message. + HbSig *[]byte `json:"hb-sig,omitempty"` +} + // HealthCheck A health check response. type HealthCheck struct { Data *map[string]interface{} `json:"data,omitempty"` @@ -827,6 +847,12 @@ type Transaction struct { // Group \[grp\] Base64 encoded byte array of a sha512/256 digest. When present indicates that this transaction is part of a transaction group and the value is the sha512/256 hash of the transactions in that group. Group *[]byte `json:"group,omitempty"` + // HeartbeatTransaction Fields for a heartbeat transaction. + // + // Definition: + // data/transactions/heartbeat.go : HeartbeatTxnFields + HeartbeatTransaction *TransactionHeartbeat `json:"heartbeat-transaction,omitempty"` + // Id Transaction ID Id *string `json:"id,omitempty"` @@ -897,6 +923,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction + // * \[hb\] heartbeat-transaction TxType TransactionTxType `json:"tx-type"` } @@ -910,6 +937,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction +// * \[hb\] heartbeat-transaction type TransactionTxType string // TransactionApplication Fields for application transactions. @@ -1019,6 +1047,27 @@ type TransactionAssetTransfer struct { Sender *string `json:"sender,omitempty"` } +// TransactionHeartbeat Fields for a heartbeat transaction. +// +// Definition: +// data/transactions/heartbeat.go : HeartbeatTxnFields +type TransactionHeartbeat struct { + // HbAddress \[hbad\] HbAddress is the account this txn is proving onlineness for. + HbAddress string `json:"hb-address"` + + // HbKeyDilution \[hbkd\] HbKeyDilution must match HbAddress account's current KeyDilution. + HbKeyDilution uint64 `json:"hb-key-dilution"` + + // HbProof \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. + HbProof HbProofFields `json:"hb-proof"` + + // HbSeed \[hbsd\] HbSeed must be the block seed for the this transaction's firstValid block. + HbSeed []byte `json:"hb-seed"` + + // HbVoteId \[hbvid\] HbVoteID must match the HbAddress account's current VoteID. + HbVoteId []byte `json:"hb-vote-id"` +} + // TransactionKeyreg Fields for a keyreg transaction. // // Definition: diff --git a/api/generated/v2/routes.go b/api/generated/v2/routes.go index ae2f05591..d90642f3d 100644 --- a/api/generated/v2/routes.go +++ b/api/generated/v2/routes.go @@ -1290,234 +1290,241 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/generated/v2/types.go b/api/generated/v2/types.go index 4916b726c..6b26b2aac 100644 --- a/api/generated/v2/types.go +++ b/api/generated/v2/types.go @@ -36,6 +36,7 @@ const ( TransactionTxTypeAfrz TransactionTxType = "afrz" TransactionTxTypeAppl TransactionTxType = "appl" TransactionTxTypeAxfer TransactionTxType = "axfer" + TransactionTxTypeHb TransactionTxType = "hb" TransactionTxTypeKeyreg TransactionTxType = "keyreg" TransactionTxTypePay TransactionTxType = "pay" TransactionTxTypeStpf TransactionTxType = "stpf" @@ -61,6 +62,7 @@ const ( TxTypeAfrz TxType = "afrz" TxTypeAppl TxType = "appl" TxTypeAxfer TxType = "axfer" + TxTypeHb TxType = "hb" TxTypeKeyreg TxType = "keyreg" TxTypePay TxType = "pay" TxTypeStpf TxType = "stpf" @@ -92,6 +94,7 @@ const ( LookupAccountTransactionsParamsTxTypeAfrz LookupAccountTransactionsParamsTxType = "afrz" LookupAccountTransactionsParamsTxTypeAppl LookupAccountTransactionsParamsTxType = "appl" LookupAccountTransactionsParamsTxTypeAxfer LookupAccountTransactionsParamsTxType = "axfer" + LookupAccountTransactionsParamsTxTypeHb LookupAccountTransactionsParamsTxType = "hb" LookupAccountTransactionsParamsTxTypeKeyreg LookupAccountTransactionsParamsTxType = "keyreg" LookupAccountTransactionsParamsTxTypePay LookupAccountTransactionsParamsTxType = "pay" LookupAccountTransactionsParamsTxTypeStpf LookupAccountTransactionsParamsTxType = "stpf" @@ -110,6 +113,7 @@ const ( LookupAssetTransactionsParamsTxTypeAfrz LookupAssetTransactionsParamsTxType = "afrz" LookupAssetTransactionsParamsTxTypeAppl LookupAssetTransactionsParamsTxType = "appl" LookupAssetTransactionsParamsTxTypeAxfer LookupAssetTransactionsParamsTxType = "axfer" + LookupAssetTransactionsParamsTxTypeHb LookupAssetTransactionsParamsTxType = "hb" LookupAssetTransactionsParamsTxTypeKeyreg LookupAssetTransactionsParamsTxType = "keyreg" LookupAssetTransactionsParamsTxTypePay LookupAssetTransactionsParamsTxType = "pay" LookupAssetTransactionsParamsTxTypeStpf LookupAssetTransactionsParamsTxType = "stpf" @@ -135,6 +139,7 @@ const ( Afrz SearchForTransactionsParamsTxType = "afrz" Appl SearchForTransactionsParamsTxType = "appl" Axfer SearchForTransactionsParamsTxType = "axfer" + Hb SearchForTransactionsParamsTxType = "hb" Keyreg SearchForTransactionsParamsTxType = "keyreg" Pay SearchForTransactionsParamsTxType = "pay" Stpf SearchForTransactionsParamsTxType = "stpf" @@ -649,6 +654,24 @@ type HashFactory struct { // * sha256 type Hashtype string +// HbProofFields \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. +type HbProofFields struct { + // HbPk \[p\] Public key of the heartbeat message. + HbPk *[]byte `json:"hb-pk,omitempty"` + + // HbPk1sig \[p1s\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2. + HbPk1sig *[]byte `json:"hb-pk1sig,omitempty"` + + // HbPk2 \[p2\] Key for new-style two-level ephemeral signature. + HbPk2 *[]byte `json:"hb-pk2,omitempty"` + + // HbPk2sig \[p2s\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier). + HbPk2sig *[]byte `json:"hb-pk2sig,omitempty"` + + // HbSig \[s\] Signature of the heartbeat message. + HbSig *[]byte `json:"hb-sig,omitempty"` +} + // HealthCheck A health check response. type HealthCheck struct { Data *map[string]interface{} `json:"data,omitempty"` @@ -915,6 +938,12 @@ type Transaction struct { // Group \[grp\] Base64 encoded byte array of a sha512/256 digest. When present indicates that this transaction is part of a transaction group and the value is the sha512/256 hash of the transactions in that group. Group *[]byte `json:"group,omitempty"` + // HeartbeatTransaction Fields for a heartbeat transaction. + // + // Definition: + // data/transactions/heartbeat.go : HeartbeatTxnFields + HeartbeatTransaction *TransactionHeartbeat `json:"heartbeat-transaction,omitempty"` + // Id Transaction ID Id *string `json:"id,omitempty"` @@ -985,6 +1014,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction + // * \[hb\] heartbeat-transaction TxType TransactionTxType `json:"tx-type"` } @@ -998,6 +1028,7 @@ type Transaction struct { // * \[afrz\] asset-freeze-transaction // * \[appl\] application-transaction // * \[stpf\] state-proof-transaction +// * \[hb\] heartbeat-transaction type TransactionTxType string // TransactionApplication Fields for application transactions. @@ -1107,6 +1138,27 @@ type TransactionAssetTransfer struct { Sender *string `json:"sender,omitempty"` } +// TransactionHeartbeat Fields for a heartbeat transaction. +// +// Definition: +// data/transactions/heartbeat.go : HeartbeatTxnFields +type TransactionHeartbeat struct { + // HbAddress \[hbad\] HbAddress is the account this txn is proving onlineness for. + HbAddress string `json:"hb-address"` + + // HbKeyDilution \[hbkd\] HbKeyDilution must match HbAddress account's current KeyDilution. + HbKeyDilution uint64 `json:"hb-key-dilution"` + + // HbProof \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. + HbProof HbProofFields `json:"hb-proof"` + + // HbSeed \[hbsd\] HbSeed must be the block seed for the this transaction's firstValid block. + HbSeed []byte `json:"hb-seed"` + + // HbVoteId \[hbvid\] HbVoteID must match the HbAddress account's current VoteID. + HbVoteId []byte `json:"hb-vote-id"` +} + // TransactionKeyreg Fields for a keyreg transaction. // // Definition: diff --git a/api/handlers_test.go b/api/handlers_test.go index c18323513..b290835d4 100644 --- a/api/handlers_test.go +++ b/api/handlers_test.go @@ -643,6 +643,15 @@ func TestFetchTransactions(t *testing.T) { loadTransactionFromFile("test_resources/state_proof_with_index.response"), }, }, + { + name: "Heartbeat Txn", + txnBytes: [][]byte{ + loadResourceFileOrPanic("test_resources/heartbeat.txn"), + }, + response: []generated.Transaction{ + loadTransactionFromFile("test_resources/heartbeat.response"), + }, + }, } // use for the branch below and createTxn helper func to add a new test case @@ -655,8 +664,8 @@ func TestFetchTransactions(t *testing.T) { response []generated.Transaction created uint64 }{ - name: "State Proof Txn", - txnBytes: [][]byte{loadResourceFileOrPanic("test_resources/state_proof.txn")}, + name: "HeartBeat Txn", + txnBytes: [][]byte{loadResourceFileOrPanic("test_resources/heartbeat.txn")}, }) } for _, test := range tests { diff --git a/api/indexer.oas2.json b/api/indexer.oas2.json index 08061df94..17ceb5316 100644 --- a/api/indexer.oas2.json +++ b/api/indexer.oas2.json @@ -2048,7 +2048,8 @@ "asset-transfer-transaction", "keyreg-transaction", "payment-transaction", - "state-proof-transaction" + "state-proof-transaction", + "heartbeat-transaction" ], "properties": { "application-transaction": { @@ -2066,6 +2067,9 @@ "state-proof-transaction": { "$ref": "#/definitions/TransactionStateProof" }, + "heartbeat-transaction": { + "$ref": "#/definitions/TransactionHeartbeat" + }, "auth-addr": { "description": "\\[sgnr\\] this is included with signed transactions when the signing address does not equal the sender. The backend can use this to ensure that auth addr is equal to the accounts auth addr.", "type": "string", @@ -2166,7 +2170,7 @@ "$ref": "#/definitions/TransactionSignature" }, "tx-type": { - "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction", + "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction\n* \\[hb\\] heartbeat-transaction", "type": "string", "enum": [ "pay", @@ -2175,7 +2179,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "x-algorand-format": "tx-type-enum" }, @@ -2333,6 +2338,41 @@ } } }, + "TransactionHeartbeat": { + "description": "Fields for a heartbeat transaction.\n\nDefinition:\ndata/transactions/heartbeat.go : HeartbeatTxnFields", + "type": "object", + "required": [ + "hb-address", + "hb-proof", + "hb-seed", + "hb-vote-id", + "hb-key-dilution" + ], + "properties": { + "hb-address": { + "description": "\\[hbad\\] HbAddress is the account this txn is proving onlineness for.", + "type": "string" + }, + "hb-proof": { + "$ref": "#/definitions/HbProofFields" + }, + "hb-seed": { + "description": "\\[hbsd\\] HbSeed must be the block seed for the this transaction's firstValid block.", + "type": "string", + "format": "byte" + }, + "hb-vote-id": { + "description": "\\[hbvid\\] HbVoteID must match the HbAddress account's current VoteID.", + "type": "string", + "format": "byte" + }, + "hb-key-dilution": { + "description": "\\[hbkd\\] HbKeyDilution must match HbAddress account's current KeyDilution.", + "type": "integer", + "x-algorand-format": "uint64" + } + } + }, "TransactionAssetTransfer": { "description": "Fields for an asset transfer transaction.\n\nDefinition:\ndata/transactions/asset.go : AssetTransferTxnFields", "type": "object", @@ -2564,6 +2604,37 @@ } } }, + "HbProofFields": { + "description": "\\[hbprf\\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online.", + "type": "object", + "properties": { + "hb-sig": { + "description": "\\[s\\] Signature of the heartbeat message.", + "type": "string", + "format": "byte" + }, + "hb-pk": { + "description": "\\[p\\] Public key of the heartbeat message.", + "type": "string", + "format": "byte" + }, + "hb-pk2": { + "description": "\\[p2\\] Key for new-style two-level ephemeral signature.", + "type": "string", + "format": "byte" + }, + "hb-pk1sig": { + "description": "\\[p1s\\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2.", + "type": "string", + "format": "byte" + }, + "hb-pk2sig": { + "description": "\\[p2s\\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier).", + "type": "string", + "format": "byte" + } + } + }, "IndexerStateProofMessage": { "type": "object", "properties": { @@ -2961,7 +3032,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string", "name": "tx-type", diff --git a/api/indexer.oas3.yml b/api/indexer.oas3.yml index 0e2c96185..7a94029d0 100644 --- a/api/indexer.oas3.yml +++ b/api/indexer.oas3.yml @@ -294,7 +294,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -1564,6 +1565,42 @@ ], "type": "string" }, + "HbProofFields": { + "description": "\\[hbprf\\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online.", + "properties": { + "hb-pk": { + "description": "\\[p\\] Public key of the heartbeat message.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk1sig": { + "description": "\\[p1s\\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk2": { + "description": "\\[p2\\] Key for new-style two-level ephemeral signature.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-pk2sig": { + "description": "\\[p2s\\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier).", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-sig": { + "description": "\\[s\\] Signature of the heartbeat message.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + } + }, + "type": "object" + }, "HealthCheck": { "description": "A health check response.", "properties": { @@ -2012,6 +2049,9 @@ "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", "type": "string" }, + "heartbeat-transaction": { + "$ref": "#/components/schemas/TransactionHeartbeat" + }, "id": { "description": "Transaction ID", "type": "string" @@ -2093,7 +2133,7 @@ "$ref": "#/components/schemas/TransactionStateProof" }, "tx-type": { - "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction", + "description": "\\[type\\] Indicates what type of transaction this is. Different types have different fields.\n\nValid types, and where their fields are stored:\n* \\[pay\\] payment-transaction\n* \\[keyreg\\] keyreg-transaction\n* \\[acfg\\] asset-config-transaction\n* \\[axfer\\] asset-transfer-transaction\n* \\[afrz\\] asset-freeze-transaction\n* \\[appl\\] application-transaction\n* \\[stpf\\] state-proof-transaction\n* \\[hb\\] heartbeat-transaction", "enum": [ "pay", "keyreg", @@ -2101,7 +2141,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string", "x-algorand-format": "tx-type-enum" @@ -2259,6 +2300,43 @@ ], "type": "object" }, + "TransactionHeartbeat": { + "description": "Fields for a heartbeat transaction.\n\nDefinition:\ndata/transactions/heartbeat.go : HeartbeatTxnFields", + "properties": { + "hb-address": { + "description": "\\[hbad\\] HbAddress is the account this txn is proving onlineness for.", + "type": "string" + }, + "hb-key-dilution": { + "description": "\\[hbkd\\] HbKeyDilution must match HbAddress account's current KeyDilution.", + "type": "integer", + "x-algorand-format": "uint64" + }, + "hb-proof": { + "$ref": "#/components/schemas/HbProofFields" + }, + "hb-seed": { + "description": "\\[hbsd\\] HbSeed must be the block seed for the this transaction's firstValid block.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "hb-vote-id": { + "description": "\\[hbvid\\] HbVoteID must match the HbAddress account's current VoteID.", + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + } + }, + "required": [ + "hb-address", + "hb-key-dilution", + "hb-proof", + "hb-seed", + "hb-vote-id" + ], + "type": "object" + }, "TransactionKeyreg": { "description": "Fields for a keyreg transaction.\n\nDefinition:\ndata/transactions/keyreg.go : KeyregTxnFields", "properties": { @@ -3439,7 +3517,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -4618,7 +4697,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } @@ -5144,7 +5224,8 @@ "axfer", "afrz", "appl", - "stpf" + "stpf", + "hb" ], "type": "string" } diff --git a/api/test_resources/heartbeat.response b/api/test_resources/heartbeat.response new file mode 100644 index 000000000..5e3840863 --- /dev/null +++ b/api/test_resources/heartbeat.response @@ -0,0 +1,2 @@ +{"close-rewards":0,"closing-amount":0,"confirmed-round":1,"fee":0,"first-valid":106,"genesis-hash":"OW4MK52kEQKHteDrbndB7aFjdP9JvNlI9HYefg2WbDg=","heartbeat-transaction":{"hb-address":"RI53WA75QRYLN64GMKBALH35SDFPEJDW5QMTYU3H2F36DBVAHPDN3FF4YA","hb-key-dilution":100,"hb-proof":{"hb-pk":"xcOVoARNqc58zm7KQrbrSunoTMH+56ISf2BLPX4k5DM=","hb-pk1sig":"KwZl8nfA0linTL+quZ1UF3u0ac0y44nKNvjaUOia8sWGedTWiDr3tElqWTdICVVpvCzn2hIweDaWM1QqREV2BA==","hb-pk2":"ORrVitDsfmg/tlsfRIWu35InLVNSLCNWHPf9cxAz4r0=","hb-pk2sig":"gBcItYg/otjdd3kj0cPKkj6JO+CBeEgjBJ+NGLX4j3kU6u1d8+B8f2hytjkNwmja4w04JZaPzQpP5R8kgqgjDA==","hb-sig":"NvPZHly6M+neFgSXwzsYiL3gav4easw+Nmavp1q/GwWwLIOCX3W7jBzilLHypGymkso/M1RNzUq3O8yMdPA8Cw=="},"hb-seed":"mFwjFbENs+K/0c/ebAMSH2LGcn2YMdJ6gTyo6XF9lfQ=","hb-vote-id":"onzbcE5b1QBnQcWrWWC2g/hE3Wea737jMaap7CSx4ss="},"id":"RCV4LKXW34V6767SD4DBNSE4U4PRBDYKR6NVHZ54FFRVYWDNJV7Q","intra-round-offset":2,"last-valid":116,"receiver-rewards":0,"round-time":1734723734,"sender":"GAU5WA6DT2EPFS6LKOA333BQP67NXIHZ7JPOOHMZWJDPZRL4XMHDDDUCKA","sender-rewards":0,"signature":{},"tx-type":"hb"} + diff --git a/api/test_resources/heartbeat.txn b/api/test_resources/heartbeat.txn new file mode 100644 index 0000000000000000000000000000000000000000..c2070003715cfa97b61a6bbe64c0858f1b901f10 GIT binary patch literal 502 zcmVJX2@Ucj1PhJI11GWMrdoa%P30 zaKs?R!=b{pSU`suIB;+%ra4~blKr04i@^`?}Sf@*J;|(|^mXFN}PvsvZ zf~X@5p>xDQHuKpYT)H#q-WCLx!#fy=z2Iv89%{@!HfFD!*?bnrY2qH|=#AedYu6|oJo;=j?)-fROBA7aLGeV8%QdVxHs z>2ZCP^rLoZWW*q%eA{qNTh#z(LB*?CV77z!MBQha?|$Pkrm5^CvEs|3Y<6^`b8ckB sATTN01H+z(^2^IpfxYZ7f4aAl%sVm|Bq`2YX_ literal 0 HcmV?d00001 diff --git a/e2e_tests/docker/indexer/Dockerfile b/e2e_tests/docker/indexer/Dockerfile index 754e1e8b4..50bff4ae1 100644 --- a/e2e_tests/docker/indexer/Dockerfile +++ b/e2e_tests/docker/indexer/Dockerfile @@ -1,4 +1,4 @@ -ARG GO_IMAGE=golang:1.213.3 +ARG GO_IMAGE=golang:1.23.3 FROM $GO_IMAGE ARG CHANNEL=stable diff --git a/go.mod b/go.mod index 6e2af3088..dd5e53b6a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.3 require ( github.com/algorand/avm-abi v0.2.0 - github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241114170414-874d2110dae4 + github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241220043854-72370807aa70 github.com/algorand/go-codec/codec v1.1.10 github.com/algorand/oapi-codegen v1.12.0-algorand.0 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 107f1976a..fb7e2ce75 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k= github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= -github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241114170414-874d2110dae4 h1:qQXdtMWlP9ynxeDEhWpUWer3AM51482pnfe7ptTDXm0= -github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241114170414-874d2110dae4/go.mod h1:BkHnK2PuCqzdGPNeWUo5yo6lRjyDZ9QoMN8GIjfijrA= +github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241220043854-72370807aa70 h1:nCVMHFT4U9VEC9Znsz4JVNsSFuhIpvpZidfnMtIcYJg= +github.com/algorand/go-algorand-sdk/v2 v2.2.1-0.20241220043854-72370807aa70/go.mod h1:BkHnK2PuCqzdGPNeWUo5yo6lRjyDZ9QoMN8GIjfijrA= github.com/algorand/go-codec/codec v1.1.10 h1:zmWYU1cp64jQVTOG8Tw8wa+k0VfwgXIPbnDfiVa+5QA= github.com/algorand/go-codec/codec v1.1.10/go.mod h1:YkEx5nmr/zuCeaDYOIhlDg92Lxju8tj2d2NrYqP7g7k= github.com/algorand/oapi-codegen v1.12.0-algorand.0 h1:W9PvED+wAJc+9EeXPONnA+0zE9UhynEqoDs4OgAxKhk= diff --git a/idb/postgres/internal/writer/write_txn.go b/idb/postgres/internal/writer/write_txn.go index 4a6be5f0b..97f819fbe 100644 --- a/idb/postgres/internal/writer/write_txn.go +++ b/idb/postgres/internal/writer/write_txn.go @@ -28,7 +28,7 @@ func transactionAssetID(stxnad *types.SignedTxnWithAD, intra uint, block *types. case types.ApplicationCallTx: assetid = uint64(stxnad.Txn.ApplicationID) if assetid == 0 { - assetid = uint64(stxnad.ApplyData.ApplicationID) + assetid = stxnad.ApplyData.ApplicationID } if assetid == 0 { if block == nil { @@ -42,7 +42,7 @@ func transactionAssetID(stxnad *types.SignedTxnWithAD, intra uint, block *types. case types.AssetConfigTx: assetid = uint64(stxnad.Txn.ConfigAsset) if assetid == 0 { - assetid = uint64(stxnad.ApplyData.ConfigAsset) + assetid = stxnad.ApplyData.ConfigAsset } if assetid == 0 { if block == nil { @@ -112,6 +112,7 @@ func yieldInnerTransactions(ctx context.Context, stxnad *types.SignedTxnWithAD, // Writes database rows for transactions (including inner transactions) to `outCh`. func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []types.SignedTxnInBlock, outCh chan []interface{}) error { intra := uint(0) + for idx, stib := range block.Payset { var stxnad types.SignedTxnWithAD var err error @@ -123,7 +124,7 @@ func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []t } txn := &stxnad.Txn - typeenum, ok := idb.GetTypeEnum(types.TxType(txn.Type)) + typeenum, ok := idb.GetTypeEnum(txn.Type) if !ok { return fmt.Errorf("yieldTransactions() get type enum") } @@ -153,9 +154,53 @@ func yieldTransactions(ctx context.Context, block *types.Block, modifiedTxns []t } } + if block.ProposerPayout > 0 { + stxnad := SignedTransactionFromBlockPayout(block) + typeenum, ok := idb.GetTypeEnum(stxnad.Txn.Type) + if !ok { + return fmt.Errorf("yieldTransactions() ProposerPayout get type enum - should NEVER happen") + } + id := crypto.TransactionIDString(stxnad.Txn) + extra := idb.TxnExtra{} + row := []interface{}{ + uint64(block.Round), intra, int(typeenum), 0, id, + encoding.EncodeSignedTxnWithAD(stxnad), + encoding.EncodeTxnExtra(&extra)} + select { + case <-ctx.Done(): + return fmt.Errorf("yieldTransactions() ProposerPayout ctx.Err(): %w", ctx.Err()) + case outCh <- row: + } + } + return nil } +// SignedTransactionFromBlockPayout creates a synthetic transaction for the proposer payout. +func SignedTransactionFromBlockPayout(block *types.Block) types.SignedTxnWithAD { + stxnad := types.SignedTxnWithAD{ + SignedTxn: types.SignedTxn{ + Txn: types.Transaction{ + Type: types.PaymentTx, + Header: types.Header{ + Sender: block.FeeSink, + Note: []byte("ProposerPayout for Round " + fmt.Sprint(block.Round)), + FirstValid: block.Round, + LastValid: block.Round, + GenesisID: block.GenesisID, + GenesisHash: block.GenesisHash, + }, + PaymentTxnFields: types.PaymentTxnFields{ + Receiver: block.Proposer, + Amount: block.ProposerPayout, + }, + }, + }, + } + + return stxnad +} + // AddTransactions adds transactions from `block` to the database. // `modifiedTxns` contains enhanced apply data generated by evaluator. func AddTransactions(block *types.Block, modifiedTxns []types.SignedTxnInBlock, tx pgx.Tx) error { diff --git a/idb/postgres/internal/writer/write_txn_participation.go b/idb/postgres/internal/writer/write_txn_participation.go index 65edf1c1c..d458d8548 100644 --- a/idb/postgres/internal/writer/write_txn_participation.go +++ b/idb/postgres/internal/writer/write_txn_participation.go @@ -125,6 +125,14 @@ func AddTransactionParticipation(block *types.Block, tx pgx.Tx) error { next, rows = addInnerTransactionParticipation(&stxnib.SignedTxnWithAD, uint64(block.Round), next+1, rows) } + if block.ProposerPayout > 0 { + // FeeSink is the sender, Proposer is the receiver. + participants := []types.Address{block.FeeSink, block.Proposer} + for j := range participants { + rows = append(rows, []interface{}{participants[j][:], uint64(block.Round), next}) + } + } + _, err := tx.CopyFrom( context.Background(), pgx.Identifier{"txn_participation"}, diff --git a/idb/postgres/internal/writer/writer_test.go b/idb/postgres/internal/writer/writer_test.go index d793b97a4..e0edcbfb8 100644 --- a/idb/postgres/internal/writer/writer_test.go +++ b/idb/postgres/internal/writer/writer_test.go @@ -168,7 +168,7 @@ func TestWriterSpecialAccounts(t *testing.T) { assert.Equal(t, expected, accounts) } -func TestWriterTxnTableBasic(t *testing.T) { +func TestWriterTxnTableBasicNoPayout(t *testing.T) { db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) defer shutdownFunc() @@ -255,6 +255,116 @@ func TestWriterTxnTableBasic(t *testing.T) { assert.NoError(t, rows.Err()) } +func TestWriterTxnTableBasicWithProposalPayout(t *testing.T) { + db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) + defer shutdownFunc() + + block := sdk.Block{ + BlockHeader: sdk.BlockHeader{ + Round: sdk.Round(2), + TimeStamp: 333, + GenesisID: test.MakeGenesis().ID(), + GenesisHash: test.MakeGenesis().Hash(), + RewardsState: sdk.RewardsState{ + FeeSink: test.FeeAddr, + RewardsLevel: 111111, + }, + TxnCounter: 9, + UpgradeState: sdk.UpgradeState{ + CurrentProtocol: "future", + }, + Proposer: test.AccountE, + ProposerPayout: 2000000, + }, + Payset: make([]sdk.SignedTxnInBlock, 2), + } + + stxnad0 := test.MakePaymentTxn( + 1000, 1, 0, 0, 0, 0, sdk.Address(test.AccountA), sdk.Address(test.AccountB), sdk.Address{}, + sdk.Address{}) + var err error + block.Payset[0], err = + util.EncodeSignedTxn(block.BlockHeader, stxnad0.SignedTxn, stxnad0.ApplyData) + require.NoError(t, err) + + stxnad1 := test.MakeAssetConfigTxn( + 0, 100, 1, false, "ma", "myasset", "myasset.com", sdk.Address(test.AccountA)) + block.Payset[1], err = + util.EncodeSignedTxn(block.BlockHeader, stxnad1.SignedTxn, stxnad1.ApplyData) + require.NoError(t, err) + + f := func(tx pgx.Tx) error { + return writer.AddTransactions(&block, block.Payset, tx) + } + err = pgutil.TxWithRetry(db, serializable, f, nil) + require.NoError(t, err) + + rows, err := db.Query(context.Background(), "SELECT * FROM txn ORDER BY intra") + require.NoError(t, err) + defer rows.Close() + + var round uint64 + var intra uint64 + var typeenum uint + var asset uint64 + var txid []byte + var txn []byte + var extra []byte + + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(0), intra) + assert.Equal(t, idb.TypeEnumPay, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(0), asset) + assert.Equal(t, crypto2.TransactionIDString(stxnad0.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, stxnad0, stxn) + } + assert.Equal(t, "{}", string(extra)) + + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(1), intra) + assert.Equal(t, idb.TypeEnumAssetConfig, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(9), asset) + assert.Equal(t, crypto2.TransactionIDString(stxnad1.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, stxnad1, stxn) + } + assert.Equal(t, "{}", string(extra)) + + // Payout should be the last transaction. + require.True(t, rows.Next()) + err = rows.Scan(&round, &intra, &typeenum, &asset, &txid, &txn, &extra) + require.NoError(t, err) + + assert.Equal(t, block.Round, sdk.Round(round)) + assert.Equal(t, uint64(2), intra) + assert.Equal(t, idb.TypeEnumPay, idb.TxnTypeEnum(typeenum)) + assert.Equal(t, uint64(0), asset) + + // Intentionally using synthetic payout transaction logic; we are testing insertion and validity + payoutTxn := writer.SignedTransactionFromBlockPayout(&block) + assert.Equal(t, crypto2.TransactionIDString(payoutTxn.Txn), string(txid)) + { + stxn, err := encoding.DecodeSignedTxnWithAD(txn) + require.NoError(t, err) + assert.Equal(t, payoutTxn, stxn) + } + assert.Equal(t, "{}", string(extra)) + + assert.False(t, rows.Next()) + assert.NoError(t, rows.Err()) +} + // Test that asset close amount is written even if it is missing in the apply data // in the block (it is present in the "modified transactions"). func TestWriterTxnTableAssetCloseAmount(t *testing.T) { @@ -314,7 +424,7 @@ func TestWriterTxnTableAssetCloseAmount(t *testing.T) { assert.NoError(t, rows.Err()) } -func TestWriterTxnParticipationTable(t *testing.T) { +func TestWriterTxnParticipationTableNoPayout(t *testing.T) { type testtype struct { name string payset sdk.Payset @@ -425,6 +535,144 @@ func TestWriterTxnParticipationTable(t *testing.T) { } } +func TestWriterTxnParticipationTableWithPayout(t *testing.T) { + type testtype struct { + name string + payset sdk.Payset + expected []txnParticipationRow + } + + makeBlockFunc := func() sdk.Block { + return sdk.Block{ + BlockHeader: sdk.BlockHeader{ + Round: sdk.Round(2), + GenesisID: test.MakeGenesis().ID(), + GenesisHash: test.MakeGenesis().Hash(), + RewardsState: sdk.RewardsState{ + FeeSink: test.FeeAddr, + }, + UpgradeState: sdk.UpgradeState{ + CurrentProtocol: "future", + }, + Proposer: test.AccountE, + ProposerPayout: 2000000, + }, + } + } + + var tests []testtype + { + stxnad0 := test.MakePaymentTxn( + 1000, 1, 0, 0, 0, 0, sdk.Address(test.AccountA), sdk.Address(test.AccountB), sdk.Address{}, + sdk.Address{}) + stib0, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad0.SignedTxn, stxnad0.ApplyData) + require.NoError(t, err) + + stxnad1 := test.MakeAssetConfigTxn( + 0, 100, 1, false, "ma", "myasset", "myasset.com", sdk.Address(test.AccountC)) + stib1, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad1.SignedTxn, stxnad1.ApplyData) + require.NoError(t, err) + + testcase := testtype{ + name: "basic", + payset: []sdk.SignedTxnInBlock{stib0, stib1}, + expected: []txnParticipationRow{ + { + addr: test.AccountA, + round: 2, + intra: 0, + }, + { + addr: test.AccountB, + round: 2, + intra: 0, + }, + { + addr: test.AccountC, + round: 2, + intra: 1, + }, + // Payout involved accounts + { + addr: test.AccountE, + round: 2, + intra: 2, + }, + { + addr: test.FeeAddr, + round: 2, + intra: 2, + }, + }, + } + tests = append(tests, testcase) + } + { + stxnad := test.MakeCreateAppTxn(sdk.Address(test.AccountA)) + stxnad.Txn.ApplicationCallTxnFields.Accounts = + []sdk.Address{sdk.Address(test.AccountB), sdk.Address(test.AccountC)} + stib, err := util.EncodeSignedTxn(makeBlockFunc().BlockHeader, stxnad.SignedTxn, stxnad.ApplyData) + require.NoError(t, err) + + testcase := testtype{ + name: "app_call_addresses", + payset: []sdk.SignedTxnInBlock{stib}, + expected: []txnParticipationRow{ + { + addr: sdk.Address(test.AccountA), + round: 2, + intra: 0, + }, + { + addr: sdk.Address(test.AccountB), + round: 2, + intra: 0, + }, + { + addr: sdk.Address(test.AccountC), + round: 2, + intra: 0, + }, + // Payout involved accounts + { + addr: test.AccountE, + round: 2, + intra: 1, + }, + { + addr: test.FeeAddr, + round: 2, + intra: 1, + }, + }, + } + tests = append(tests, testcase) + } + + for _, testcase := range tests { + t.Run(testcase.name, func(t *testing.T) { + db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) + defer shutdownFunc() + + block := makeBlockFunc() + block.Payset = testcase.payset + + f := func(tx pgx.Tx) error { + return writer.AddTransactionParticipation(&block, tx) + } + err := pgutil.TxWithRetry(db, serializable, f, nil) + require.NoError(t, err) + + results, err := txnParticipationQuery( + db, `SELECT * FROM txn_participation ORDER BY round, intra, addr`) + assert.NoError(t, err) + + // Verify expected participation + assert.Equal(t, testcase.expected, results) + }) + } +} + // Create a new account and then delete it. func TestWriterAccountTableBasic(t *testing.T) { db, _, shutdownFunc := pgtest.SetupPostgresWithSchema(t) diff --git a/idb/postgres/postgres.go b/idb/postgres/postgres.go index 7e41128eb..0b8ff2e7c 100644 --- a/idb/postgres/postgres.go +++ b/idb/postgres/postgres.go @@ -698,8 +698,9 @@ func buildTransactionQuery(tf idb.TransactionFilter) (query string, whereArgs [] whereStr := strings.Join(whereParts, " AND ") query += " WHERE " + whereStr } + if joinParticipation { - // this should match the index on txn_particpation + // this should match the index on txn_participation query += " ORDER BY p.addr, p.round DESC, p.intra DESC" } else { // this should explicitly match the primary key on txn (round,intra) diff --git a/idb/txn_type_enum.go b/idb/txn_type_enum.go index 39f0c6cac..286129dce 100644 --- a/idb/txn_type_enum.go +++ b/idb/txn_type_enum.go @@ -20,6 +20,7 @@ const ( TypeEnumAssetFreeze TypeEnumApplication TypeEnumStateProof + TypeEnumHeartbeat ) var typeEnumMap = map[string]TxnTypeEnum{ @@ -30,6 +31,7 @@ var typeEnumMap = map[string]TxnTypeEnum{ "afrz": TypeEnumAssetFreeze, "appl": TypeEnumApplication, "stpf": TypeEnumStateProof, + "hb": TypeEnumHeartbeat, } func makeTypeEnumString() string {