Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildnumber.dat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2
3
12 changes: 12 additions & 0 deletions daemon/algod/api/algod.oas2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
"408": {
"description": "timed out on request",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
},
"500": {
"description": "Internal Error",
"schema": {
Expand Down Expand Up @@ -1338,6 +1344,12 @@
"$ref": "#/definitions/ErrorResponse"
}
},
"408": {
"description": "timed out on request",
"schema": {
"$ref": "#/definitions/ErrorResponse"
}
},
"404": {
"description": "Could not create proof since some data is missing",
"schema": {
Expand Down
20 changes: 20 additions & 0 deletions daemon/algod/api/algod.oas3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2747,6 +2747,16 @@
},
"description": "Could not create proof since some data is missing"
},
"408": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "timed out on request"
},
"500": {
"content": {
"application/json": {
Expand Down Expand Up @@ -3619,6 +3629,16 @@
},
"description": "Could not find a state proof that covers a given round"
},
"408": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
},
"description": "timed out on request"
},
"500": {
"content": {
"application/json": {
Expand Down
418 changes: 209 additions & 209 deletions daemon/algod/api/server/v2/generated/routes.go

Large diffs are not rendered by default.

56 changes: 45 additions & 11 deletions daemon/algod/api/server/v2/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package v2

import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
Expand Down Expand Up @@ -150,10 +151,33 @@ func convertParticipationRecord(record account.ParticipationRecord) generated.Pa
// ErrNoStateProofForRound returned when a state proof transaction could not be found
var ErrNoStateProofForRound = errors.New("no state proof can be found for that round")

// ErrTimeout indicates a task took too long, and the server canceled it.
var ErrTimeout = errors.New("timed out on request")

// ErrShutdown represents the error for the string errServiceShuttingDown
var ErrShutdown = errors.New(errServiceShuttingDown)

// GetStateProofTransactionForRound searches for a state proof transaction that can be used to prove on the given round (i.e the round is within the
// attestation period). the latestRound should be provided as an upper bound for the search
func GetStateProofTransactionForRound(txnFetcher LedgerForAPI, round basics.Round, latestRound basics.Round) (transactions.Transaction, error) {
func GetStateProofTransactionForRound(ctx context.Context, txnFetcher LedgerForAPI, round, latestRound basics.Round, stop <-chan struct{}) (transactions.Transaction, error) {
hdr, err := txnFetcher.BlockHdr(round)
if err != nil {
return transactions.Transaction{}, err
}

if config.Consensus[hdr.CurrentProtocol].StateProofInterval == 0 {
return transactions.Transaction{}, ErrNoStateProofForRound
}

for i := round + 1; i <= latestRound; i++ {
select {
case <-stop:
return transactions.Transaction{}, ErrShutdown
case <-ctx.Done():
return transactions.Transaction{}, ErrTimeout
default:
}

txns, err := txnFetcher.AddressTxns(transactions.StateProofSender, i)
if err != nil {
return transactions.Transaction{}, err
Expand Down Expand Up @@ -1241,16 +1265,17 @@ func (v2 *Handlers) TealCompile(ctx echo.Context, params generated.TealCompilePa
// GetStateProof returns the state proof for a given round.
// (GET /v2/stateproofs/{round})
func (v2 *Handlers) GetStateProof(ctx echo.Context, round uint64) error {
ctxWithTimeout, cancel := context.WithTimeout(ctx.Request().Context(), time.Minute)
defer cancel()

ledger := v2.Node.LedgerForAPI()
if ledger.Latest() < basics.Round(round) {
return internalError(ctx, errors.New(errRoundGreaterThanTheLatest), errRoundGreaterThanTheLatest, v2.Log)
}
tx, err := GetStateProofTransactionForRound(ledger, basics.Round(round), ledger.Latest())

tx, err := GetStateProofTransactionForRound(ctxWithTimeout, ledger, basics.Round(round), ledger.Latest(), v2.Shutdown)
if err != nil {
if errors.Is(err, ErrNoStateProofForRound) {
return notFound(ctx, err, err.Error(), v2.Log)
}
return internalError(ctx, err, err.Error(), v2.Log)
return v2.wrapStateproofError(ctx, err)
}

response := generated.StateProofResponse{
Expand All @@ -1266,20 +1291,29 @@ func (v2 *Handlers) GetStateProof(ctx echo.Context, round uint64) error {
return ctx.JSON(http.StatusOK, response)
}

func (v2 *Handlers) wrapStateproofError(ctx echo.Context, err error) error {
if errors.Is(err, ErrNoStateProofForRound) {
return notFound(ctx, err, err.Error(), v2.Log)
}
if errors.Is(err, ErrTimeout) {
return timeout(ctx, err, err.Error(), v2.Log)
}
return internalError(ctx, err, err.Error(), v2.Log)
}

// GetLightBlockHeaderProof Gets a proof of a light block header for a given round
// (GET /v2/blocks/{round}/lightheader/proof)
func (v2 *Handlers) GetLightBlockHeaderProof(ctx echo.Context, round uint64) error {
ctxWithTimeout, cancel := context.WithTimeout(ctx.Request().Context(), time.Minute)
defer cancel()
ledger := v2.Node.LedgerForAPI()
if ledger.Latest() < basics.Round(round) {
return internalError(ctx, errors.New(errRoundGreaterThanTheLatest), errRoundGreaterThanTheLatest, v2.Log)
}

stateProof, err := GetStateProofTransactionForRound(ledger, basics.Round(round), ledger.Latest())
stateProof, err := GetStateProofTransactionForRound(ctxWithTimeout, ledger, basics.Round(round), ledger.Latest(), v2.Shutdown)
if err != nil {
if errors.Is(err, ErrNoStateProofForRound) {
return notFound(ctx, err, err.Error(), v2.Log)
}
return internalError(ctx, err, err.Error(), v2.Log)
return v2.wrapStateproofError(ctx, err)
}

lastAttestedRound := stateProof.Message.LastAttestedRound
Expand Down
15 changes: 12 additions & 3 deletions daemon/algod/api/server/v2/test/handlers_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package test

import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -98,8 +99,12 @@ func (l *mockLedger) BlockCert(rnd basics.Round) (blk bookkeeping.Block, cert ag
func (l *mockLedger) LatestTotals() (rnd basics.Round, at ledgercore.AccountTotals, err error) {
panic("not implemented")
}
func (l *mockLedger) BlockHdr(rnd basics.Round) (blk bookkeeping.BlockHeader, err error) {
panic("not implemented")
func (l *mockLedger) BlockHdr(rnd basics.Round) (bookkeeping.BlockHeader, error) {
blk, err := l.Block(rnd)
if err != nil {
return bookkeeping.BlockHeader{}, err
}
return blk.BlockHeader, nil
}
func (l *mockLedger) Wait(r basics.Round) chan struct{} {
panic("not implemented")
Expand All @@ -111,7 +116,11 @@ func (l *mockLedger) EncodedBlockCert(rnd basics.Round) (blk []byte, cert []byte
panic("not implemented")
}
func (l *mockLedger) Block(rnd basics.Round) (blk bookkeeping.Block, err error) {
panic("not implemented")
if len(l.blocks) == 0 {
err = fmt.Errorf("mockledger error: no block")
return
}
return l.blocks[0], nil
}

func (l *mockLedger) AddressTxns(id basics.Address, r basics.Round) ([]transactions.SignedTxnWithAD, error) {
Expand Down
85 changes: 81 additions & 4 deletions daemon/algod/api/server/v2/test/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ package test

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -1135,25 +1137,100 @@ func TestStateproofTransactionForRound(t *testing.T) {
var blk bookkeeping.Block
blk.BlockHeader = bookkeeping.BlockHeader{
Round: basics.Round(i),
UpgradeState: bookkeeping.UpgradeState{
CurrentProtocol: protocol.ConsensusCurrentVersion,
},
}
blk = addStateProofIfNeeded(blk)
ledger.blocks = append(ledger.blocks, blk)
}

txn, err := v2.GetStateProofTransactionForRound(&ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000)
ctx, cncl := context.WithTimeout(context.Background(), time.Minute*2)
defer cncl()
txn, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
a.NoError(err)
a.Equal(2*stateProofIntervalForHandlerTests+1, txn.Message.FirstAttestedRound)
a.Equal(3*stateProofIntervalForHandlerTests, txn.Message.LastAttestedRound)
a.Equal([]byte{0x0, 0x1, 0x2}, txn.Message.BlockHeadersCommitment)

txn, err = v2.GetStateProofTransactionForRound(&ledger, basics.Round(2*stateProofIntervalForHandlerTests), 1000)
txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(2*stateProofIntervalForHandlerTests), 1000, nil)
a.NoError(err)
a.Equal(stateProofIntervalForHandlerTests+1, txn.Message.FirstAttestedRound)
a.Equal(2*stateProofIntervalForHandlerTests, txn.Message.LastAttestedRound)

txn, err = v2.GetStateProofTransactionForRound(&ledger, 999, 1000)
txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, 999, 1000, nil)
a.ErrorIs(err, v2.ErrNoStateProofForRound)

txn, err = v2.GetStateProofTransactionForRound(&ledger, basics.Round(2*stateProofIntervalForHandlerTests), basics.Round(2*stateProofIntervalForHandlerTests))
txn, err = v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(2*stateProofIntervalForHandlerTests), basics.Round(2*stateProofIntervalForHandlerTests), nil)
a.ErrorIs(err, v2.ErrNoStateProofForRound)
}

func TestStateproofTransactionForRoundWithoutStateproofs(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
for i := 0; i <= 1000; i++ {
var blk bookkeeping.Block
blk.BlockHeader = bookkeeping.BlockHeader{
Round: basics.Round(i),
UpgradeState: bookkeeping.UpgradeState{
CurrentProtocol: protocol.ConsensusV30, // should have StateProofInterval == 0 .
},
}
blk = addStateProofIfNeeded(blk)
ledger.blocks = append(ledger.blocks, blk)
}
ctx, cncl := context.WithTimeout(context.Background(), time.Minute)
defer cncl()
_, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
a.ErrorIs(err, v2.ErrNoStateProofForRound)
}

func TestStateproofTransactionForRoundTimeouts(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
for i := 0; i <= 1000; i++ {
var blk bookkeeping.Block
blk.BlockHeader = bookkeeping.BlockHeader{
Round: basics.Round(i),
UpgradeState: bookkeeping.UpgradeState{
CurrentProtocol: protocol.ConsensusCurrentVersion, // should have StateProofInterval != 0 .
},
}
blk = addStateProofIfNeeded(blk)
ledger.blocks = append(ledger.blocks, blk)
}

ctx, cncl := context.WithTimeout(context.Background(), time.Nanosecond)
defer cncl()
_, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, nil)
a.ErrorIs(err, v2.ErrTimeout)
}

func TestStateproofTransactionForRoundShutsDown(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)

ledger := mockLedger{blocks: make([]bookkeeping.Block, 0, 1000)}
for i := 0; i <= 1000; i++ {
var blk bookkeeping.Block
blk.BlockHeader = bookkeeping.BlockHeader{
Round: basics.Round(i),
UpgradeState: bookkeeping.UpgradeState{
CurrentProtocol: protocol.ConsensusCurrentVersion, // should have StateProofInterval != 0 .
},
}
blk = addStateProofIfNeeded(blk)
ledger.blocks = append(ledger.blocks, blk)
}

stoppedChan := make(chan struct{})
close(stoppedChan)
ctx, cncl := context.WithTimeout(context.Background(), time.Minute)
defer cncl()
_, err := v2.GetStateProofTransactionForRound(ctx, &ledger, basics.Round(stateProofIntervalForHandlerTests*2+1), 1000, stoppedChan)
a.ErrorIs(err, v2.ErrShutdown)
}
4 changes: 4 additions & 0 deletions daemon/algod/api/server/v2/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func serviceUnavailable(ctx echo.Context, internal error, external string, log l
return returnError(ctx, http.StatusServiceUnavailable, internal, external, log)
}

func timeout(ctx echo.Context, internal error, external string, log logging.Logger) error {
return returnError(ctx, http.StatusRequestTimeout, internal, external, log)
}

func internalError(ctx echo.Context, internal error, external string, log logging.Logger) error {
return returnError(ctx, http.StatusInternalServerError, internal, external, log)
}
Expand Down
Binary file modified installer/external/node_exporter-stable-darwin-x86_64.tar.gz
Binary file not shown.
Binary file modified installer/external/node_exporter-stable-linux-x86_64.tar.gz
Binary file not shown.
17 changes: 13 additions & 4 deletions network/wsPeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,24 @@ const averageMessageLength = 2 * 1024 // Most of the messages are smaller tha
// buffer and starve messages from other peers.
const msgsInReadBufferPerPeer = 10

var tagStringList []string

func init() {
tagStringList = make([]string, len(protocol.TagList))
for i, t := range protocol.TagList {
tagStringList[i] = string(t)
}
}

var networkSentBytesTotal = metrics.MakeCounter(metrics.NetworkSentBytesTotal)
var networkSentBytesByTag = metrics.NewTagCounter("algod_network_sent_bytes_{TAG}", "Number of bytes that were sent over the network for {TAG} messages")
var networkSentBytesByTag = metrics.NewTagCounterFiltered("algod_network_sent_bytes_{TAG}", "Number of bytes that were sent over the network for {TAG} messages", tagStringList, "UNK")
var networkReceivedBytesTotal = metrics.MakeCounter(metrics.NetworkReceivedBytesTotal)
var networkReceivedBytesByTag = metrics.NewTagCounter("algod_network_received_bytes_{TAG}", "Number of bytes that were received from the network for {TAG} messages")
var networkReceivedBytesByTag = metrics.NewTagCounterFiltered("algod_network_received_bytes_{TAG}", "Number of bytes that were received from the network for {TAG} messages", tagStringList, "UNK")

var networkMessageReceivedTotal = metrics.MakeCounter(metrics.NetworkMessageReceivedTotal)
var networkMessageReceivedByTag = metrics.NewTagCounter("algod_network_message_received_{TAG}", "Number of complete messages that were received from the network for {TAG} messages")
var networkMessageReceivedByTag = metrics.NewTagCounterFiltered("algod_network_message_received_{TAG}", "Number of complete messages that were received from the network for {TAG} messages", tagStringList, "UNK")
var networkMessageSentTotal = metrics.MakeCounter(metrics.NetworkMessageSentTotal)
var networkMessageSentByTag = metrics.NewTagCounter("algod_network_message_sent_{TAG}", "Number of complete messages that were sent to the network for {TAG} messages")
var networkMessageSentByTag = metrics.NewTagCounterFiltered("algod_network_message_sent_{TAG}", "Number of complete messages that were sent to the network for {TAG} messages", tagStringList, "UNK")

var networkConnectionsDroppedTotal = metrics.MakeCounter(metrics.NetworkConnectionsDroppedTotal)
var networkMessageQueueMicrosTotal = metrics.MakeCounter(metrics.MetricName{Name: "algod_network_message_sent_queue_micros_total", Description: "Total microseconds message spent waiting in queue to be sent"})
Expand Down
19 changes: 19 additions & 0 deletions protocol/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,22 @@ const (
//UniCatchupResTag Tag = "UT" was used for wsfetcherservice
VoteBundleTag Tag = "VB"
)

// TagList is a list of all currently used protocol tags.
// TODO: generate this and/or have a test that it is complete.
var TagList = []Tag{
UnknownMsgTag,
AgreementVoteTag,
MsgOfInterestTag,
MsgDigestSkipTag,
NetPrioResponseTag,
PingTag,
PingReplyTag,
ProposalPayloadTag,
StateProofSigTag,
TopicMsgRespTag,
TxnTag,
UniCatchupReqTag,
UniEnsBlockReqTag,
VoteBundleTag,
}
Loading