diff --git a/.gitignore b/.gitignore index 8deb3a2d9f..b0f211512a 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ crypto/libs # doc intermediates data/transactions/logic/*.md +!data/transactions/logic/TEAL_opcodes*.md *.pem diff --git a/agreement/actions.go b/agreement/actions.go index 833dc8af36..0e0d5f19c4 100644 --- a/agreement/actions.go +++ b/agreement/actions.go @@ -19,6 +19,7 @@ package agreement import ( "context" "fmt" + "time" "github.com/algorand/go-algorand/logging/logspec" "github.com/algorand/go-algorand/logging/telemetryspec" @@ -65,6 +66,7 @@ type action interface { do(context.Context, *Service) String() string + ComparableStr() string } type nonpersistent struct{} @@ -87,6 +89,8 @@ func (a noopAction) String() string { return a.t().String() } +func (a noopAction) ComparableStr() string { return a.String() } + type networkAction struct { nonpersistent @@ -119,6 +123,13 @@ func (a networkAction) String() string { return fmt.Sprintf("%s: %2v", a.t().String(), a.Tag) } +func (a networkAction) ComparableStr() string { + if a.Tag == protocol.AgreementVoteTag { + return fmt.Sprintf("%s: %2v: %3v-%2v-%2v", a.t().String(), a.Tag, a.UnauthenticatedVote.R.Round, a.UnauthenticatedVote.R.Period, a.UnauthenticatedVote.R.Step) + } + return a.String() +} + func (a networkAction) do(ctx context.Context, s *Service) { if a.T == broadcastVotes { tag := protocol.AgreementVoteTag @@ -191,6 +202,18 @@ func (a cryptoAction) String() string { return a.t().String() } +func (a cryptoAction) ComparableStr() (s string) { + switch a.T { + case verifyVote: + s = fmt.Sprintf("%s: %3v-%2v TaskIndex %d", a.t().String(), a.Round, a.Period, a.TaskIndex) + case verifyPayload: + s = fmt.Sprintf("%s: %3v-%2v Pinned %v", a.t().String(), a.Round, a.Period, a.Pinned) + case verifyBundle: + s = fmt.Sprintf("%s: %3v-%2v-%2v", a.t().String(), a.Round, a.Period, a.Step) + } + return +} + func (a cryptoAction) do(ctx context.Context, s *Service) { switch a.T { case verifyVote: @@ -209,6 +232,11 @@ type ensureAction struct { Payload proposal // the certificate proving commitment Certificate Certificate + + // The time that the winning proposal-vote was validated, relative to the beginning of the round + voteValidatedAt time.Duration + // The dynamic filter timeout calculated for this round, even if not enabled, for reporting to telemetry. + dynamicFilterTimeout time.Duration } func (a ensureAction) t() actionType { @@ -219,6 +247,8 @@ func (a ensureAction) String() string { return fmt.Sprintf("%s: %.5s: %v, %v, %.5s", a.t().String(), a.Payload.Digest().String(), a.Certificate.Round, a.Certificate.Period, a.Certificate.Proposal.BlockDigest.String()) } +func (a ensureAction) ComparableStr() string { return a.String() } + func (a ensureAction) do(ctx context.Context, s *Service) { logEvent := logspec.AgreementEvent{ Hash: a.Certificate.Proposal.BlockDigest.String(), @@ -231,14 +261,16 @@ func (a ensureAction) do(ctx context.Context, s *Service) { logEvent.Type = logspec.RoundConcluded s.log.with(logEvent).Infof("committed round %d with pre-validated block %v", a.Certificate.Round, a.Certificate.Proposal) s.log.EventWithDetails(telemetryspec.Agreement, telemetryspec.BlockAcceptedEvent, telemetryspec.BlockAcceptedEventDetails{ - Address: a.Certificate.Proposal.OriginalProposer.String(), - Hash: a.Certificate.Proposal.BlockDigest.String(), - Round: uint64(a.Certificate.Round), - ValidatedAt: a.Payload.validatedAt, - ReceivedAt: a.Payload.receivedAt, - PreValidated: true, - PropBufLen: uint64(len(s.demux.rawProposals)), - VoteBufLen: uint64(len(s.demux.rawVotes)), + Address: a.Certificate.Proposal.OriginalProposer.String(), + Hash: a.Certificate.Proposal.BlockDigest.String(), + Round: uint64(a.Certificate.Round), + ValidatedAt: a.Payload.validatedAt, + ReceivedAt: a.Payload.receivedAt, + VoteValidatedAt: a.voteValidatedAt, + DynamicFilterTimeout: a.dynamicFilterTimeout, + PreValidated: true, + PropBufLen: uint64(len(s.demux.rawProposals)), + VoteBufLen: uint64(len(s.demux.rawVotes)), }) s.Ledger.EnsureValidatedBlock(a.Payload.ve, a.Certificate) } else { @@ -246,14 +278,16 @@ func (a ensureAction) do(ctx context.Context, s *Service) { logEvent.Type = logspec.RoundConcluded s.log.with(logEvent).Infof("committed round %d with block %v", a.Certificate.Round, a.Certificate.Proposal) s.log.EventWithDetails(telemetryspec.Agreement, telemetryspec.BlockAcceptedEvent, telemetryspec.BlockAcceptedEventDetails{ - Address: a.Certificate.Proposal.OriginalProposer.String(), - Hash: a.Certificate.Proposal.BlockDigest.String(), - Round: uint64(a.Certificate.Round), - ValidatedAt: a.Payload.validatedAt, - ReceivedAt: a.Payload.receivedAt, - PreValidated: false, - PropBufLen: uint64(len(s.demux.rawProposals)), - VoteBufLen: uint64(len(s.demux.rawVotes)), + Address: a.Certificate.Proposal.OriginalProposer.String(), + Hash: a.Certificate.Proposal.BlockDigest.String(), + Round: uint64(a.Certificate.Round), + ValidatedAt: a.Payload.validatedAt, + ReceivedAt: a.Payload.receivedAt, + VoteValidatedAt: a.voteValidatedAt, + DynamicFilterTimeout: a.dynamicFilterTimeout, + PreValidated: false, + PropBufLen: uint64(len(s.demux.rawProposals)), + VoteBufLen: uint64(len(s.demux.rawVotes)), }) s.Ledger.EnsureBlock(block, a.Certificate) } @@ -278,6 +312,8 @@ func (a stageDigestAction) String() string { return fmt.Sprintf("%s: %.5s. %v. %v", a.t().String(), a.Certificate.Proposal.BlockDigest.String(), a.Certificate.Round, a.Certificate.Period) } +func (a stageDigestAction) ComparableStr() string { return a.String() } + func (a stageDigestAction) do(ctx context.Context, service *Service) { logEvent := logspec.AgreementEvent{ Hash: a.Certificate.Proposal.BlockDigest.String(), @@ -304,8 +340,25 @@ func (a rezeroAction) String() string { return a.t().String() } +func (a rezeroAction) ComparableStr() string { + return fmt.Sprintf("%s: %d", a.t().String(), a.Round) +} + func (a rezeroAction) do(ctx context.Context, s *Service) { s.Clock = s.Clock.Zero() + // Preserve the zero time of the new round a.Round (for + // period 0) for future use if a late proposal-vote arrives, + // for late credential tracking. + if _, ok := s.historicalClocks[a.Round]; !ok { + s.historicalClocks[a.Round] = s.Clock + } + + // Garbage collect clocks that are too old + for rnd := range s.historicalClocks { + if a.Round > rnd+credentialRoundLag { + delete(s.historicalClocks, rnd) + } + } } type pseudonodeAction struct { @@ -326,6 +379,8 @@ func (a pseudonodeAction) String() string { return fmt.Sprintf("%v %3v-%2v-%2v: %.5v", a.t().String(), a.Round, a.Period, a.Step, a.Proposal.BlockDigest.String()) } +func (a pseudonodeAction) ComparableStr() string { return a.String() } + func (a pseudonodeAction) persistent() bool { return a.T == attest } @@ -518,3 +573,5 @@ func (c checkpointAction) do(ctx context.Context, s *Service) { func (c checkpointAction) String() string { return c.t().String() } + +func (c checkpointAction) ComparableStr() string { return c.String() } diff --git a/agreement/credentialArrivalHistory.go b/agreement/credentialArrivalHistory.go new file mode 100644 index 0000000000..b4bc48c976 --- /dev/null +++ b/agreement/credentialArrivalHistory.go @@ -0,0 +1,83 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package agreement + +import ( + "sort" + "time" +) + +// credentialArrivalHistory maintains a circular buffer of time.Duration samples. +type credentialArrivalHistory struct { + history []time.Duration + writePtr int + full bool +} + +func makeCredentialArrivalHistory(size int) credentialArrivalHistory { + if size < 0 { + panic("can't create CredentialArrivalHistory with negative size") + } + history := credentialArrivalHistory{history: make([]time.Duration, size)} + history.reset() + return history +} + +// store saves a new sample into the circular buffer. +// If the buffer is full, it overwrites the oldest sample. +func (history *credentialArrivalHistory) store(sample time.Duration) { + if len(history.history) == 0 { + return + } + + history.history[history.writePtr] = sample + history.writePtr++ + if history.writePtr == len(history.history) { + history.full = true + history.writePtr = 0 + } +} + +// reset marks the history buffer as empty +func (history *credentialArrivalHistory) reset() { + history.writePtr = 0 + history.full = false +} + +// isFull checks if the circular buffer has been fully populated at least once. +func (history *credentialArrivalHistory) isFull() bool { + return history.full +} + +// orderStatistics returns the idx'th time duration in the sorted history array. +// It assumes that history is full and the idx is within the array bounds, and +// panics if either of these assumptions doesn't hold. +func (history *credentialArrivalHistory) orderStatistics(idx int) time.Duration { + if !history.isFull() { + panic("history not full") + } + if idx < 0 || idx >= len(history.history) { + panic("index out of bounds") + } + + // if history.history is long, then we could optimize this function to use + // the linear time order statistics algorithm. + sortedArrivals := make([]time.Duration, len(history.history)) + copy(sortedArrivals[:], history.history[:]) + sort.Slice(sortedArrivals, func(i, j int) bool { return sortedArrivals[i] < sortedArrivals[j] }) + return sortedArrivals[idx] +} diff --git a/agreement/credentialArrivalHistory_test.go b/agreement/credentialArrivalHistory_test.go new file mode 100644 index 0000000000..8626094dd8 --- /dev/null +++ b/agreement/credentialArrivalHistory_test.go @@ -0,0 +1,129 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package agreement + +import ( + "testing" + "time" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestCredentialHistoryStore(t *testing.T) { + partitiontest.PartitionTest(t) + + size := 5 + buffer := makeCredentialArrivalHistory(size) + // last store call overwrites the first one + for i := 0; i < size+1; i++ { + buffer.store(time.Duration(i)) + } + + require.True(t, buffer.isFull()) + require.Equal(t, time.Duration(size), buffer.history[0]) + for i := 1; i < size; i++ { + require.Equal(t, time.Duration(i), buffer.history[i]) + } +} + +func TestCredentialHistoryReset(t *testing.T) { + partitiontest.PartitionTest(t) + + size := 5 + buffer := makeCredentialArrivalHistory(size) + // last store call overwrites the first one + for i := 0; i < size+1; i++ { + buffer.store(time.Duration(i)) + } + + require.Equal(t, time.Duration(size), buffer.history[0]) + for i := 1; i < size; i++ { + require.Equal(t, time.Duration(i), buffer.history[i]) + } + require.True(t, buffer.isFull()) + buffer.reset() + require.False(t, buffer.isFull()) + buffer.store(time.Duration(100)) + require.Equal(t, time.Duration(100), buffer.history[0]) +} + +func TestCredentialHistoryIsFull(t *testing.T) { + partitiontest.PartitionTest(t) + var buffer credentialArrivalHistory + require.False(t, buffer.isFull()) + + size := 5 + buffer = makeCredentialArrivalHistory(size) + require.False(t, buffer.isFull()) + + for i := 1; i < size+10; i++ { + buffer.store(time.Duration(i)) + if i < size { + require.False(t, buffer.isFull()) + } else { + require.True(t, buffer.isFull()) + } + } + + // reset the buffer and then fill it again + buffer.reset() + require.False(t, buffer.isFull()) + + for i := 1; i < size+10; i++ { + buffer.store(time.Duration(i)) + if i < size { + require.False(t, buffer.isFull()) + } else { + require.True(t, buffer.isFull()) + } + } +} + +func TestCredentialHisotyZeroSize(t *testing.T) { + partitiontest.PartitionTest(t) + + var buffer credentialArrivalHistory + require.False(t, buffer.isFull()) + + size := 0 + buffer = makeCredentialArrivalHistory(size) + require.False(t, buffer.isFull()) + + // trying to store new samples won't panic but the history is never full + for i := 0; i < size+10; i++ { + buffer.store(time.Duration(i)) + require.False(t, buffer.isFull()) + } +} + +func TestOrderStatistics(t *testing.T) { + partitiontest.PartitionTest(t) + + size := 5 + buffer := makeCredentialArrivalHistory(size) + require.False(t, buffer.isFull()) + + for i := 0; i < size; i++ { + buffer.store(time.Duration(size - i)) + } + require.True(t, buffer.isFull()) + + for i := 0; i < size; i++ { + require.Equal(t, time.Duration(i+1), buffer.orderStatistics(i)) + } +} diff --git a/agreement/demux.go b/agreement/demux.go index c225396293..7f888dc97f 100644 --- a/agreement/demux.go +++ b/agreement/demux.go @@ -199,9 +199,14 @@ func (d *demux) next(s *Service, deadline Deadline, fastDeadline Deadline, curre switch e.t() { case payloadVerified: - e = e.(messageEvent).AttachValidatedAt(s.Clock.Since()) + e = e.(messageEvent).AttachValidatedAt(clockForRound(currentRound, s.Clock, s.historicalClocks)) case payloadPresent, votePresent: - e = e.(messageEvent).AttachReceivedAt(s.Clock.Since()) + e = e.(messageEvent).AttachReceivedAt(clockForRound(currentRound, s.Clock, s.historicalClocks)) + case voteVerified: + // if this is a proposal vote (step 0), record the validatedAt time on the vote + if e.(messageEvent).Input.Vote.R.Step == 0 { + e = e.(messageEvent).AttachValidatedAt(clockForRound(currentRound, s.Clock, s.historicalClocks)) + } } }() diff --git a/agreement/dynamicFilterTimeoutParams.go b/agreement/dynamicFilterTimeoutParams.go new file mode 100644 index 0000000000..36348615b5 --- /dev/null +++ b/agreement/dynamicFilterTimeoutParams.go @@ -0,0 +1,45 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package agreement + +import "time" + +// This file contains parameters for the dynamic filter timeout mechanism. When +// this feature is enabled (dynamicFilterTimeout is true), these parameters +// should migrate to be consensus params. + +// DynamicFilterCredentialArrivalHistory specifies the number of past +// credential arrivals that are measured to determine the next filter +// timeout. If DynamicFilterCredentialArrivalHistory <= 0, then the dynamic +// timeout feature is off and the filter step timeout is calculated using +// the static configuration. +const dynamicFilterCredentialArrivalHistory int = 40 + +// DynamicFilterTimeoutLowerBound specifies a minimal duration that the +// filter timeout must meet. +const dynamicFilterTimeoutLowerBound time.Duration = 500 * time.Millisecond + +// DynamicFilterTimeoutCredentialArrivalHistoryIdx specified which sample to use +// out of a sorted DynamicFilterCredentialArrivalHistory-sized array of time +// samples. The 95th percentile of dynamicFilterCredentialArrivalHistory = 40 +// sorted samples, is at index 37. +const dynamicFilterTimeoutCredentialArrivalHistoryIdx int = 37 + +// DynamicFilterTimeoutGraceInterval is additional extension to the dynamic +// filter time atop the one calculated based on the history of credential +// arrivals. +const dynamicFilterTimeoutGraceInterval time.Duration = 50 * time.Millisecond diff --git a/agreement/dynamicFilterTimeoutParams_test.go b/agreement/dynamicFilterTimeoutParams_test.go new file mode 100644 index 0000000000..7d03de7dea --- /dev/null +++ b/agreement/dynamicFilterTimeoutParams_test.go @@ -0,0 +1,41 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package agreement + +import ( + "testing" + "time" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestSampleIndexIsValid(t *testing.T) { + partitiontest.PartitionTest(t) + + require.GreaterOrEqual(t, dynamicFilterCredentialArrivalHistory, 0) + require.GreaterOrEqual(t, dynamicFilterTimeoutCredentialArrivalHistoryIdx, 0) + if dynamicFilterCredentialArrivalHistory > 0 { + require.Less(t, dynamicFilterTimeoutCredentialArrivalHistoryIdx, dynamicFilterCredentialArrivalHistory) + } +} + +func TestLowerBound(t *testing.T) { + partitiontest.PartitionTest(t) + + require.Less(t, 20*time.Millisecond, dynamicFilterTimeoutLowerBound) +} diff --git a/agreement/events.go b/agreement/events.go index 52737e5f2c..afcfa3052d 100644 --- a/agreement/events.go +++ b/agreement/events.go @@ -197,6 +197,10 @@ const ( // readPinned is sent to the proposalStore to read the pinned value, if it exists. readPinned + // readLowestVote is sent to the proposalPeriodMachine to read the + // proposal-vote with the lowest credential. + readLowestVote + /* * The following are event types that replace queries, and may warrant * a revision to make them more state-machine-esque. @@ -292,7 +296,7 @@ func (e messageEvent) String() string { } func (e messageEvent) ComparableStr() string { - return e.T.String() + return fmt.Sprintf("{T:%s %d Err:%v}", e.t().String(), e.ConsensusRound(), e.Err) } func (e messageEvent) ConsensusRound() round { @@ -407,6 +411,41 @@ func (e newRoundEvent) ComparableStr() string { return e.String() } +type readLowestEvent struct { + // T currently only supports readLowestVote + T eventType + + // Round and Period are the round and period for which to query the + // lowest-credential vote, value or payload. This type of event is only + // sent for reading the lowest period 0 credential, but the Period is here + // anyway to route to the appropriate proposalMachinePeriod. + Round round + Period period + + // Vote holds the lowest-credential vote. + Vote vote + // LowestIncludingLate holds the lowest-credential vote that was received, including + // after Vote has been frozen. + LowestIncludingLate vote + + // Filled and HasLowestIncludingLate indicates whether the Vote or LowestIncludingLate + // fields are filled, respectively. + Filled bool + HasLowestIncludingLate bool +} + +func (e readLowestEvent) t() eventType { + return e.T +} + +func (e readLowestEvent) String() string { + return fmt.Sprintf("%s: %d %d", e.t().String(), e.Round, e.Period) +} + +func (e readLowestEvent) ComparableStr() string { + return e.String() +} + type newPeriodEvent struct { // Period holds the latest period relevant to the proposalRoundMachine. Period period @@ -557,10 +596,36 @@ func (e payloadProcessedEvent) ComparableStr() string { return fmt.Sprintf("%v: %.5v", e.t().String(), e.Proposal.BlockDigest.String()) } +// LateCredentialTrackingEffect indicates the impact of a vote that was filtered (due to age) +// on the credential tracking system (in credentialArrivalHistory), for the purpose of tracking +// the time it took the best credential to arrive, even if it was late. +type LateCredentialTrackingEffect uint8 + +const ( + // NoLateCredentialTrackingImpact indicates the filtered event would have no impact on + // the credential tracking mechanism. + NoLateCredentialTrackingImpact LateCredentialTrackingEffect = iota + + // UnverifiedLateCredentialForTracking indicates the filtered event could impact + // the credential tracking mechanism and more processing (validation) may be required. + // It may be set by proposalManager when handling votePresent events. + UnverifiedLateCredentialForTracking + + // VerifiedBetterLateCredentialForTracking indicates that the filtered event provides a new best + // credential for its round. + // It may be set by proposalManager when handling voteVerified events. + VerifiedBetterLateCredentialForTracking +) + type filteredEvent struct { // {proposal,vote,bundle}{Filtered,Malformed} T eventType + // LateCredentialTrackingNote indicates the impact of the filtered event on the + // credential tracking machinery used for dynamically setting the filter + // timeout. + LateCredentialTrackingNote LateCredentialTrackingEffect + // Err is the reason cryptographic verification failed and is set for // events {proposal,vote,bundle}Malformed. Err *serializableError @@ -941,18 +1006,52 @@ func (e checkpointEvent) AttachConsensusVersion(v ConsensusVersionView) external return e } -func (e messageEvent) AttachValidatedAt(d time.Duration) messageEvent { - e.Input.Proposal.validatedAt = d +// This timestamp is assigned to messages that arrive for round R+1 while the current player +// is still waiting for quorum on R. +const pipelinedMessageTimestamp = time.Nanosecond + +//msgp:ignore constantRoundStartTimer +type constantRoundStartTimer time.Duration + +func (c constantRoundStartTimer) Since() time.Duration { return time.Duration(c) } + +// clockForRound retrieves the roundStartTimer used for AttachValidatedAt and AttachReceivedAt. +func clockForRound(currentRound round, currentClock roundStartTimer, historicalClocks map[round]roundStartTimer) func(round) roundStartTimer { + return func(eventRound round) roundStartTimer { + if eventRound > currentRound { + return constantRoundStartTimer(pipelinedMessageTimestamp) + } + if eventRound == currentRound { + return currentClock + } + if clock, ok := historicalClocks[eventRound]; ok { + return clock + } + return constantRoundStartTimer(0) + } +} + +// AttachValidatedAt looks for a validated proposal or vote inside a +// payloadVerified or voteVerified messageEvent, and attaches the given time to +// the proposal's validatedAt field. +func (e messageEvent) AttachValidatedAt(getClock func(eventRound round) roundStartTimer) messageEvent { + switch e.T { + case payloadVerified: + e.Input.Proposal.validatedAt = getClock(e.Input.Proposal.Round()).Since() + case voteVerified: + e.Input.Vote.validatedAt = getClock(e.Input.Vote.R.Round).Since() + } return e } // AttachReceivedAt looks for an unauthenticatedProposal inside a // payloadPresent or votePresent messageEvent, and attaches the given // time to the proposal's receivedAt field. -func (e messageEvent) AttachReceivedAt(d time.Duration) messageEvent { - if e.T == payloadPresent { - e.Input.UnauthenticatedProposal.receivedAt = d - } else if e.T == votePresent { +func (e messageEvent) AttachReceivedAt(getClock func(eventRound round) roundStartTimer) messageEvent { + switch e.T { + case payloadPresent: + e.Input.UnauthenticatedProposal.receivedAt = getClock(e.Input.UnauthenticatedProposal.Round()).Since() + case votePresent: // Check for non-nil Tail, indicating this votePresent event // contains a synthetic payloadPresent event that was attached // to it by setupCompoundMessage. @@ -960,7 +1059,7 @@ func (e messageEvent) AttachReceivedAt(d time.Duration) messageEvent { // The tail event is payloadPresent, serialized together // with the proposal vote as a single CompoundMessage // using a protocol.ProposalPayloadTag network message. - e.Tail.Input.UnauthenticatedProposal.receivedAt = d + e.Tail.Input.UnauthenticatedProposal.receivedAt = getClock(e.Tail.Input.UnauthenticatedProposal.Round()).Since() } } return e diff --git a/agreement/eventtype_string.go b/agreement/eventtype_string.go index 9da84c1b98..9973215b12 100644 --- a/agreement/eventtype_string.go +++ b/agreement/eventtype_string.go @@ -37,21 +37,22 @@ func _() { _ = x[newPeriod-26] _ = x[readStaging-27] _ = x[readPinned-28] - _ = x[voteFilterRequest-29] - _ = x[voteFilteredStep-30] - _ = x[nextThresholdStatusRequest-31] - _ = x[nextThresholdStatus-32] - _ = x[freshestBundleRequest-33] - _ = x[freshestBundle-34] - _ = x[dumpVotesRequest-35] - _ = x[dumpVotes-36] - _ = x[wrappedAction-37] - _ = x[checkpointReached-38] + _ = x[readLowestVote-29] + _ = x[voteFilterRequest-30] + _ = x[voteFilteredStep-31] + _ = x[nextThresholdStatusRequest-32] + _ = x[nextThresholdStatus-33] + _ = x[freshestBundleRequest-34] + _ = x[freshestBundle-35] + _ = x[dumpVotesRequest-36] + _ = x[dumpVotes-37] + _ = x[wrappedAction-38] + _ = x[checkpointReached-39] } -const _eventType_name = "nonevotePresentpayloadPresentbundlePresentvoteVerifiedpayloadVerifiedbundleVerifiedroundInterruptiontimeoutfastTimeoutsoftThresholdcertThresholdnextThresholdproposalCommittableproposalAcceptedvoteFilteredvoteMalformedbundleFilteredbundleMalformedpayloadRejectedpayloadMalformedpayloadPipelinedpayloadAcceptedproposalFrozenvoteAcceptednewRoundnewPeriodreadStagingreadPinnedvoteFilterRequestvoteFilteredStepnextThresholdStatusRequestnextThresholdStatusfreshestBundleRequestfreshestBundledumpVotesRequestdumpVoteswrappedActioncheckpointReached" +const _eventType_name = "nonevotePresentpayloadPresentbundlePresentvoteVerifiedpayloadVerifiedbundleVerifiedroundInterruptiontimeoutfastTimeoutsoftThresholdcertThresholdnextThresholdproposalCommittableproposalAcceptedvoteFilteredvoteMalformedbundleFilteredbundleMalformedpayloadRejectedpayloadMalformedpayloadPipelinedpayloadAcceptedproposalFrozenvoteAcceptednewRoundnewPeriodreadStagingreadPinnedreadLowestVotevoteFilterRequestvoteFilteredStepnextThresholdStatusRequestnextThresholdStatusfreshestBundleRequestfreshestBundledumpVotesRequestdumpVoteswrappedActioncheckpointReached" -var _eventType_index = [...]uint16{0, 4, 15, 29, 42, 54, 69, 83, 100, 107, 118, 131, 144, 157, 176, 192, 204, 217, 231, 246, 261, 277, 293, 308, 322, 334, 342, 351, 362, 372, 389, 405, 431, 450, 471, 485, 501, 510, 523, 540} +var _eventType_index = [...]uint16{0, 4, 15, 29, 42, 54, 69, 83, 100, 107, 118, 131, 144, 157, 176, 192, 204, 217, 231, 246, 261, 277, 293, 308, 322, 334, 342, 351, 362, 372, 386, 403, 419, 445, 464, 485, 499, 515, 524, 537, 554} func (i eventType) String() string { if i >= eventType(len(_eventType_index)-1) { diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 90de4bd8a5..16679ce797 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -46,6 +46,16 @@ import ( // |-----> (*) MsgIsZero // |-----> DeadlineMaxSize() // +// LateCredentialTrackingEffect +// |-----> MarshalMsg +// |-----> CanMarshalMsg +// |-----> (*) UnmarshalMsg +// |-----> (*) UnmarshalMsgWithState +// |-----> (*) CanUnmarshalMsg +// |-----> Msgsize +// |-----> MsgIsZero +// |-----> LateCredentialTrackingEffectMaxSize() +// // TimeoutType // |-----> MarshalMsg // |-----> CanMarshalMsg @@ -605,11 +615,11 @@ func (_ *Certificate) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Certificate) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -902,11 +912,11 @@ func (_ *ConsensusVersionView) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ConsensusVersionView) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1065,11 +1075,11 @@ func (_ *Deadline) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Deadline) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1178,6 +1188,66 @@ func DeadlineMaxSize() (s int) { return } +// MarshalMsg implements msgp.Marshaler +func (z LateCredentialTrackingEffect) MarshalMsg(b []byte) (o []byte) { + o = msgp.Require(b, z.Msgsize()) + o = msgp.AppendUint8(o, uint8(z)) + return +} + +func (_ LateCredentialTrackingEffect) CanMarshalMsg(z interface{}) bool { + _, ok := (z).(LateCredentialTrackingEffect) + if !ok { + _, ok = (z).(*LateCredentialTrackingEffect) + } + return ok +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *LateCredentialTrackingEffect) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { + if st.AllowableDepth == 0 { + err = msgp.ErrMaxDepthExceeded{} + return + } + st.AllowableDepth-- + { + var zb0001 uint8 + zb0001, bts, err = msgp.ReadUint8Bytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + (*z) = LateCredentialTrackingEffect(zb0001) + } + o = bts + return +} + +func (z *LateCredentialTrackingEffect) UnmarshalMsg(bts []byte) (o []byte, err error) { + return z.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState) +} +func (_ *LateCredentialTrackingEffect) CanUnmarshalMsg(z interface{}) bool { + _, ok := (z).(*LateCredentialTrackingEffect) + return ok +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z LateCredentialTrackingEffect) Msgsize() (s int) { + s = msgp.Uint8Size + return +} + +// MsgIsZero returns whether this is a zero value +func (z LateCredentialTrackingEffect) MsgIsZero() bool { + return z == 0 +} + +// MaxSize returns a maximum valid message size for this message type +func LateCredentialTrackingEffectMaxSize() (s int) { + s = msgp.Uint8Size + return +} + // MarshalMsg implements msgp.Marshaler func (z TimeoutType) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) @@ -1195,11 +1265,11 @@ func (_ TimeoutType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TimeoutType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 int8 zb0001, bts, err = msgp.ReadInt8Bytes(bts) @@ -1255,11 +1325,11 @@ func (_ actionType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *actionType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint8 zb0001, bts, err = msgp.ReadUint8Bytes(bts) @@ -1334,11 +1404,11 @@ func (_ *blockAssembler) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *blockAssembler) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -1579,11 +1649,11 @@ func (_ *bundle) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *bundle) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -1814,11 +1884,11 @@ func (_ *compoundMessage) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *compoundMessage) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1962,11 +2032,11 @@ func (_ *diskState) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *diskState) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -2284,11 +2354,11 @@ func (_ *equivocationVote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *equivocationVote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -2572,11 +2642,11 @@ func (_ *equivocationVoteAuthenticator) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *equivocationVoteAuthenticator) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -2782,11 +2852,11 @@ func (_ eventType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *eventType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint8 zb0001, bts, err = msgp.ReadUint8Bytes(bts) @@ -2851,11 +2921,11 @@ func (_ *freshnessData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *freshnessData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3067,11 +3137,11 @@ func (_ *message) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *message) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3443,11 +3513,11 @@ func (_ *messageEvent) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *messageEvent) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3714,11 +3784,11 @@ func (_ *nextThresholdStatusEvent) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *nextThresholdStatusEvent) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3836,11 +3906,11 @@ func (_ period) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *period) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -3924,11 +3994,11 @@ func (_ *periodRouter) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *periodRouter) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -4144,7 +4214,7 @@ func (z *player) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values zb0001Len := uint32(9) - var zb0001Mask uint16 /* 10 bits */ + var zb0001Mask uint16 /* 12 bits */ if (*z).OldDeadline == 0 { zb0001Len-- zb0001Mask |= 0x1 @@ -4192,11 +4262,11 @@ func (_ *player) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *player) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -4722,11 +4792,11 @@ func (_ *proposal) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -5370,11 +5440,11 @@ func (_ *proposalManager) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalManager) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -5470,11 +5540,11 @@ func (_ *proposalSeeker) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalSeeker) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -5642,11 +5712,11 @@ func (_ *proposalStore) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalStore) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0005 int @@ -5930,11 +6000,11 @@ func (_ *proposalTable) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalTable) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -6157,11 +6227,11 @@ func (_ *proposalTracker) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalTracker) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -6359,11 +6429,11 @@ func (_ *proposalTrackerContract) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalTrackerContract) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -6548,11 +6618,11 @@ func (_ *proposalValue) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalValue) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -6724,11 +6794,11 @@ func (_ *proposalVoteCounter) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposalVoteCounter) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -6904,11 +6974,11 @@ func (_ *proposerSeed) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *proposerSeed) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -7074,11 +7144,11 @@ func (_ *rawVote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *rawVote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -7281,11 +7351,11 @@ func (_ *rootRouter) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *rootRouter) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -7674,11 +7744,11 @@ func (_ *roundRouter) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *roundRouter) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -8023,11 +8093,11 @@ func (_ *seedInput) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *seedInput) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -8154,11 +8224,11 @@ func (_ *selector) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *selector) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -8320,11 +8390,11 @@ func (_ serializableError) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *serializableError) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -8380,11 +8450,11 @@ func (_ step) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *step) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -8443,11 +8513,11 @@ func (_ *stepRouter) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *stepRouter) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -8583,11 +8653,11 @@ func (_ *thresholdEvent) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *thresholdEvent) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -9094,11 +9164,11 @@ func (_ *transmittedPayload) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -9829,11 +9899,11 @@ func (_ *unauthenticatedBundle) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *unauthenticatedBundle) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -10191,11 +10261,11 @@ func (_ *unauthenticatedEquivocationVote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *unauthenticatedEquivocationVote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -10745,11 +10815,11 @@ func (_ *unauthenticatedProposal) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -11425,11 +11495,11 @@ func (_ *unauthenticatedVote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *unauthenticatedVote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -11549,7 +11619,7 @@ func (z *vote) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values zb0001Len := uint32(3) - var zb0001Mask uint8 /* 4 bits */ + var zb0001Mask uint8 /* 5 bits */ if (*z).Cred.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x2 @@ -11591,11 +11661,11 @@ func (_ *vote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *vote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -11725,11 +11795,11 @@ func (_ *voteAggregator) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteAggregator) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -11837,11 +11907,11 @@ func (_ *voteAuthenticator) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteAuthenticator) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -12027,11 +12097,11 @@ func (_ *voteTracker) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteTracker) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0007 int @@ -12354,11 +12424,11 @@ func (_ *voteTrackerContract) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteTrackerContract) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -12504,11 +12574,11 @@ func (_ *voteTrackerPeriod) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteTrackerPeriod) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -12743,11 +12813,11 @@ func (_ *voteTrackerRound) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *voteTrackerRound) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/agreement/persistence.go b/agreement/persistence.go index 0e5e75915a..b8510e446c 100644 --- a/agreement/persistence.go +++ b/agreement/persistence.go @@ -55,6 +55,21 @@ func persistent(as []action) bool { // encode serializes the current state into a byte array. func encode(t timers.Clock[TimeoutType], rr rootRouter, p player, a []action, reflect bool) (raw []byte) { var s diskState + + // Don't persist state for old rounds + // rootRouter.update() may preserve roundRouters from credentialRoundLag rounds ago + children := make(map[round]*roundRouter) + for rnd, rndRouter := range rr.Children { + if rnd >= p.Round { + children[rnd] = rndRouter + } + } + if len(children) == 0 { + rr.Children = nil + } else { + rr.Children = children + } + if reflect { s.Router = protocol.EncodeReflect(rr) s.Player = protocol.EncodeReflect(p) @@ -228,7 +243,7 @@ func decode(raw []byte, t0 timers.Clock[TimeoutType], log serviceLogger, reflect if err != nil { return } - + p2.lowestCredentialArrivals = makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) rr2 = makeRootRouter(p2) err = protocol.DecodeReflect(s.Router, &rr2) if err != nil { @@ -244,6 +259,7 @@ func decode(raw []byte, t0 timers.Clock[TimeoutType], log serviceLogger, reflect return } } + p2.lowestCredentialArrivals = makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) if p2.OldDeadline != 0 { p2.Deadline = Deadline{Duration: p2.OldDeadline, Type: TimeoutDeadline} p2.OldDeadline = 0 // clear old value diff --git a/agreement/persistence_test.go b/agreement/persistence_test.go index ef738e86d6..a3cc99755d 100644 --- a/agreement/persistence_test.go +++ b/agreement/persistence_test.go @@ -38,7 +38,7 @@ func TestAgreementSerialization(t *testing.T) { // todo : we need to deserialize some more meaningful state. clock := timers.MakeMonotonicClock[TimeoutType](time.Date(2015, 1, 2, 5, 6, 7, 8, time.UTC)) - status := player{Round: 350, Step: soft, Deadline: Deadline{Duration: time.Duration(23) * time.Second, Type: TimeoutDeadline}} + status := player{Round: 350, Step: soft, Deadline: Deadline{Duration: time.Duration(23) * time.Second, Type: TimeoutDeadline}, lowestCredentialArrivals: makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory)} router := makeRootRouter(status) a := []action{checkpointAction{}, disconnectAction(messageEvent{}, nil)} @@ -218,6 +218,27 @@ func TestRandomizedEncodingFullDiskState(t *testing.T) { } +func TestCredentialHistoryAllocated(t *testing.T) { + partitiontest.PartitionTest(t) + router, player := randomizeDiskState() + a := []action{} + clock := timers.MakeMonotonicClock[TimeoutType](time.Date(2015, 1, 2, 5, 6, 7, 8, time.UTC)) + log := makeServiceLogger(logging.Base()) + e1 := encode(clock, router, player, a, true) + e2 := encode(clock, router, player, a, false) + require.Equalf(t, e1, e2, "msgp and go-codec encodings differ: len(msgp)=%v, len(reflect)=%v", len(e1), len(e2)) + _, _, p1, _, err1 := decode(e1, clock, log, true) + _, _, p2, _, err2 := decode(e1, clock, log, false) + require.NoErrorf(t, err1, "reflect decoding failed") + require.NoErrorf(t, err2, "msgp decoding failed") + + require.Len(t, p1.lowestCredentialArrivals.history, dynamicFilterCredentialArrivalHistory) + require.Len(t, p2.lowestCredentialArrivals.history, dynamicFilterCredentialArrivalHistory) + emptyHistory := makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) + require.Equalf(t, p1.lowestCredentialArrivals, emptyHistory, "credential arrival history isn't empty") + require.Equalf(t, p2.lowestCredentialArrivals, emptyHistory, "credential arrival history isn't empty") +} + func BenchmarkRandomizedEncode(b *testing.B) { clock := timers.MakeMonotonicClock[TimeoutType](time.Date(2015, 1, 2, 5, 6, 7, 8, time.UTC)) router, player := randomizeDiskState() diff --git a/agreement/player.go b/agreement/player.go index a3451ea0db..21d65ecf88 100644 --- a/agreement/player.go +++ b/agreement/player.go @@ -58,6 +58,15 @@ type player struct { // Pending holds the player's proposalTable, which stores proposals that // must be verified after some vote has been verified. Pending proposalTable + + // the history of arrival times of the lowest credential from previous + // ronuds, used for calculating the filter timeout dynamically. + lowestCredentialArrivals credentialArrivalHistory + + // The period 0 dynamic filter timeout calculated for this round, if set, + // even if dynamic filter timeouts are not enabled. It is used for reporting + // to telemetry. + dynamicFilterTimeout time.Duration } func (p *player) T() stateMachineTag { @@ -266,6 +275,74 @@ func (p *player) handleCheckpointEvent(r routerHandle, e checkpointEvent) []acti }} } +// updateCredentialArrivalHistory is called at the end of a successful +// uninterrupted round (just after ensureAction is generated) to collect +// credential arrival times to dynamically set the filter timeout. +// It returns the time of the lowest credential's arrival from +// credentialRoundLag rounds ago, if one was collected and added to +// lowestCredentialArrivals, or zero otherwise. +func (p *player) updateCredentialArrivalHistory(r routerHandle, ver protocol.ConsensusVersion) time.Duration { + if p.Period != 0 { + // only append to lowestCredentialArrivals if this was a successful round completing in period 0. + return 0 + } + + if p.Round <= credentialRoundLag { + // not sufficiently many rounds had passed to collect any measurement + return 0 + } + + // look up the validatedAt time of the winning proposal-vote from credentialRoundLag ago, + // by now we should have seen the lowest credential for that round. + credHistoryRound := p.Round - credentialRoundLag + re := readLowestEvent{T: readLowestVote, Round: credHistoryRound, Period: 0} + re = r.dispatch(*p, re, proposalMachineRound, credHistoryRound, 0, 0).(readLowestEvent) + if !re.HasLowestIncludingLate { + return 0 + } + + p.lowestCredentialArrivals.store(re.LowestIncludingLate.validatedAt) + return re.LowestIncludingLate.validatedAt +} + +// calculateFilterTimeout chooses the appropriate filter timeout. +func (p *player) calculateFilterTimeout(ver protocol.ConsensusVersion, tracer *tracer) time.Duration { + proto := config.Consensus[ver] + if dynamicFilterCredentialArrivalHistory <= 0 || p.Period != 0 { + // Either dynamic filter timeout is disabled, or we're not in period 0 + // and therefore, can't use dynamic timeout + return FilterTimeout(p.Period, ver) + } + defaultTimeout := FilterTimeout(0, ver) + if !p.lowestCredentialArrivals.isFull() { + // not enough samples, use the default + return defaultTimeout + } + + dynamicTimeout := p.lowestCredentialArrivals.orderStatistics(dynamicFilterTimeoutCredentialArrivalHistoryIdx) + dynamicFilterTimeoutGraceInterval + + // Make sure the dynamic filter timeout is not too small nor too large + clampedTimeout := dynamicTimeout + if clampedTimeout < dynamicFilterTimeoutLowerBound { + clampedTimeout = dynamicFilterTimeoutLowerBound + } + if clampedTimeout > defaultTimeout { + clampedTimeout = defaultTimeout + } + tracer.log.Debugf("round %d, period %d: dynamicTimeout = %d, clamped timeout = %d", p.Round, p.Period, dynamicTimeout, clampedTimeout) + // store dynamicFilterTimeout on the player for debugging & reporting + p.dynamicFilterTimeout = dynamicTimeout + + if !proto.DynamicFilterTimeout { + // If the dynamic filter timeout is disabled, return the default filter + // timeout (after logging what the timeout would have been, if this + // feature were enabled). + return defaultTimeout + } + + return clampedTimeout +} + func (p *player) handleThresholdEvent(r routerHandle, e thresholdEvent) []action { r.t.timeR().RecThreshold(e) @@ -280,6 +357,8 @@ func (p *player) handleThresholdEvent(r routerHandle, e thresholdEvent) []action if res.Committable { cert := Certificate(e.Bundle) a0 := ensureAction{Payload: res.Payload, Certificate: cert} + a0.voteValidatedAt = p.updateCredentialArrivalHistory(r, e.Proto) + a0.dynamicFilterTimeout = p.dynamicFilterTimeout actions = append(actions, a0) as := p.enterRound(r, e, p.Round+1) return append(actions, as...) @@ -333,7 +412,13 @@ func (p *player) enterPeriod(r routerHandle, source thresholdEvent, target perio p.Step = soft p.Napping = false p.FastRecoveryDeadline = 0 // set immediately - p.Deadline = Deadline{Duration: FilterTimeout(target, source.Proto), Type: TimeoutFilter} + + if target != 0 { + // We entered a non-0 period, we should reset the filter timeout + // calculation mechanism. + p.lowestCredentialArrivals.reset() + } + p.Deadline = Deadline{Duration: p.calculateFilterTimeout(source.Proto, r.t), Type: TimeoutFilter} // update tracer state to match player r.t.setMetadata(tracerMetadata{p.Round, p.Period, p.Step}) @@ -381,11 +466,11 @@ func (p *player) enterRound(r routerHandle, source event, target round) []action switch source := source.(type) { case roundInterruptionEvent: - p.Deadline = Deadline{Duration: FilterTimeout(0, source.Proto.Version), Type: TimeoutFilter} + p.Deadline = Deadline{Duration: p.calculateFilterTimeout(source.Proto.Version, r.t), Type: TimeoutFilter} case thresholdEvent: - p.Deadline = Deadline{Duration: FilterTimeout(0, source.Proto), Type: TimeoutFilter} + p.Deadline = Deadline{Duration: p.calculateFilterTimeout(source.Proto, r.t), Type: TimeoutFilter} case filterableMessageEvent: - p.Deadline = Deadline{Duration: FilterTimeout(0, source.Proto.Version), Type: TimeoutFilter} + p.Deadline = Deadline{Duration: p.calculateFilterTimeout(source.Proto.Version, r.t), Type: TimeoutFilter} } // update tracer state to match player @@ -527,8 +612,31 @@ func (p *player) handleMessageEvent(r routerHandle, e messageEvent) (actions []a err := ef.(filteredEvent).Err return append(actions, disconnectAction(e, err)) case voteFiltered: - err := ef.(filteredEvent).Err - return append(actions, ignoreAction(e, err)) + ver := e.Proto.Version + proto := config.Consensus[ver] + if !proto.DynamicFilterTimeout { + // Dynamic filter timeout feature disabled, so we filter the + // message as usual (keeping earlier behavior) + err := ef.(filteredEvent).Err + return append(actions, ignoreAction(e, err)) + } + switch ef.(filteredEvent).LateCredentialTrackingNote { + case VerifiedBetterLateCredentialForTracking: + // Dynamic filter timeout feature enabled, and current message + // updated the best credential arrival time + v := e.Input.Vote + return append(actions, relayAction(e, protocol.AgreementVoteTag, v.u())) + case NoLateCredentialTrackingImpact: + // Dynamic filter timeout feature enabled, but current message + // may not update the best credential arrival time, so we should + // ignore it. + err := ef.(filteredEvent).Err + return append(actions, ignoreAction(e, err)) + case UnverifiedLateCredentialForTracking: + // In this case, the vote may impact credential tracking, but needs to + // be validated. So we do not return here, and continue processing, so that + // the votePresent check below will make a verifyVoteAction for this vote. + } } if e.t() == votePresent { @@ -599,6 +707,8 @@ func (p *player) handleMessageEvent(r routerHandle, e messageEvent) (actions []a if freshestRes.Ok && freshestRes.Event.t() == certThreshold && freshestRes.Event.Proposal == e.Input.Proposal.value() { cert := Certificate(freshestRes.Event.Bundle) a0 := ensureAction{Payload: e.Input.Proposal, Certificate: cert} + a0.voteValidatedAt = p.updateCredentialArrivalHistory(r, e.Proto.Version) + a0.dynamicFilterTimeout = p.dynamicFilterTimeout actions = append(actions, a0) as := p.enterRound(r, delegatedE, cert.Round+1) return append(actions, as...) diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index 0335093933..bd2c2c84da 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -264,6 +264,7 @@ func getMessageEventPermutation(t *testing.T, n int, helper *voteMakerHelper) (e bun := unauthenticatedBundle{ Round: r, Period: p, + Step: cert, Proposal: pV, } e = messageEvent{ @@ -339,6 +340,24 @@ func expectIgnore(t *testing.T, trace ioTrace, errMsg string, playerN int, event }), errMsg, playerN, eventN) } +func expectRelay(t *testing.T, trace ioTrace, errMsg string, playerN int, eventN int) { + require.Truef(t, trace.ContainsFn(func(b event) bool { + if b.t() != wrappedAction { + return false + } + + wrapper := b.(wrappedActionEvent) + if wrapper.action.t() != relay { + return false + } + act := wrapper.action.(networkAction) + if act.T == relay && act.Err == nil { + return true + } + return false + }), errMsg, playerN, eventN) +} + func expectDisconnect(t *testing.T, trace ioTrace, errMsg string, playerN int, eventN int) { require.Truef(t, trace.ContainsFn(func(b event) bool { if b.t() != wrappedAction { @@ -356,15 +375,37 @@ func expectDisconnect(t *testing.T, trace ioTrace, errMsg string, playerN int, e }), errMsg, playerN, eventN) } +func expectVerify(t *testing.T, trace ioTrace, errMsg string, playerN int, eventN int) { + require.Truef(t, trace.ContainsFn(func(b event) bool { + if b.t() != wrappedAction { + return false + } + + wrapper := b.(wrappedActionEvent) + if wrapper.action.t() != verifyVote { + return false + } + act := wrapper.action.(cryptoAction) + if act.T == verifyVote { + return true + } + return false + }), errMsg, playerN, eventN) +} + func requireActionCount(t *testing.T, trace ioTrace, expectedCount, playerN, eventN int) { require.Equalf(t, trace.countAction(), expectedCount, "Player should not emit extra actions, player: %v, event: %v", playerN, eventN) } func requireTraceContains(t *testing.T, trace ioTrace, expected event, playerN, eventN int) { + if !trace.Contains(expected) { + t.Log("expected:", expected.ComparableStr()) + t.Log("trace:", trace.String()) + } require.Truef(t, trace.Contains(expected), "Player should emit action, player: %v, event: %v", playerN, eventN) } -func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, helper *voteMakerHelper, trace ioTrace) { +func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, helper *voteMakerHelper, trace ioTrace, dynamicFilterTimeoutEnabled bool) { const r = round(209) const p = period(0) var payload = makeRandomProposalPayload(r) @@ -380,7 +421,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -395,7 +436,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case proposeVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, propose, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 1) requireTraceContains(t, trace, ev(a), playerN, eventN) case payloadPresentEvent, payloadVerifiedEvent, payloadVerifiedEventNoMessageHandle: requireActionCount(t, trace, 1, playerN, eventN) @@ -417,8 +458,8 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r, p, cert, pV) } - bun := unauthenticatedBundle{Round: r, Period: p, Proposal: pV} - ca := cryptoAction{T: verifyBundle, M: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}, TaskIndex: 0} + bun := unauthenticatedBundle{Round: r, Period: p, Step: cert, Proposal: pV} + ca := verifyBundleAction(messageEvent{Input: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}}, r, p, cert) requireTraceContains(t, trace, ev(ca), playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: requireActionCount(t, trace, 1, playerN, eventN) @@ -431,7 +472,25 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel } case playerNextRound: switch eventN { - case softVoteVerifiedEventSamePeriod, softVotePresentEventSamePeriod, proposeVoteVerifiedEventNextPeriod, proposeVoteVerifiedEventSamePeriod, proposeVotePresentEventSamePeriod, payloadPresentEvent, payloadVerifiedEvent, payloadVerifiedEventNoMessageHandle, bundleVerifiedEventSamePeriod, bundlePresentEventSamePeriod: + case proposeVoteVerifiedEventSamePeriod: + requireActionCount(t, trace, 1, playerN, eventN) + // This case should never happen -- player is on R+1 and the voteVerified event is for R. + // Player will not queue up a verifyVoteAction for this vote (without DynamicFilterTimeout enabled). + // We are asserting the relay behavior player currently implements, but it is not possible given current + // code -- you would have filtered the votePresent for this vote. + if dynamicFilterTimeoutEnabled && p == 0 { + expectRelay(t, trace, "Player should relay period 0 msg from past rounds, player: %v, event: %v", playerN, eventN) + } else { + expectIgnore(t, trace, "Player should ignore msg from past rounds, player: %v, event: %v", playerN, eventN) + } + case proposeVotePresentEventSamePeriod: + requireActionCount(t, trace, 1, playerN, eventN) + if dynamicFilterTimeoutEnabled && p == 0 { + expectVerify(t, trace, "Player should verify period 0 msg from past rounds, player: %v, event: %v", playerN, eventN) + } else { + expectIgnore(t, trace, "Player should ignore msg from past rounds, player: %v, event: %v", playerN, eventN) + } + case softVoteVerifiedEventSamePeriod, softVotePresentEventSamePeriod, proposeVoteVerifiedEventNextPeriod, payloadPresentEvent, payloadVerifiedEvent, payloadVerifiedEventNoMessageHandle, bundleVerifiedEventSamePeriod, bundlePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) expectIgnore(t, trace, "Player should ignore msg from past rounds, player: %v, event: %v", playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: @@ -453,7 +512,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -468,7 +527,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case proposeVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, propose, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0}, r, p, 2) requireTraceContains(t, trace, ev(a), playerN, eventN) case payloadPresentEvent, payloadVerifiedEvent, payloadVerifiedEventNoMessageHandle: requireActionCount(t, trace, 1, playerN, eventN) @@ -495,7 +554,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -507,7 +566,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel expectIgnore(t, trace, "Player should ignore proposalvvote already received: %v, event: %v", playerN, eventN) case payloadPresentEvent: requireActionCount(t, trace, 2, playerN, eventN) - ca := cryptoAction{T: verifyPayload, M: message{UnauthenticatedProposal: payload.u()}, TaskIndex: 0} + ca := verifyPayloadAction(messageEvent{Input: message{UnauthenticatedProposal: payload.u()}}, r, p, false) requireTraceContains(t, trace, ev(ca), playerN, eventN) na := networkAction{T: relay, Tag: protocol.ProposalPayloadTag, CompoundMessage: compoundMessage{Proposal: payload.u()}} requireTraceContains(t, trace, ev(na), playerN, eventN) @@ -534,8 +593,8 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r, p, cert, pV) } - bun := unauthenticatedBundle{Round: r, Period: p, Proposal: pV} - ca := cryptoAction{T: verifyBundle, M: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}, TaskIndex: 0} + bun := unauthenticatedBundle{Round: r, Period: p, Step: cert, Proposal: pV} + ca := verifyBundleAction(messageEvent{Input: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}}, r, p, cert) requireTraceContains(t, trace, ev(ca), playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: requireActionCount(t, trace, 1, playerN, eventN) @@ -557,7 +616,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -569,7 +628,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel expectIgnore(t, trace, "Player should ignore proposalvvote already received: %v, event: %v", playerN, eventN) case payloadPresentEvent: requireActionCount(t, trace, 2, playerN, eventN) - ca := cryptoAction{T: verifyPayload, M: message{UnauthenticatedProposal: payload.u()}, TaskIndex: 0} + ca := verifyPayloadAction(messageEvent{Input: message{UnauthenticatedProposal: payload.u()}}, r, p, false) requireTraceContains(t, trace, ev(ca), playerN, eventN) na := networkAction{T: relay, Tag: protocol.ProposalPayloadTag, CompoundMessage: compoundMessage{Proposal: payload.u()}} requireTraceContains(t, trace, ev(na), playerN, eventN) @@ -600,8 +659,8 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r, p, cert, pV) } - bun := unauthenticatedBundle{Round: r, Period: p, Proposal: pV} - ca := cryptoAction{T: verifyBundle, M: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}, TaskIndex: 0} + bun := unauthenticatedBundle{Round: r, Period: p, Step: cert, Proposal: pV} + ca := verifyBundleAction(messageEvent{Input: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}}, r, p, cert) requireTraceContains(t, trace, ev(ca), playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: requireActionCount(t, trace, 1, playerN, eventN) @@ -623,7 +682,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -635,7 +694,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel expectIgnore(t, trace, "Player should ignore proposalvvote already received: %v, event: %v", playerN, eventN) case payloadPresentEvent: requireActionCount(t, trace, 2, playerN, eventN) - ca := cryptoAction{T: verifyPayload, M: message{UnauthenticatedProposal: payload.u()}, TaskIndex: 0} + ca := verifyPayloadAction(messageEvent{Input: message{UnauthenticatedProposal: payload.u()}}, r, p, false) requireTraceContains(t, trace, ev(ca), playerN, eventN) na := networkAction{T: relay, Tag: protocol.ProposalPayloadTag, CompoundMessage: compoundMessage{Proposal: payload.u()}} requireTraceContains(t, trace, ev(na), playerN, eventN) @@ -666,8 +725,8 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r, p, cert, pV) } - bun := unauthenticatedBundle{Round: r, Period: p, Proposal: pV} - ca := cryptoAction{T: verifyBundle, M: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}, TaskIndex: 0} + bun := unauthenticatedBundle{Round: r, Period: p, Step: cert, Proposal: pV} + ca := verifyBundleAction(messageEvent{Input: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}}, r, p, cert) requireTraceContains(t, trace, ev(ca), playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: requireActionCount(t, trace, 1, playerN, eventN) @@ -689,7 +748,7 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel case softVotePresentEventSamePeriod: requireActionCount(t, trace, 1, playerN, eventN) vvote := helper.MakeVerifiedVote(t, 0, r, p, soft, pV) - a := cryptoAction{T: verifyVote, M: message{UnauthenticatedVote: vvote.u()}, TaskIndex: 0} + a := verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vvote.u()}}, r, p, 0) requireTraceContains(t, trace, ev(a), playerN, eventN) case proposeVoteVerifiedEventNextPeriod: requireActionCount(t, trace, 1, playerN, eventN) @@ -723,8 +782,8 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r, p, cert, pV) } - bun := unauthenticatedBundle{Round: r, Period: p, Proposal: pV} - ca := cryptoAction{T: verifyBundle, M: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}, TaskIndex: 0} + bun := unauthenticatedBundle{Round: r, Period: p, Step: cert, Proposal: pV} + ca := verifyBundleAction(messageEvent{Input: message{Bundle: bundle{U: bun, Votes: votes}, UnauthenticatedBundle: bun}}, r, p, cert) requireTraceContains(t, trace, ev(ca), playerN, eventN) case softVoteVerifiedErrorEventSamePeriod, proposeVoteVerifiedErrorEventSamePeriod, bundleVerifiedErrorEvent: requireActionCount(t, trace, 1, playerN, eventN) @@ -745,18 +804,31 @@ func verifyPermutationExpectedActions(t *testing.T, playerN int, eventN int, hel // Generates a set of player states, router states, and messageEvents and tests all permutations of them func TestPlayerPermutation(t *testing.T) { partitiontest.PartitionTest(t) + // check with current consensus params and with consensus params that + // explicitly enable dynamic filter timeout + playerPermutationCheck(t, false) + playerPermutationCheck(t, true) +} + +func playerPermutationCheck(t *testing.T, enableDynamicFilterTimeout bool) { + // create a protocol version where dynamic filter is enabled + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() for i := 0; i < 7; i++ { for j := 0; j < 14; j++ { _, pMachine, helper := getPlayerPermutation(t, i) inMsg := getMessageEventPermutation(t, j, helper) + if enableDynamicFilterTimeout { + inMsg.Proto = ConsensusVersionView{Version: version} + } err, panicErr := pMachine.transition(inMsg) fmt.Println(pMachine.getTrace().events) fmt.Println("") require.NoErrorf(t, err, "player: %v, event: %v", i, j) require.NoErrorf(t, panicErr, "player: %v, event: %v", i, j) - verifyPermutationExpectedActions(t, i, j, helper, pMachine.getTrace()) + verifyPermutationExpectedActions(t, i, j, helper, pMachine.getTrace(), enableDynamicFilterTimeout) } } } diff --git a/agreement/player_test.go b/agreement/player_test.go index 84aad574ba..2bb2ffe818 100644 --- a/agreement/player_test.go +++ b/agreement/player_test.go @@ -416,7 +416,8 @@ func testPlayerSetup() (player, rootRouter, testAccountData, testBlockFactory, L accs := testAccountData{addresses: addresses, vrfs: vrfSecrets, ots: otSecrets} round := ledger.NextRound() period := period(0) - player := player{Round: round, Period: period, Step: soft} + historyBuffer := makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) + player := player{Round: round, Period: period, Step: soft, lowestCredentialArrivals: historyBuffer} var p actor = ioLoggedActor{checkedActor{actor: &player, actorContract: playerContract{}}, playerTracer} router := routerFixture @@ -500,9 +501,11 @@ func TestPlayerLateBlockProposalPeriod0(t *testing.T) { func setupP(t *testing.T, r round, p period, s step) (plyr *player, pMachine ioAutomata, helper *voteMakerHelper) { // Set up a composed test machine starting at specified rps - rRouter := makeRootRouter(player{Round: r, Period: p, Step: s, Deadline: Deadline{Duration: FilterTimeout(p, protocol.ConsensusCurrentVersion), Type: TimeoutFilter}}) + history := makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) + rRouter := makeRootRouter(player{Round: r, Period: p, Step: s, Deadline: Deadline{Duration: FilterTimeout(p, protocol.ConsensusCurrentVersion), Type: TimeoutFilter}, lowestCredentialArrivals: history}) concreteMachine := ioAutomataConcretePlayer{rootRouter: &rRouter} plyr = concreteMachine.underlying() + plyr.lowestCredentialArrivals = makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory) pMachine = &concreteMachine helper = &voteMakerHelper{} helper.Setup() @@ -1616,7 +1619,7 @@ func TestPlayerReproposesNextValueBundleWithoutPayload(t *testing.T) { // check player fast-forwarded, rezeros, reproposed, relays next-value bundle require.Equalf(t, p, pWhite.Period, "player did not fast forward to new period") - zeroEvent := ev(rezeroAction{}) + zeroEvent := ev(rezeroAction{Round: r}) require.Truef(t, pM.getTrace().Contains(zeroEvent), "Player should reset clock") reproposeEvent := ev(pseudonodeAction{T: repropose, Round: r, Period: p, Proposal: *pV}) require.Truef(t, pM.getTrace().Contains(reproposeEvent), "Player should repropose from next-value quorum") @@ -1686,7 +1689,7 @@ func TestPlayerReproposesNextValueBundleRelaysPayload(t *testing.T) { // check player fast-forwarded, rezeros, reproposed, relays next-value bundle require.Equalf(t, p, pWhite.Period, "player did not fast forward to new period") - zeroEvent := ev(rezeroAction{}) + zeroEvent := ev(rezeroAction{Round: r}) require.Truef(t, pM.getTrace().Contains(zeroEvent), "Player should reset clock") reproposeEvent := ev(pseudonodeAction{T: repropose, Round: r, Period: p, Proposal: *pV}) require.Truef(t, pM.getTrace().Contains(reproposeEvent), "Player should repropose from next-value quorum") @@ -2160,7 +2163,6 @@ func TestPlayerRePropagatesProposalPayload(t *testing.T) { require.NoError(t, err) require.NoError(t, panicErr) relayPayloadEvent = ev(networkAction{T: broadcast, Tag: protocol.ProposalPayloadTag, CompoundMessage: compoundMessage{Proposal: payloadNext.u()}}) - fmt.Println(relayPayloadEvent) require.Truef(t, pM.getTrace().Contains(relayPayloadEvent), "Player should relay staged payload over pinned payload on resynch") } @@ -2415,7 +2417,7 @@ func TestPlayerRequestsVoteVerification(t *testing.T) { require.NoError(t, err) require.NoError(t, panicErr) - verifyEvent := ev(cryptoAction{T: verifyVote, M: m, TaskIndex: 0}) + verifyEvent := ev(cryptoAction{T: verifyVote, M: m, Round: r, Period: p, TaskIndex: 0}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") } @@ -2438,7 +2440,7 @@ func TestPlayerRequestsProposalVoteVerification(t *testing.T) { require.NoError(t, err) require.NoError(t, panicErr) - verifyEvent := ev(cryptoAction{T: verifyVote, M: m, TaskIndex: 0}) + verifyEvent := ev(cryptoAction{T: verifyVote, M: m, Round: r, Period: p, TaskIndex: 1}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") } @@ -2462,7 +2464,7 @@ func TestPlayerRequestsBundleVerification(t *testing.T) { err, panicErr := pM.transition(inMsg) require.NoError(t, err) require.NoError(t, panicErr) - verifyEvent := ev(cryptoAction{T: verifyBundle, M: m, TaskIndex: 0}) + verifyEvent := ev(cryptoAction{T: verifyBundle, M: m, Round: r, Period: p, TaskIndex: 0}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify bundle") } @@ -2499,7 +2501,7 @@ func TestPlayerRequestsPayloadVerification(t *testing.T) { require.NoError(t, panicErr) // make sure payload verify request - verifyEvent := ev(cryptoAction{T: verifyPayload, M: m, TaskIndex: 0}) + verifyEvent := ev(cryptoAction{T: verifyPayload, M: m, Round: r, Period: p, TaskIndex: 0}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify payload") } @@ -2535,8 +2537,7 @@ func TestPlayerRequestsPipelinedPayloadVerification(t *testing.T) { require.NoError(t, err) require.NoError(t, panicErr) // make sure no payload verify request, because its for the next round - verifyEvent := ev(cryptoAction{T: verifyPayload, M: m, TaskIndex: 0}) - require.Falsef(t, pM.getTrace().Contains(verifyEvent), "Player should not verify payload from r + 1") + require.Falsef(t, pM.getTrace().ContainsString(verifyPayload.String()), "Player should not verify payload from r + 1") // now enter next round pP, pV := helper.MakeRandomProposalPayload(t, r) @@ -2594,7 +2595,7 @@ func TestPlayerRequestsPipelinedPayloadVerification(t *testing.T) { require.Truef(t, pM.getTrace().Contains(commitEvent), "Player should try to ensure block/digest on ledger") // make sure we sent out pipelined payload verify requests - verifyEvent = ev(cryptoAction{T: verifyPayload}) + verifyEvent := ev(cryptoAction{T: verifyPayload, Round: r + 1, TaskIndex: 0}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify pipelined payload first seen in previous round") } @@ -2709,7 +2710,7 @@ func TestPlayerHandlesPipelinedThresholds(t *testing.T) { err, panicErr = pM.transition(inMsg) require.NoError(t, err) require.NoError(t, panicErr) - verifyEvent := ev(cryptoAction{T: verifyPayload}) + verifyEvent := ev(cryptoAction{T: verifyPayload, Round: r + 1}) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify pipelined payload first seen in previous round") } @@ -3223,7 +3224,7 @@ func TestPlayerAlwaysResynchsPinnedValue(t *testing.T) { // Now, player should be in period 12, and should have tried to resychronize the pinned payload trace := pM.getTrace() require.Equalf(t, p, pWhite.Period, "player did not fast forward to new period") - zeroEvent := ev(rezeroAction{}) + zeroEvent := ev(rezeroAction{Round: r}) require.Truef(t, trace.Contains(zeroEvent), "Player should reset clock") resynchEvent := ev(networkAction{T: broadcast, Tag: protocol.VoteBundleTag, UnauthenticatedBundle: bun}) @@ -3234,86 +3235,727 @@ func TestPlayerAlwaysResynchsPinnedValue(t *testing.T) { } // test that ReceivedAt and ValidateAt timing information are retained in proposalStore -// when the payloadPresent and payloadVerified events are processed, and that both timings +// when the payloadPresent, payloadVerified, and voteVerified events are processed, and that all timings // are available when the ensureAction is called for the block. -func TestPlayerRetainsReceivedValidatedAt(t *testing.T) { +func TestPlayerRetainsReceivedValidatedAtOneSample(t *testing.T) { partitiontest.PartitionTest(t) + version := protocol.ConsensusFuture const r = round(20239) - const p = period(1001) + const p = period(131) pWhite, pM, helper := setupP(t, r-1, p, soft) pP, pV := helper.MakeRandomProposalPayload(t, r-1) + // send voteVerified message for round r-credentialRoundLag-1, then for r-1 + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 502*time.Millisecond, nil) + // send payloadPresent message for r-1 + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + // advance player to R and check timings ensured for R-1 are correct + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) +} + +// test that ReceivedAt and ValidateAt timing information are retained in proposalStore +// when the payloadPresent, payloadVerified, and voteVerified events are processed, and that all timings +// are available when the ensureAction is called for the block. +func TestPlayerRetainsReceivedValidatedAtCredentialHistory(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-credentialRoundLag-1, p, soft) + + // send voteVerified and payloadPresent messages with timings, and advance through rounds r-credentialRoundLag-1 up to r-1 + voteVerifiedTiming := 501 * time.Millisecond + payloadPresentTiming := 1001 * time.Millisecond + payloadVerifiedTiming := 2001 * time.Millisecond + for rnd := r - credentialRoundLag - 1; rnd < r-1; rnd++ { + pP, pV := helper.MakeRandomProposalPayload(t, rnd) + sendVoteVerified(t, helper, pWhite, pM, 0, rnd, rnd, p, pV, voteVerifiedTiming, nil) + sendPayloadPresent(t, pWhite, pM, rnd, pP, payloadPresentTiming, nil) + moveToRound(t, pWhite, pM, helper, rnd+1, p, pP, pV, payloadVerifiedTiming, version) + + voteVerifiedTiming += time.Millisecond + payloadPresentTiming += time.Millisecond + payloadVerifiedTiming += time.Millisecond + } + + // send in voteVerified and payloadPresent for r-1 + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 600*time.Millisecond, nil) + sendPayloadPresent(t, pWhite, pM, r-1, pP, 1500*time.Millisecond, nil) + // advance player to R and check timings ensured for R-1 are correct + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2500*time.Millisecond, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, 1500*time.Millisecond, 2500*time.Millisecond) + + // player is looking up arrival times from r-roundLag ago so only the 501ms vote will be in lowestCredentialArrivals + assertSingleCredentialArrival(t, pWhite, 501*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the payloadPresent, payloadVerified, and voteVerified +// events are processed in the *preceding round*, and that all timings are +// available when the ensureAction is called for the block. +func TestPlayerRetainsEarlyReceivedValidatedAtOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + // send voteVerified message - vVote := helper.MakeVerifiedVote(t, 0, r-1, p, propose, *pV) - inMsg := messageEvent{T: voteVerified, Input: message{Vote: vVote, UnauthenticatedVote: vVote.u()}} - err, panicErr := pM.transition(inMsg) - require.NoError(t, err) - require.NoError(t, panicErr) + pP, pV := helper.MakeRandomProposalPayload(t, r-credentialRoundLag-1) + sendVoteVerified(t, helper, pWhite, pM, 0, r-credentialRoundLag-2, r-credentialRoundLag-1, p, pV, 401*time.Millisecond, nil) + + // send voteVerified message + pP, pV = helper.MakeRandomProposalPayload(t, r-1) + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) // send payloadPresent message - m := message{UnauthenticatedProposal: pP.u()} - inMsg = messageEvent{T: payloadPresent, Input: m} - inMsg = inMsg.AttachReceivedAt(time.Second) - err, panicErr = pM.transition(inMsg) - require.NoError(t, err) - require.NoError(t, panicErr) + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + // advance player to R and check timings ensured for R-1 are correct + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, pipelinedMessageTimestamp) +} + +func testClockForRound(t *testing.T, pWhite *player, fixedDur time.Duration, currentRound round, historicalClocks map[round]roundStartTimer) func(round) roundStartTimer { + return func(eventRound round) roundStartTimer { + //require.Equal(t, pWhite.Round, currentRound) // TODO make tests more realistic + return clockForRound(currentRound, constantRoundStartTimer(fixedDur), historicalClocks)(eventRound) + } +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the payloadPresent, payloadVerified, and voteVerified +// events are processed credentialRoundLag after the round they belong to, and +// that all timings are available when the ensureAction is called for the block. +func TestPlayerRetainsLateReceivedValidatedAtOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + + historicalClocks := map[round]roundStartTimer{ + r - credentialRoundLag - 1: constantRoundStartTimer(900 * time.Millisecond), + } + + // send voteVerified message + pP, pV := helper.MakeRandomProposalPayload(t, r-credentialRoundLag-1) + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-credentialRoundLag-1, p, pV, 401*time.Millisecond, historicalClocks) + + // send voteVerified message + pP, pV = helper.MakeRandomProposalPayload(t, r-1) + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + // advance player to R and check timings ensured for R-1 are correct + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, 900*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in proposalStore +// when the payloadPresent, payloadVerified, and voteVerified events are processed, and that all timings +// are available when the ensureAction is called for the block. The history should be kept for the last +// DynamicFilterCredentialArrivalHistory rounds. +func TestPlayerRetainsReceivedValidatedAtForHistoryWindow(t *testing.T) { + partitiontest.PartitionTest(t) + testPlayerRetainsReceivedValidatedAtForHistoryWindow(t, false) +} + +func TestPlayerRetainsReceivedValidatedAtForHistoryWindowLateBetter(t *testing.T) { + partitiontest.PartitionTest(t) + testPlayerRetainsReceivedValidatedAtForHistoryWindow(t, true) +} + +func testPlayerRetainsReceivedValidatedAtForHistoryWindow(t *testing.T, addBetterLate bool) { + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + + require.NotZero(t, dynamicFilterCredentialArrivalHistory) + + for i := 0; i < dynamicFilterCredentialArrivalHistory+int(credentialRoundLag); i++ { + // send voteVerified message + pP, pV := helper.MakeRandomProposalPayload(t, r+round(i)-1) + vVote := helper.MakeVerifiedVote(t, 0, r+round(i)-1, p, propose, *pV) + var betterLateVote vote + if addBetterLate { + // set up better late proposal-vote from someone else, so it won't be a errProposalTrackerSenderDup + vVote2 := helper.MakeVerifiedVote(t, 1, r+round(i)-1, p, propose, *pV) + vVote.Cred.VrfOut = crypto.Digest{1} + vVote2.Cred.VrfOut = crypto.Digest{2} + if vVote2.Cred.Less(vVote.Cred) { + betterLateVote = vVote2 + } else { + betterLateVote = vVote + vVote = vVote2 + } + require.True(t, betterLateVote.Cred.Less(vVote.Cred)) + require.False(t, vVote.Cred.Less(betterLateVote.Cred)) + } + inMsg := messageEvent{T: voteVerified, Input: message{Vote: vVote, UnauthenticatedVote: vVote.u()}} + timestamp := 500 + i + inMsg = inMsg.AttachValidatedAt(testClockForRound(t, pWhite, time.Duration(timestamp)*time.Millisecond, r+round(i)-1, nil)) + err, panicErr := pM.transition(inMsg) + require.NoError(t, err) + require.NoError(t, panicErr) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r+round(i)-1, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+round(i), p, pP, pV, 2*time.Second, version) + + // send better late voteVerified message + if addBetterLate { + inMsg = messageEvent{T: voteVerified, Input: message{Vote: betterLateVote, UnauthenticatedVote: betterLateVote.u()}} + timestamp := 600 + i + inMsg = inMsg.AttachValidatedAt(testClockForRound(t, pWhite, time.Duration(timestamp)*time.Millisecond, r+round(i)-1, nil)) + err, panicErr = pM.transition(inMsg) + require.NoError(t, err) + require.NoError(t, panicErr) + } + } - assertCorrectReceivedAtSet(t, pWhite, pM, helper, r, p, pP, pV, m) + // assert lowest vote validateAt time was recorded into payloadArrivals + require.True(t, pWhite.lowestCredentialArrivals.isFull()) + for i := 0; i < dynamicFilterCredentialArrivalHistory; i++ { + // only the last historyLen samples are kept, so the first one is discarded + timestamp := 500 + i + if addBetterLate { + timestamp = 600 + i + } + require.Equal(t, time.Duration(timestamp)*time.Millisecond, pWhite.lowestCredentialArrivals.history[i]) + } } // test that ReceivedAt and ValidateAt timing information are retained in proposalStore -// when the payloadPresent (as part of the CompoundMessage encoding used by PP messages) -// and payloadVerified events are processed, and that both timings +// when the payloadPresent (as part of the CompoundMessage encoding used by PP messages), +// payloadVerified, and voteVerified events are processed, and that all timings // are available when the ensureAction is called for the block. -func TestPlayerRetainsReceivedValidatedAtPP(t *testing.T) { +func TestPlayerRetainsReceivedValidatedAtPPOneSample(t *testing.T) { partitiontest.PartitionTest(t) + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() const r = round(20239) - const p = period(1001) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + // XXX this behavior only happens if dynamic timeout enabled; test the other way + + historicalClocks := map[round]roundStartTimer{ + r - credentialRoundLag: constantRoundStartTimer(900 * time.Millisecond), + } + // create a PP message for the round we're going to take the sample from when round r-1 ends + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendCompoundMessage(t, helper, pWhite, pM, r, r-credentialRoundLag, p, pP, pV, time.Second, nil, version) + + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r, 502*time.Millisecond, historicalClocks, 1) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, 900*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the payloadPresent (as part of the CompoundMessage +// encoding used by PP messages), payloadVerified, and voteVerified events are +// processed one round early, and that all timings are available when the +// ensureAction is called for the block. +func TestPlayerRetainsEarlyReceivedValidatedAtPPOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() + + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + // create a PP message for the round we're going to take the sample from when round r-1 ends + // Now we're going to pretend we got the message one round early. + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendCompoundMessage(t, helper, pWhite, pM, r-credentialRoundLag-1, r-credentialRoundLag, p, pP, pV, time.Second, nil, version) + + // make sure vote verify requests + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r-credentialRoundLag, 502*time.Millisecond, nil, 1) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-credentialRoundLag, pP, time.Second, nil) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, 502*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the payloadPresent (as part of the CompoundMessage +// encoding used by PP messages), payloadVerified, and voteVerified events are +// processed credentialRoundLag after the round they belong to, and that all +// timings are available when the ensureAction is called for the block. +func TestPlayerRetainsLateReceivedValidatedAtPPOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + // Go from round r-1 to r + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + historicalClocks := map[round]roundStartTimer{ + r - credentialRoundLag: constantRoundStartTimer(900 * time.Millisecond), + } + // create a PP message for the round we're going to take the sample from when round r-1 ends + // Now we're going to pretend we got the message credentialRoundLag too late. + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendCompoundMessage(t, helper, pWhite, pM, r, r-credentialRoundLag, p, pP, pV, time.Second, historicalClocks, version) + + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r, 502*time.Millisecond, historicalClocks, 1) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, 503*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, 900*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the payloadPresent (as part of the CompoundMessage +// encoding used by PP messages), payloadVerified, and voteVerified events are +// processed, and that all timings are available when the ensureAction is called +// for the block. The history should be kept for the last +// DynamicFilterCredentialArrivalHistory rounds. +func TestPlayerRetainsReceivedValidatedAtPPForHistoryWindow(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + + require.NotZero(t, dynamicFilterCredentialArrivalHistory) + + for i := 0; i < dynamicFilterCredentialArrivalHistory+int(credentialRoundLag); i++ { + // create a PP message for an arbitrary proposal/payload similar to setupCompoundMessage + pP, pV := helper.MakeRandomProposalPayload(t, r+round(i)-1) + vVote := sendCompoundMessage(t, helper, pWhite, pM, r+round(i)-1, r+round(i)-1, p, pP, pV, time.Second, nil, version) + + // make sure vote verify requests + taskIndex := uint64(i + 1) + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r+round(i)-1, p, taskIndex)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + // send voteVerified + timestamp := 500 + i + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r+round(i)-1, time.Duration(timestamp)*time.Millisecond, nil, taskIndex) + sendPayloadPresent(t, pWhite, pM, r+round(i)-1, pP, time.Duration(timestamp)*time.Millisecond+time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+round(i), p, pP, pV, 2*time.Second+time.Duration(timestamp)*time.Millisecond, version) + } + + // assert lowest vote validateAt time was recorded into payloadArrivals + require.True(t, pWhite.lowestCredentialArrivals.isFull()) + for i := 0; i < dynamicFilterCredentialArrivalHistory; i++ { + // only the last historyLen samples are kept, so the first one is discarded + timestamp := 500 + i + require.Equal(t, time.Duration(timestamp)*time.Millisecond, pWhite.lowestCredentialArrivals.history[i]) + } +} + +// test that ReceivedAt and ValidateAt timing information are retained in proposalStore +// when the voteVerified event comes in first (as part of the AV message before PP), +// then the payloadPresent (as part of the CompoundMessage encoding used by PP messages) +// and payloadVerified events are processed, and that all timings +// are available when the ensureAction is called for the block. +func TestPlayerRetainsReceivedValidatedAtAVPPOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + // create a protocol version where dynamic lambda is enabled + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + // send votePresent message (mimicking the first AV message validating) + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendVotePresent(t, helper, pWhite, pM, 0, r-credentialRoundLag, p, pV, version) + + // make sure vote verify requests + unverifiedVoteMsg := message{UnauthenticatedVote: vVote.u()} + verifyEvent := ev(verifyVoteAction(messageEvent{Input: unverifiedVoteMsg}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + // send voteVerified + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r-credentialRoundLag, 502*time.Millisecond, nil, 1) + + // create a PP message for an arbitrary proposal/payload similar to setupCompoundMessage + sendCompoundMessageForVote(t, vVote, pWhite, pM, r-credentialRoundLag, pP, time.Second, nil, version) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, time.Second, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, 502*time.Millisecond) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the voteVerified event comes in first (as part of the AV +// message before PP), then the payloadPresent (as part of the CompoundMessage +// encoding used by PP messages) and payloadVerified events are processed one +// round early, and that all timings are available when the ensureAction is +// called for the block. +func TestPlayerRetainsEarlyReceivedValidatedAtAVPPOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) pWhite, pM, helper := setupP(t, r-1, p, soft) pP, pV := helper.MakeRandomProposalPayload(t, r-1) + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + // create a protocol version where dynamic filter is enabled + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() + + // send votePresent message (mimicking the first AV message validating) + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendVotePresent(t, helper, pWhite, pM, 0, r-credentialRoundLag, p, pV, version) + + // make sure vote verify requests + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + // send voteVerified, pretend we're one round too early + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r-credentialRoundLag-1, 502*time.Millisecond, nil, 1) + // create a PP message for an arbitrary proposal/payload similar to setupCompoundMessage - vVote := helper.MakeVerifiedVote(t, 0, r-1, p, propose, *pV) - voteMsg := message{Vote: vVote, UnauthenticatedVote: vVote.u()} - proposalMsg := message{UnauthenticatedProposal: pP.u()} - compoundMsg := messageEvent{T: votePresent, Input: voteMsg, - Tail: &messageEvent{T: payloadPresent, Input: proposalMsg}} - inMsg := compoundMsg.AttachReceivedAt(time.Second) // call AttachReceivedAt like demux would + sendCompoundMessageForVote(t, vVote, pWhite, pM, r-credentialRoundLag, pP, time.Second, nil, version) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, time.Second, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into payloadArrivals + assertSingleCredentialArrival(t, pWhite, pipelinedMessageTimestamp) +} + +// test that ReceivedAt and ValidateAt timing information are retained in +// proposalStore when the voteVerified event comes in first (as part of the AV +// message before PP), then the payloadPresent (as part of the CompoundMessage +// encoding used by PP messages) and payloadVerified events are processed +// credentialRoundLag after the round they belong to, and that all timings are +// available when the ensureAction is called for the block. +func TestPlayerRetainsLateReceivedValidatedAtAVPPOneSample(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + pP, pV := helper.MakeRandomProposalPayload(t, r-1) + + // Move to round r, no credentials arrived. + // send voteVerified message + sendVoteVerified(t, helper, pWhite, pM, 0, r-1, r-1, p, pV, 501*time.Millisecond, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r-1, pP, time.Second, nil) + + moveToRound(t, pWhite, pM, helper, r, p, pP, pV, 2*time.Second, version) + assertPayloadTimings(t, pWhite, pM, r-1, pV, time.Second, 2*time.Second) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 0) + + // create a protocol version where dynamic filter is enabled + version, _, configCleanup := createDynamicFilterConfig() + defer configCleanup() + + // send votePresent message (mimicking the first AV message validating) + pP, pV = helper.MakeRandomProposalPayload(t, r-credentialRoundLag) + vVote := sendVotePresent(t, helper, pWhite, pM, 0, r-credentialRoundLag, p, pV, version) + + // make sure vote verify requests + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r-credentialRoundLag, p, 1)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + historicalClocks := map[round]roundStartTimer{ + r - credentialRoundLag: constantRoundStartTimer(900 * time.Millisecond), + } + // send voteVerified, pretend we're credentialRoundLag after the message was sent + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r, 502*time.Millisecond, historicalClocks, 1) + + // create a PP message for an arbitrary proposal/payload similar to setupCompoundMessage + sendCompoundMessageForVote(t, vVote, pWhite, pM, r-credentialRoundLag, pP, time.Second, nil, version) + + // move to round r+1, triggering history update + pP, pV = helper.MakeRandomProposalPayload(t, r) + sendVoteVerified(t, helper, pWhite, pM, 0, r, r, p, pV, time.Second, nil) + + // send payloadPresent message + sendPayloadPresent(t, pWhite, pM, r, pP, time.Second, nil) + moveToRound(t, pWhite, pM, helper, r+1, p, pP, pV, 2*time.Second, version) + + // assert lowest vote validateAt time was recorded into lowestCredentialArrivals + assertSingleCredentialArrival(t, pWhite, 900*time.Millisecond) +} + +func TestPlayerRetainsReceivedValidatedAtAVPPHistoryWindow(t *testing.T) { + partitiontest.PartitionTest(t) + + version := protocol.ConsensusFuture + const r = round(20239) + const p = period(0) + pWhite, pM, helper := setupP(t, r-1, p, soft) + + require.NotZero(t, dynamicFilterCredentialArrivalHistory) + + for i := 0; i < dynamicFilterCredentialArrivalHistory+int(credentialRoundLag); i++ { + pP, pV := helper.MakeRandomProposalPayload(t, r+round(i)-1) + + // send votePresent message (mimicking the first AV message validating) + vVote := sendVotePresent(t, helper, pWhite, pM, 0, r+round(i)-1, p, pV, version) + + // make sure vote verify requests + taskIndex := uint64(i + 1) + verifyEvent := ev(verifyVoteAction(messageEvent{Input: message{UnauthenticatedVote: vVote.u()}}, r+round(i)-1, p, taskIndex)) + require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") + + // send voteVerified + timestamp := 500 + i + sendVoteVerifiedForVote(t, vVote, pWhite, pM, r+round(i)-1, time.Duration(timestamp)*time.Millisecond, nil, taskIndex) + + // create a PP message for an arbitrary proposal/payload similar to setupCompoundMessage + sendCompoundMessageForVote(t, vVote, pWhite, pM, r+round(i)-1, pP, time.Second, nil, version) + + moveToRound(t, pWhite, pM, helper, r+round(i), p, pP, pV, 2*time.Second, version) + } + + // assert lowest vote validateAt time was recorded into payloadArrivals + require.True(t, pWhite.lowestCredentialArrivals.isFull()) + for i := 0; i < dynamicFilterCredentialArrivalHistory; i++ { + // only the last historyLen samples are kept, so the first one is discarded + timestamp := 500 + i + require.Equal(t, time.Duration(timestamp)*time.Millisecond, pWhite.lowestCredentialArrivals.history[i]) + } +} + +// Helper function to send voteVerified message +func sendVoteVerified(t *testing.T, helper *voteMakerHelper, pWhite *player, pM ioAutomata, addrIndex int, + curRound round, voteRound round, votePeriod period, pV *proposalValue, validatedAt time.Duration, + historicalClocks map[round]roundStartTimer) { + vVote := helper.MakeVerifiedVote(t, addrIndex, voteRound, votePeriod, propose, *pV) + sendVoteVerifiedForVote(t, vVote, pWhite, pM, curRound, validatedAt, historicalClocks, 0) +} + +func sendVoteVerifiedForVote(t *testing.T, vVote vote, pWhite *player, pM ioAutomata, + curRound round, validatedAt time.Duration, historicalClocks map[round]roundStartTimer, taskIndex uint64) { + inMsg := messageEvent{T: voteVerified, Input: message{Vote: vVote, UnauthenticatedVote: vVote.u()}, TaskIndex: taskIndex} + inMsg = inMsg.AttachValidatedAt(testClockForRound(t, pWhite, validatedAt, curRound, historicalClocks)) err, panicErr := pM.transition(inMsg) require.NoError(t, err) require.NoError(t, panicErr) +} - // make sure vote verify requests - verifyEvent := ev(cryptoAction{T: verifyVote, M: voteMsg, Round: r - 1, Period: p, Step: propose, TaskIndex: 1}) - require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify vote") +func sendVotePresent(t *testing.T, helper *voteMakerHelper, pWhite *player, pM ioAutomata, addrIndex int, + voteRound round, votePeriod period, pV *proposalValue, version protocol.ConsensusVersion) vote { + vVote := helper.MakeVerifiedVote(t, addrIndex, voteRound, votePeriod, propose, *pV) + inMsg := messageEvent{T: votePresent, Input: message{UnauthenticatedVote: vVote.u()}, Proto: ConsensusVersionView{Version: version}} + err, panicErr := pM.transition(inMsg) + require.NoError(t, err) + require.NoError(t, panicErr) + return vVote +} - // send voteVerified - inMsg = messageEvent{T: voteVerified, Input: voteMsg, TaskIndex: 1} - err, panicErr = pM.transition(inMsg) +// Helper function to send payloadPresent message +func sendPayloadPresent(t *testing.T, pWhite *player, pM ioAutomata, curRound round, pP *proposal, receivedAt time.Duration, historicalClocks map[round]roundStartTimer) { + m := message{UnauthenticatedProposal: pP.u()} + inMsg := messageEvent{T: payloadPresent, Input: m} + inMsg = inMsg.AttachReceivedAt(testClockForRound(t, pWhite, receivedAt, curRound, historicalClocks)) + err, panicErr := pM.transition(inMsg) + require.NoError(t, err) + require.NoError(t, panicErr) +} + +// Helper function to send a compound PP message (votePresent + payloadPresent) +func sendCompoundMessage(t *testing.T, helper *voteMakerHelper, pWhite *player, pM ioAutomata, curRound round, voteRound round, votePeriod period, pP *proposal, pV *proposalValue, receivedAt time.Duration, historicalClocks map[round]roundStartTimer, version protocol.ConsensusVersion) vote { + vVote := helper.MakeVerifiedVote(t, 0, voteRound, votePeriod, propose, *pV) + sendCompoundMessageForVote(t, vVote, pWhite, pM, curRound, pP, receivedAt, historicalClocks, version) + return vVote +} + +func sendCompoundMessageForVote(t *testing.T, vVote vote, pWhite *player, pM ioAutomata, curRound round, pP *proposal, receivedAt time.Duration, historicalClocks map[round]roundStartTimer, version protocol.ConsensusVersion) { + unverifiedVoteMsg := message{UnauthenticatedVote: vVote.u()} + proposalMsg := message{UnauthenticatedProposal: pP.u()} + compoundMsg := messageEvent{ + T: votePresent, + Input: unverifiedVoteMsg, + Tail: &messageEvent{ + T: payloadPresent, + Input: proposalMsg, + Proto: ConsensusVersionView{Version: version}, + }, + Proto: ConsensusVersionView{Version: version}, + } + inMsg := compoundMsg.AttachReceivedAt(testClockForRound(t, pWhite, receivedAt, curRound, historicalClocks)) // call AttachReceivedAt like demux would + err, panicErr := pM.transition(inMsg) require.NoError(t, err) require.NoError(t, panicErr) +} - assertCorrectReceivedAtSet(t, pWhite, pM, helper, r, p, pP, pV, proposalMsg) +// Helper function to assert lowest vote validateAt time was recorded into lowestCredentialArrivals +func assertSingleCredentialArrival(t *testing.T, pWhite *player, expectedTime time.Duration) { + require.NotZero(t, dynamicFilterCredentialArrivalHistory) + require.Equal(t, pWhite.lowestCredentialArrivals.writePtr, 1) + require.False(t, pWhite.lowestCredentialArrivals.isFull()) + require.Equal(t, expectedTime, pWhite.lowestCredentialArrivals.history[0]) } -func assertCorrectReceivedAtSet(t *testing.T, pWhite *player, pM ioAutomata, helper *voteMakerHelper, - r round, p period, pP *proposal, pV *proposalValue, m message) { +// Helper function to submit payloadVerified message and a bundleVerified for a cert threshold +// to move into the next round. +// Assumes payloadPresent has alread been sent and the verifyPayload action has already requested. +func moveToRound(t *testing.T, pWhite *player, pM ioAutomata, helper *voteMakerHelper, + r round, p period, pP *proposal, pV *proposalValue, validatedAt time.Duration, ver protocol.ConsensusVersion) { + // make sure payload verify request - verifyEvent := ev(cryptoAction{T: verifyPayload, M: m, Round: r - 1, Period: p, Step: propose, TaskIndex: 0}) + verifyEvent := ev(verifyPayloadAction(messageEvent{Input: message{UnauthenticatedProposal: pP.u()}}, r-1, p, false)) require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify payload") // payloadVerified - inMsg := messageEvent{T: payloadVerified, Input: message{Proposal: *pP}, Proto: ConsensusVersionView{Version: protocol.ConsensusCurrentVersion}} - inMsg = inMsg.AttachValidatedAt(2 * time.Second) // call AttachValidatedAt like demux would + inMsg := messageEvent{T: payloadVerified, Input: message{Proposal: *pP}, Proto: ConsensusVersionView{Version: ver}} + inMsg = inMsg.AttachValidatedAt(testClockForRound(t, pWhite, validatedAt, r-1, nil)) // call AttachValidatedAt like demux would err, panicErr := pM.transition(inMsg) require.NoError(t, err) require.NoError(t, panicErr) + // // now, trigger soft vote timeout + // err, panicErr = pM.transition(makeTimeoutEvent()) + // require.NoError(t, err) + // require.NoError(t, panicErr) + // softVoteEvent := ev(pseudonodeAction{T: attest, Round: r - 1, Period: p, Step: soft, Proposal: *pV}) + // require.Truef(t, pM.getTrace().Contains(softVoteEvent), "Player should issue soft vote") + // gen cert to move into the next round - votes := make([]vote, int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion]))) - for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ { + votes := make([]vote, int(cert.threshold(config.Consensus[ver]))) + for i := 0; i < int(cert.threshold(config.Consensus[ver])); i++ { votes[i] = helper.MakeVerifiedVote(t, i, r-1, p, cert, *pV) } bun := unauthenticatedBundle{ @@ -3330,7 +3972,7 @@ func assertCorrectReceivedAtSet(t *testing.T, pWhite *player, pM ioAutomata, hel }, UnauthenticatedBundle: bun, }, - Proto: ConsensusVersionView{Version: protocol.ConsensusCurrentVersion}, + Proto: ConsensusVersionView{Version: ver}, } err, panicErr = pM.transition(inMsg) require.NoError(t, err) @@ -3340,6 +3982,10 @@ func assertCorrectReceivedAtSet(t *testing.T, pWhite *player, pM ioAutomata, hel require.Equalf(t, period(0), pWhite.Period, "player did not enter period 0 in new round") commitEvent := ev(ensureAction{Certificate: Certificate(bun), Payload: *pP}) require.Truef(t, pM.getTrace().Contains(commitEvent), "Player should try to ensure block/digest on ledger") +} + +// inspect the ensureAction for round R and assert the correct payload timings +func assertPayloadTimings(t *testing.T, pWhite *player, pM ioAutomata, r round, pV *proposalValue, receivedAt time.Duration, validatedAt time.Duration) { // find and unwrap ensureAction from trace var ea ensureAction @@ -3349,13 +3995,19 @@ func assertCorrectReceivedAtSet(t *testing.T, pWhite *player, pM ioAutomata, hel if wae.action.t() == ensure { require.False(t, foundEA) ea = wae.action.(ensureAction) - foundEA = true + // looking just for ensureAction on this round + if r == ea.Payload.Round() { + foundEA = true + break + } } } } require.True(t, foundEA) - require.Equal(t, 2*time.Second, ea.Payload.validatedAt) - require.Equal(t, time.Second, ea.Payload.receivedAt) + require.Equal(t, *pV, ea.Certificate.Proposal) + require.Equal(t, r, ea.Payload.Round()) + require.Equal(t, validatedAt, ea.Payload.validatedAt) + require.Equal(t, receivedAt, ea.Payload.receivedAt) } // todo: test pipelined rounds, and round interruption diff --git a/agreement/proposalManager.go b/agreement/proposalManager.go index affa17f898..71bea3df53 100644 --- a/agreement/proposalManager.go +++ b/agreement/proposalManager.go @@ -132,9 +132,15 @@ func (m *proposalManager) handleMessageEvent(r routerHandle, p player, e filtera switch e.t() { case votePresent: - err := m.filterProposalVote(p, r, e.Input.UnauthenticatedVote, e.FreshnessData) + verifyForCredHistory, err := m.filterProposalVote(p, r, e.Input.UnauthenticatedVote, e.FreshnessData) if err != nil { - return filteredEvent{T: voteFiltered, Err: makeSerErr(err)} + credTrackingNote := NoLateCredentialTrackingImpact + if verifyForCredHistory { + // mark filtered votes that may still update the best credential arrival time + // the freshness check failed, but we still want to verify this proposal-vote for credential tracking + credTrackingNote = UnverifiedLateCredentialForTracking + } + return filteredEvent{T: voteFiltered, Err: makeSerErr(err), LateCredentialTrackingNote: credTrackingNote} } return emptyEvent{} @@ -150,9 +156,14 @@ func (m *proposalManager) handleMessageEvent(r routerHandle, p player, e filtera v := e.Input.Vote err := proposalFresh(e.FreshnessData, v.u()) + keepForLateCredentialTracking := false if err != nil { - err := makeSerErrf("proposalManager: ignoring proposal-vote due to age: %v", err) - return filteredEvent{T: voteFiltered, Err: err} + // if we should keep processing this credential message only to record its timestamp, we continue + keepForLateCredentialTracking = proposalUsefulForCredentialHistory(e.FreshnessData.PlayerRound, v.u()) + if !keepForLateCredentialTracking { + err := makeSerErrf("proposalManager: ignoring proposal-vote due to age: %v", err) + return filteredEvent{T: voteFiltered, Err: err} + } } if v.R.Round == p.Round { @@ -161,7 +172,26 @@ func (m *proposalManager) handleMessageEvent(r routerHandle, p player, e filtera r.t.timeRPlus1().RecVoteReceived(v) } - return r.dispatch(p, e.messageEvent, proposalMachineRound, v.R.Round, v.R.Period, 0) + e := r.dispatch(p, e.messageEvent, proposalMachineRound, v.R.Round, v.R.Period, 0) + + if keepForLateCredentialTracking { + // we only continued processing this vote to see whether it updates the credential arrival time + err := makeSerErrf("proposalManager: ignoring proposal-vote due to age: %v", err) + if e.t() == voteFiltered { + credNote := e.(filteredEvent).LateCredentialTrackingNote + if credNote != VerifiedBetterLateCredentialForTracking && credNote != NoLateCredentialTrackingImpact { + // It should be impossible to hit this condition + r.t.log.Debugf("vote verified may only be tagged with VerifiedBetterLateCredential/NoLateCredentialTrackingImpact but saw %v", credNote) + credNote = NoLateCredentialTrackingImpact + } + // indicate whether it updated + return filteredEvent{T: voteFiltered, Err: err, LateCredentialTrackingNote: credNote} + } + // the proposalMachineRound didn't filter the vote, so it must have had a better credential, + // indicate that it did cause updating its state + return filteredEvent{T: voteFiltered, Err: err, LateCredentialTrackingNote: VerifiedBetterLateCredentialForTracking} + } + return e case payloadPresent: propRound := e.Input.UnauthenticatedProposal.Round() @@ -215,19 +245,48 @@ func (m *proposalManager) handleMessageEvent(r routerHandle, p player, e filtera } } -// filterVote filters a vote, checking if it is both fresh and not a duplicate. -func (m *proposalManager) filterProposalVote(p player, r routerHandle, uv unauthenticatedVote, freshData freshnessData) error { +// filterVote filters a vote, checking if it is both fresh and not a duplicate, returning +// an errProposalManagerPVNotFresh or errProposalManagerPVDuplicate if so, else nil. +// It also returns a bool indicating whether this proposal-vote should still be verified for tracking credential history. +func (m *proposalManager) filterProposalVote(p player, r routerHandle, uv unauthenticatedVote, freshData freshnessData) (bool, error) { + // check if the vote is within the credential history window + credHistory := proposalUsefulForCredentialHistory(freshData.PlayerRound, uv) + + // checkDup asks proposalTracker if the vote is a duplicate, returning true if so + checkDup := func() bool { + qe := voteFilterRequestEvent{RawVote: uv.R} + sawVote := r.dispatch(p, qe, proposalMachinePeriod, uv.R.Round, uv.R.Period, 0) + return sawVote.t() == voteFiltered + } + + // check the vote against the current player's freshness rules err := proposalFresh(freshData, uv) if err != nil { - return fmt.Errorf("proposalManager: filtered proposal-vote due to age: %v", err) + // not fresh, but possibly useful for credential history: ensure not a duplicate + if credHistory && checkDup() { + credHistory = false + } + return credHistory, fmt.Errorf("proposalManager: filtered proposal-vote due to age: %v", err) } - qe := voteFilterRequestEvent{RawVote: uv.R} - sawVote := r.dispatch(p, qe, proposalMachinePeriod, uv.R.Round, uv.R.Period, 0) - if sawVote.t() == voteFiltered { - return fmt.Errorf("proposalManager: filtered proposal-vote: sender %v had already sent a vote in round %d period %d", uv.R.Sender, uv.R.Round, uv.R.Period) + if checkDup() { + return credHistory, fmt.Errorf("proposalManager: filtered proposal-vote: sender %v had already sent a vote in round %d period %d", uv.R.Sender, uv.R.Round, uv.R.Period) + } - return nil + return credHistory, nil +} + +func proposalUsefulForCredentialHistory(curRound round, vote unauthenticatedVote) bool { + if vote.R.Round < curRound && curRound <= vote.R.Round+credentialRoundLag && + vote.R.Period == 0 && + vote.R.Step == propose { + if dynamicFilterCredentialArrivalHistory > 0 { + // continue processing old period 0 votes so we could track their + // arrival times and inform setting the filter timeout dynamically. + return true + } + } + return false } // voteFresh determines whether a proposal satisfies freshness rules. diff --git a/agreement/proposalManager_test.go b/agreement/proposalManager_test.go index e76de0be5e..48481bd730 100644 --- a/agreement/proposalManager_test.go +++ b/agreement/proposalManager_test.go @@ -19,6 +19,7 @@ package agreement import ( "testing" + "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" ) @@ -209,6 +210,69 @@ func TestProposalManagerRejectsUnknownEvent(t *testing.T) { require.Errorf(t, panicErr, "proposalManager must reject bundleVerified event") } +func TestLateVotes(t *testing.T) { + partitiontest.PartitionTest(t) + + const r = 100 + const p = 3 + const s = soft + currentPlayerState := freshnessData{ + PlayerRound: r, + PlayerPeriod: p, + PlayerStep: s, + PlayerLastConcluding: 0, + } + b := testCaseBuilder{} + _, pM, helper := setupManager(t, r) + + // vote from credentialRoundLag rounds ago and period 0 should continue + // processing only for the purpose of tracking credentials. + pV := helper.MakeRandomProposalValue() + v1 := helper.MakeVerifiedVote(t, 0, r-credentialRoundLag, 0, 0, *pV) + inMsg1 := filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: voteVerified, + Input: message{ + UnauthenticatedVote: v1.u(), + Vote: v1, + }, + }, + } + + v2 := helper.MakeVerifiedVote(t, 0, r-credentialRoundLag, 0, 0, *pV) + inMsg2 := filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: voteVerified, + Input: message{ + UnauthenticatedVote: v2.u(), + Vote: v2, + }, + }, + } + + // Order the messages such that the first message's credential is lower + // (i.e., preferred). The first vote should be the best credential we get, + // so the second credential should be filtered without impacting the + // credential tracking mechanism. + v1.Cred.VrfOut = crypto.Digest{1} + v2.Cred.VrfOut = crypto.Digest{2} + if v1.Cred.Less(v2.Cred) { + require.False(t, v2.Cred.Less(v1.Cred)) + b.AddInOutPair(inMsg1, filteredEvent{T: voteFiltered, LateCredentialTrackingNote: VerifiedBetterLateCredentialForTracking}) + b.AddInOutPair(inMsg2, filteredEvent{T: voteFiltered, LateCredentialTrackingNote: NoLateCredentialTrackingImpact}) + } else { + require.True(t, v2.Cred.Less(v1.Cred)) + b.AddInOutPair(inMsg2, filteredEvent{T: voteFiltered, LateCredentialTrackingNote: VerifiedBetterLateCredentialForTracking}) + b.AddInOutPair(inMsg1, filteredEvent{T: voteFiltered, LateCredentialTrackingNote: NoLateCredentialTrackingImpact}) + } + + res, err := b.Build().Validate(pM) + require.NoError(t, err) + require.NoErrorf(t, res, "VerifiedVote resulted in unexpected output") +} + func TestProposalFreshAdjacentPeriods(t *testing.T) { partitiontest.PartitionTest(t) @@ -323,6 +387,63 @@ func TestProposalFreshAdjacentPeriods(t *testing.T) { } b.AddInOutPair(inMsg, filteredEvent{T: voteFiltered}) + // vote from credentialRoundLag rounds ago and period 0 should continue + // processing only for the purpose of tracking credentials. + pV = helper.MakeRandomProposalValue() + uv = helper.MakeUnauthenticatedVote(t, 0, r-credentialRoundLag, 0, 0, *pV) + inMsg = filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: votePresent, + Input: message{ + UnauthenticatedVote: uv, + }, + }, + } + b.AddInOutPair(inMsg, filteredEvent{T: voteFiltered, LateCredentialTrackingNote: UnverifiedLateCredentialForTracking}) + + // vote from credentialRoundLag ago and period > 0 should be filtered + pV = helper.MakeRandomProposalValue() + uv = helper.MakeUnauthenticatedVote(t, 0, r-credentialRoundLag, 1, 0, *pV) + inMsg = filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: votePresent, + Input: message{ + UnauthenticatedVote: uv, + }, + }, + } + b.AddInOutPair(inMsg, filteredEvent{T: voteFiltered}) + + // vote older than credentialRoundLag should be rejected even if period 0 + pV = helper.MakeRandomProposalValue() + uv = helper.MakeUnauthenticatedVote(t, 0, r-credentialRoundLag-1, 0, 0, *pV) + inMsg = filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: votePresent, + Input: message{ + UnauthenticatedVote: uv, + }, + }, + } + b.AddInOutPair(inMsg, filteredEvent{T: voteFiltered}) + + // vote older than credentialRoundLag should be rejected on any period + pV = helper.MakeRandomProposalValue() + uv = helper.MakeUnauthenticatedVote(t, 0, r-credentialRoundLag-1, 1, 0, *pV) + inMsg = filterableMessageEvent{ + FreshnessData: currentPlayerState, + messageEvent: messageEvent{ + T: votePresent, + Input: message{ + UnauthenticatedVote: uv, + }, + }, + } + b.AddInOutPair(inMsg, filteredEvent{T: voteFiltered}) + res, err := b.Build().Validate(pM) require.NoError(t, err) require.NoErrorf(t, res, "VotePresent accidentally filtered") diff --git a/agreement/proposalStore.go b/agreement/proposalStore.go index 080609de50..fdfecac5f2 100644 --- a/agreement/proposalStore.go +++ b/agreement/proposalStore.go @@ -352,6 +352,9 @@ func (store *proposalStore) handle(r routerHandle, p player, e event) event { se.Committable = ea.Assembled se.Payload = ea.Payload return se + case readLowestVote: + re := e.(readLowestEvent) + return r.dispatch(p, re, proposalMachinePeriod, re.Round, re.Period, 0).(readLowestEvent) case readPinned: se := e.(pinnedValueEvent) ea := store.Assemblers[store.Pinned] // If pinned is bottom, assembled/payloadOK = false, payload = bottom diff --git a/agreement/proposalTracker.go b/agreement/proposalTracker.go index 59ffb77a28..de89f8e3e9 100644 --- a/agreement/proposalTracker.go +++ b/agreement/proposalTracker.go @@ -33,22 +33,46 @@ type proposalSeeker struct { // Frozen is set once freeze is called. When Frozen is set, Lowest and // Filled will no longer be modified. Frozen bool + + // lowestIncludingLate is used to track the lowest credential observed, even + // after the Lowest value has been frozen. + lowestIncludingLate vote + hasLowestIncludingLate bool } // accept compares a given vote with the current lowest-credentialled vote and -// sets it if freeze has not been called. -func (s proposalSeeker) accept(v vote) (proposalSeeker, error) { +// sets it if freeze has not been called. Returns: +// - updated proposalSeeker state, +// - a LateCredentialTrackingEffect describing the usefulness of proposal-vote's +// credential for late credential tracking (for choosing dynamic filter timeout), +// - and an error if the proposal was not better than the lowest seen, or the +// seeker was already frozen. +func (s proposalSeeker) accept(v vote) (proposalSeeker, LateCredentialTrackingEffect, error) { if s.Frozen { - return s, errProposalSeekerFrozen{} + effect := NoLateCredentialTrackingImpact + // continue tracking and forwarding the lowest proposal even when frozen + if !s.hasLowestIncludingLate || v.Cred.Less(s.lowestIncludingLate.Cred) { + s.lowestIncludingLate = v + s.hasLowestIncludingLate = true + effect = VerifiedBetterLateCredentialForTracking + } + return s, effect, errProposalSeekerFrozen{} } if s.Filled && !v.Cred.Less(s.Lowest.Cred) { - return s, errProposalSeekerNotLess{NewSender: v.R.Sender, LowestSender: s.Lowest.R.Sender} + return s, NoLateCredentialTrackingImpact, errProposalSeekerNotLess{NewSender: v.R.Sender, LowestSender: s.Lowest.R.Sender} } s.Lowest = v s.Filled = true - return s, nil + s.lowestIncludingLate = v + s.hasLowestIncludingLate = true + return s, VerifiedBetterLateCredentialForTracking, nil +} + +func (s *proposalSeeker) copyLateCredentialTrackingState(s2 proposalSeeker) { + s.hasLowestIncludingLate = s2.hasLowestIncludingLate + s.lowestIncludingLate = s2.lowestIncludingLate } // freeze freezes the state of the proposalSeeker so that future calls no longer @@ -88,7 +112,7 @@ func (t *proposalTracker) underlying() listener { return t } -// A proposalTracker handles five types of events. +// A proposalTracker handles six types of events. // // - voteFilterRequest returns a voteFiltered event if a given proposal-vote // from a given sender has already been seen. Otherwise it returns an empty @@ -118,6 +142,8 @@ func (t *proposalTracker) underlying() listener { // - readStaging returns the a stagingValueEvent with the proposal-value // believed to be the staging value (i.e., sigma(S, r, p)) by the // proposalTracker in period p. +// +// - readLowestVote returns the vote with the lowest credential that was received so far. func (t *proposalTracker) handle(r routerHandle, p player, e event) event { switch e.t() { case voteFilterRequest: @@ -141,17 +167,18 @@ func (t *proposalTracker) handle(r routerHandle, p player, e event) event { } t.Duplicate[v.R.Sender] = true + newFreezer, effect, err := t.Freezer.accept(v) + t.Freezer.copyLateCredentialTrackingState(newFreezer) if t.Staging != bottom { - err := errProposalTrackerStaged{} - return filteredEvent{T: voteFiltered, Err: makeSerErr(err)} + err = errProposalTrackerStaged{} + return filteredEvent{T: voteFiltered, LateCredentialTrackingNote: effect, Err: makeSerErr(err)} } - var err error - t.Freezer, err = t.Freezer.accept(v) if err != nil { err := errProposalTrackerPS{Sub: err} - return filteredEvent{T: voteFiltered, Err: makeSerErr(err)} + return filteredEvent{T: voteFiltered, LateCredentialTrackingNote: effect, Err: makeSerErr(err)} } + t.Freezer = newFreezer return proposalAcceptedEvent{ Round: v.R.Round, @@ -165,6 +192,14 @@ func (t *proposalTracker) handle(r routerHandle, p player, e event) event { t.Freezer = t.Freezer.freeze() return e + case readLowestVote: + e := e.(readLowestEvent) + e.Vote = t.Freezer.Lowest + e.Filled = t.Freezer.Filled + e.LowestIncludingLate = t.Freezer.lowestIncludingLate + e.HasLowestIncludingLate = t.Freezer.hasLowestIncludingLate + return e + case softThreshold, certThreshold: e := e.(thresholdEvent) t.Staging = e.Proposal diff --git a/agreement/proposalTrackerContract.go b/agreement/proposalTrackerContract.go index 2b995dfcac..a6fa2ffc36 100644 --- a/agreement/proposalTrackerContract.go +++ b/agreement/proposalTrackerContract.go @@ -32,7 +32,7 @@ type proposalTrackerContract struct { // TODO check concrete types of events func (c *proposalTrackerContract) pre(p player, in event) (pre []error) { switch in.t() { - case voteVerified, proposalFrozen, softThreshold, certThreshold, voteFilterRequest, readStaging: + case voteVerified, proposalFrozen, softThreshold, certThreshold, voteFilterRequest, readStaging, readLowestVote: default: pre = append(pre, fmt.Errorf("incoming event has invalid type: %v", in.t())) } diff --git a/agreement/proposalTracker_test.go b/agreement/proposalTracker_test.go index 641dee70e7..164e9db40b 100644 --- a/agreement/proposalTracker_test.go +++ b/agreement/proposalTracker_test.go @@ -63,36 +63,57 @@ func TestProposalTrackerProposalSeeker(t *testing.T) { var err error assert.False(t, s.Frozen) assert.False(t, s.Filled) + assert.False(t, s.hasLowestIncludingLate) // issue events in the following order: 2, 3, 1, (freeze), 0 - s, err = s.accept(votes[2]) + var effect LateCredentialTrackingEffect + s, effect, err = s.accept(votes[2]) assert.NoError(t, err) + assert.Equal(t, effect, VerifiedBetterLateCredentialForTracking) assert.False(t, s.Frozen) assert.True(t, s.Filled) assert.True(t, s.Lowest.equals(votes[2])) + assert.True(t, s.hasLowestIncludingLate) + assert.Equal(t, s.Lowest, s.lowestIncludingLate) - s, err = s.accept(votes[3]) + s, effect, err = s.accept(votes[3]) assert.Error(t, err) + assert.Equal(t, effect, NoLateCredentialTrackingImpact) assert.False(t, s.Frozen) assert.True(t, s.Filled) assert.True(t, s.Lowest.equals(votes[2])) + assert.True(t, s.hasLowestIncludingLate) + assert.Equal(t, s.Lowest, s.lowestIncludingLate) - s, err = s.accept(votes[1]) + s, effect, err = s.accept(votes[1]) assert.NoError(t, err) + assert.Equal(t, effect, VerifiedBetterLateCredentialForTracking) assert.False(t, s.Frozen) assert.True(t, s.Filled) assert.True(t, s.Lowest.equals(votes[1])) + assert.True(t, s.hasLowestIncludingLate) + assert.Equal(t, s.Lowest, s.lowestIncludingLate) + lowestBeforeFreeze := s.Lowest s = s.freeze() assert.True(t, s.Frozen) assert.True(t, s.Filled) assert.True(t, s.Lowest.equals(votes[1])) + assert.True(t, s.hasLowestIncludingLate) + assert.Equal(t, s.Lowest, s.lowestIncludingLate) - s, err = s.accept(votes[0]) + s, effect, err = s.accept(votes[0]) assert.Error(t, err) + assert.Equal(t, effect, VerifiedBetterLateCredentialForTracking) + assert.Equal(t, s.Lowest, lowestBeforeFreeze) assert.True(t, s.Frozen) assert.True(t, s.Filled) assert.True(t, s.Lowest.equals(votes[1])) + assert.True(t, s.hasLowestIncludingLate) + assert.True(t, s.lowestIncludingLate.equals(votes[0])) + assert.NotEqual(t, s.Lowest, s.lowestIncludingLate) + assert.True(t, !s.Lowest.Cred.Less(s.lowestIncludingLate.Cred)) + assert.True(t, s.lowestIncludingLate.Cred.Less(s.Lowest.Cred)) } // mimics a proposalTracker, producing a trace of events diff --git a/agreement/router.go b/agreement/router.go index 32523ee8e9..6ab144470f 100644 --- a/agreement/router.go +++ b/agreement/router.go @@ -16,6 +16,10 @@ package agreement +import ( + "github.com/algorand/go-algorand/config" +) + // A stateMachineTag uniquely identifies the type of a state machine. // // Rounds, periods, and steps may be used to further identify different state machine instances of the same type. @@ -48,6 +52,19 @@ type routerHandle struct { src stateMachineTag } +// credentialRoundLag the maximal number of rounds that could pass before a credential from +// an honest party for an old round may arrive. It uses the +// dynamicFilterTimeoutLowerBound parameter as the minimal round time. +var credentialRoundLag round + +func init() { + // credential arrival time should be at most 2*config.Protocol.SmallLambda after it was sent + credentialRoundLag = round(2 * config.Protocol.SmallLambda / dynamicFilterTimeoutLowerBound) + if credentialRoundLag*round(dynamicFilterTimeoutLowerBound) < round(2*config.Protocol.SmallLambda) { + credentialRoundLag++ + } +} + // dispatch sends an event to the given state machine listener with the given stateMachineTag. // // If there are many state machines of this type (for instance, there is one voteMachineStep for each step) @@ -136,7 +153,11 @@ func (router *rootRouter) update(state player, r round, gc bool) { if gc { children := make(map[round]*roundRouter) for r, c := range router.Children { - if r >= state.Round { + // We may still receive credential messages from old rounds. Keep + // old round routers around, for as long as those credentials may + // arrive to keep track of them. + rr := r + credentialRoundLag + if rr >= state.Round { children[r] = c } } @@ -201,7 +222,6 @@ func (router *roundRouter) update(state player, p period, gc bool) { // TODO may want regression test for correct pipelining behavior children[p] = c } - } router.Children = children } diff --git a/agreement/service.go b/agreement/service.go index 765f4270a9..433a17d5c6 100644 --- a/agreement/service.go +++ b/agreement/service.go @@ -19,6 +19,7 @@ package agreement //go:generate dbgen -i agree.sql -p agreement -n agree -o agreeInstall.go -h ../scripts/LICENSE_HEADER import ( "context" + "time" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/logging" @@ -56,6 +57,9 @@ type Service struct { persistRouter rootRouter persistStatus player persistActions []action + + // Retain old rounds' period 0 start times. + historicalClocks map[round]roundStartTimer } // Parameters holds the parameters necessary to run the agreement protocol. @@ -84,6 +88,13 @@ type externalDemuxSignals struct { CurrentRound round } +// an interface allowing for measuring the duration since a clock from a previous round, +// used for measuring the arrival time of a late proposal-vote, for the dynamic filter +// timeout feature +type roundStartTimer interface { + Since() time.Duration +} + // MakeService creates a new Agreement Service instance given a set of Parameters. // // Call Start to start execution and Shutdown to finish execution. @@ -94,10 +105,15 @@ func MakeService(p Parameters) (*Service, error) { s.log = makeServiceLogger(p.Logger) + // If cadaver directory is not set, use cold data directory (which may also not be set) + cadaverDir := p.CadaverDirectory + if cadaverDir == "" { + cadaverDir = p.ColdDataDir + } // GOAL2-541: tracer is not concurrency safe. It should only ever be // accessed by main state machine loop. var err error - s.tracer, err = makeTracer(s.log, defaultCadaverName, p.CadaverSizeTarget, p.CadaverDirectory, + s.tracer, err = makeTracer(s.log, defaultCadaverName, p.CadaverSizeTarget, cadaverDir, s.Local.EnableAgreementReporting, s.Local.EnableAgreementTimeMetrics) if err != nil { return nil, err @@ -105,6 +121,8 @@ func MakeService(p Parameters) (*Service, error) { s.persistenceLoop = makeAsyncPersistenceLoop(s.log, s.Accessor, s.Ledger) + s.historicalClocks = make(map[round]roundStartTimer) + return s, nil } @@ -211,7 +229,7 @@ func (s *Service) mainLoop(input <-chan externalEvent, output chan<- []action, r s.log.Errorf("unable to retrieve consensus version for round %d, defaulting to binary consensus version", nextRound) nextVersion = protocol.ConsensusCurrentVersion } - status = player{Round: nextRound, Step: soft, Deadline: Deadline{Duration: FilterTimeout(0, nextVersion), Type: TimeoutFilter}} + status = player{Round: nextRound, Step: soft, Deadline: Deadline{Duration: FilterTimeout(0, nextVersion), Type: TimeoutFilter}, lowestCredentialArrivals: makeCredentialArrivalHistory(dynamicFilterCredentialArrivalHistory)} router = makeRootRouter(status) a1 := pseudonodeAction{T: assemble, Round: s.Ledger.NextRound()} diff --git a/agreement/service_test.go b/agreement/service_test.go index fc50c302d5..f6ff27fd2b 100644 --- a/agreement/service_test.go +++ b/agreement/service_test.go @@ -77,7 +77,7 @@ func (c *testingClock) Zero() timers.Clock[TimeoutType] { } func (c *testingClock) Since() time.Duration { - return 0 + return 1 } func (c *testingClock) TimeoutAt(d time.Duration, timeoutType TimeoutType) <-chan time.Time { @@ -93,6 +93,17 @@ func (c *testingClock) TimeoutAt(d time.Duration, timeoutType TimeoutType) <-cha return ta.ch } +func (c *testingClock) when(timeoutType TimeoutType) (time.Duration, error) { + c.mu.Lock() + defer c.mu.Unlock() + + ta, ok := c.TA[timeoutType] + if !ok { + return time.Duration(0), fmt.Errorf("no timeout of type, %v", timeoutType) + } + return ta.delta, nil +} + func (c *testingClock) Encode() []byte { return nil } @@ -862,6 +873,10 @@ func runRound(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonit triggerGlobalTimeout(filterTimeout, TimeoutFilter, clocks, activityMonitor) return expectNewPeriod(clocks, zeroes) } +func runRoundTriggerFilter(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint) (newzeroes uint) { + triggerGlobalTimeoutType(TimeoutFilter, clocks, activityMonitor) + return expectNewPeriod(clocks, zeroes) +} func sanityCheck(startRound round, numRounds round, ledgers []Ledger) { for i := range ledgers { @@ -880,18 +895,18 @@ func sanityCheck(startRound round, numRounds round, ledgers []Ledger) { } } -func simulateAgreement(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel) { - simulateAgreementWithLedgerFactory(t, numNodes, numRounds, traceLevel, makeTestLedger) +func simulateAgreement(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel) (filterTimeouts []time.Duration) { + return simulateAgreementWithLedgerFactory(t, numNodes, numRounds, traceLevel, makeTestLedger) } -func simulateAgreementWithConsensusVersion(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel, consensusVersion func(basics.Round) (protocol.ConsensusVersion, error)) { +func simulateAgreementWithConsensusVersion(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel, consensusVersion func(basics.Round) (protocol.ConsensusVersion, error)) (filterTimeouts []time.Duration) { ledgerFactory := func(data map[basics.Address]basics.AccountData) Ledger { return makeTestLedgerWithConsensusVersion(data, consensusVersion) } - simulateAgreementWithLedgerFactory(t, numNodes, numRounds, traceLevel, ledgerFactory) + return simulateAgreementWithLedgerFactory(t, numNodes, numRounds, traceLevel, ledgerFactory) } -func simulateAgreementWithLedgerFactory(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel, ledgerFactory func(map[basics.Address]basics.AccountData) Ledger) { +func simulateAgreementWithLedgerFactory(t *testing.T, numNodes int, numRounds int, traceLevel traceLevel, ledgerFactory func(map[basics.Address]basics.AccountData) Ledger) []time.Duration { _, baseLedger, cleanupFn, services, clocks, ledgers, activityMonitor := setupAgreement(t, numNodes, traceLevel, ledgerFactory) startRound := baseLedger.NextRound() defer cleanupFn() @@ -903,19 +918,56 @@ func simulateAgreementWithLedgerFactory(t *testing.T, numNodes int, numRounds in activityMonitor.waitForQuiet() zeroes := expectNewPeriod(clocks, 0) + filterTimeouts := make([][]time.Duration, numNodes, numNodes) + // run round with round-specific consensus version first (since fix in #1896) - version, _ := baseLedger.ConsensusVersion(ParamsRound(startRound)) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) for j := 1; j < numRounds; j++ { - version, _ := baseLedger.ConsensusVersion(ParamsRound(baseLedger.NextRound() + basics.Round(j-1))) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + for srvIdx, clock := range clocks { + delta, err := clock.(*testingClock).when(TimeoutFilter) + require.NoError(t, err) + filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) + } + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) } for i := 0; i < numNodes; i++ { services[i].Shutdown() } + firstHistoricalClocksRound := startRound + if basics.Round(numRounds) > credentialRoundLag { + firstHistoricalClocksRound = startRound + basics.Round(numRounds) - credentialRoundLag + } + + // check that historical clocks map didn't get too large + for i := 0; i < numNodes; i++ { + require.LessOrEqual(t, len(services[i].historicalClocks), int(credentialRoundLag)+1, "too many historical clocks kept") + for round := firstHistoricalClocksRound + 1; round <= startRound+basics.Round(numRounds); round++ { + _, has := services[i].historicalClocks[round] + require.True(t, has) + } + } + if numRounds >= int(credentialRoundLag) { + for i := 0; i < numNodes; i++ { + require.Equal(t, len(services[i].historicalClocks), int(credentialRoundLag)+1, "not enough historical clocks kept") + } + } + sanityCheck(startRound, round(numRounds), ledgers) + + if len(clocks) == 0 { + return nil + } + + for rnd := 0; rnd < numRounds-1; rnd++ { + delta := filterTimeouts[0][rnd] + for srvIdx := range clocks { + require.Equal(t, delta, filterTimeouts[srvIdx][rnd]) + } + } + + return filterTimeouts[0] } func TestAgreementSynchronous1(t *testing.T) { @@ -988,6 +1040,172 @@ func TestAgreementSynchronous5_50(t *testing.T) { simulateAgreement(t, 5, 50, disabled) } +func TestAgreementHistoricalClocksCleanup(t *testing.T) { + partitiontest.PartitionTest(t) + + if testing.Short() { + t.Skip("Skipping agreement integration test") + } + + simulateAgreement(t, 5, int(credentialRoundLag)+10, disabled) +} + +func createDynamicFilterConfig() (version protocol.ConsensusVersion, consensusVersion func(r basics.Round) (protocol.ConsensusVersion, error), configCleanup func()) { + version = protocol.ConsensusVersion("test-protocol-filtertimeout") + protoParams := config.Consensus[protocol.ConsensusCurrentVersion] + protoParams.DynamicFilterTimeout = true + config.Consensus[version] = protoParams + + consensusVersion = func(r basics.Round) (protocol.ConsensusVersion, error) { + return version, nil + } + + configCleanup = func() { + delete(config.Consensus, version) + } + + return +} + +func TestAgreementSynchronousFuture5_DynamicFilterRounds(t *testing.T) { + partitiontest.PartitionTest(t) + + if testing.Short() { + t.Skip("Skipping agreement integration test") + } + + _, consensusVersion, configCleanup := createDynamicFilterConfig() + defer configCleanup() + + if dynamicFilterCredentialArrivalHistory <= 0 { + return + } + + baseHistoryRounds := dynamicFilterCredentialArrivalHistory + int(credentialRoundLag) + rounds := baseHistoryRounds + 20 + + filterTimeouts := simulateAgreementWithConsensusVersion(t, 5, rounds, disabled, consensusVersion) + require.Len(t, filterTimeouts, rounds-1) + for i := 1; i < baseHistoryRounds-1; i++ { + require.Equal(t, filterTimeouts[i-1], filterTimeouts[i]) + } + + // dynamic filter timeout kicks in when history window is full + require.Less(t, filterTimeouts[baseHistoryRounds-1], filterTimeouts[baseHistoryRounds-2]) + + for i := baseHistoryRounds; i < len(filterTimeouts); i++ { + require.Equal(t, filterTimeouts[i-1], filterTimeouts[i]) + } +} + +func TestDynamicFilterTimeoutResets(t *testing.T) { + partitiontest.PartitionTest(t) + + if testing.Short() { + t.Skip("Skipping agreement integration test") + } + + version, consensusVersion, configCleanup := createDynamicFilterConfig() + defer configCleanup() + + if dynamicFilterCredentialArrivalHistory <= 0 { + return + } + + numNodes := 5 + + ledgerFactory := func(data map[basics.Address]basics.AccountData) Ledger { + return makeTestLedgerWithConsensusVersion(data, consensusVersion) + } + + baseNetwork, baseLedger, cleanupFn, services, clocks, ledgers, activityMonitor := setupAgreement(t, numNodes, disabled, ledgerFactory) + startRound := baseLedger.NextRound() + defer cleanupFn() + + for i := 0; i < numNodes; i++ { + services[i].Start() + } + activityMonitor.waitForActivity() + activityMonitor.waitForQuiet() + zeroes := expectNewPeriod(clocks, 0) + + filterTimeouts := make([][]time.Duration, numNodes, numNodes) + + baseHistoryRounds := dynamicFilterCredentialArrivalHistory + int(credentialRoundLag) + + // run round with round-specific consensus version first (since fix in #1896) + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + for j := 1; j < baseHistoryRounds+2; j++ { + for srvIdx, clock := range clocks { + delta, err := clock.(*testingClock).when(TimeoutFilter) + require.NoError(t, err) + filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) + } + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + } + + for i := range clocks { + require.Len(t, filterTimeouts[i], baseHistoryRounds+1) + for j := 1; j < baseHistoryRounds-2; j++ { + require.Equal(t, filterTimeouts[i][j-1], filterTimeouts[i][j]) + } + require.Less(t, filterTimeouts[i][baseHistoryRounds-1], filterTimeouts[i][baseHistoryRounds-2]) + } + + // force fast partition recovery into bottom + { + baseNetwork.dropAllSoftVotes() + baseNetwork.dropAllSlowNextVotes() + + triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) + zeroes = expectNoNewPeriod(clocks, zeroes) + + triggerGlobalTimeoutType(TimeoutDeadline, clocks, activityMonitor) + zeroes = expectNoNewPeriod(clocks, zeroes) + + triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer + zeroes = expectNoNewPeriod(clocks, zeroes) + + triggerGlobalTimeout(firstFPR, TimeoutFastRecovery, clocks, activityMonitor) + zeroes = expectNewPeriod(clocks, zeroes) + } + + // terminate on period 1 + { + baseNetwork.repairAll() + triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) + zeroes = expectNewPeriod(clocks, zeroes) + } + + filterTimeoutsPostRecovery := make([][]time.Duration, numNodes, numNodes) + + // run round with round-specific consensus version first (since fix in #1896) + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + for j := 1; j < baseHistoryRounds+1; j++ { + for srvIdx, clock := range clocks { + delta, err := clock.(*testingClock).when(TimeoutFilter) + require.NoError(t, err) + filterTimeoutsPostRecovery[srvIdx] = append(filterTimeoutsPostRecovery[srvIdx], delta) + } + zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + } + + for i := range clocks { + require.Len(t, filterTimeoutsPostRecovery[i], baseHistoryRounds) + // check that history was discarded, so filter time increased back to its original default + require.Less(t, filterTimeouts[i][baseHistoryRounds], filterTimeoutsPostRecovery[i][0]) + require.Equal(t, filterTimeouts[i][baseHistoryRounds-2], filterTimeoutsPostRecovery[i][0]) + + // check that filter timeout was updated to at the end of the history window + for j := 1; j < dynamicFilterCredentialArrivalHistory-2; j++ { + require.Equal(t, filterTimeoutsPostRecovery[i][j-1], filterTimeoutsPostRecovery[i][j]) + } + require.Less(t, filterTimeoutsPostRecovery[i][dynamicFilterCredentialArrivalHistory-1], filterTimeoutsPostRecovery[i][dynamicFilterCredentialArrivalHistory-2]) + } + + sanityCheck(startRound, 2*round(baseHistoryRounds+2), ledgers) +} + func TestAgreementSynchronousFuture1(t *testing.T) { partitiontest.PartitionTest(t) diff --git a/agreement/state_machine_test.go b/agreement/state_machine_test.go index 0effd9fda1..6946a051ec 100644 --- a/agreement/state_machine_test.go +++ b/agreement/state_machine_test.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "os" + "strings" "github.com/algorand/go-algorand/logging" ) @@ -111,7 +112,7 @@ func (t *ioTrace) String() string { var buf bytes.Buffer buf.WriteString("{\n") for i := 0; i < len(t.events); i++ { - buf.WriteString(fmt.Sprintf("\t%v |", t.events[i])) + buf.WriteString(fmt.Sprintf("\t%v |", t.events[i].ComparableStr())) if i%2 == 0 { buf.WriteString("\n") } @@ -140,6 +141,21 @@ func (t ioTrace) Contains(e event) bool { }) } +func (t ioTrace) ContainsString(s string) bool { + return t.ContainsFn(func(b event) bool { + return strings.Contains(b.ComparableStr(), s) + }) +} + +func (t ioTrace) CountEvent(b event) (count int) { + for _, e := range t.events { + if e.ComparableStr() == b.ComparableStr() { + count++ + } + } + return +} + // for each event, passes it into the given fn; if returns true, returns true. func (t ioTrace) ContainsFn(compareFn func(b event) bool) bool { for _, ev := range t.events { @@ -541,7 +557,7 @@ func (e wrappedActionEvent) String() string { } func (e wrappedActionEvent) ComparableStr() string { - return e.action.String() + return e.action.ComparableStr() } // ioAutomataConcretePlayer is a concrete wrapper around root router, implementing ioAutomata. diff --git a/agreement/vote.go b/agreement/vote.go index af157ee3bb..bed20fe88f 100644 --- a/agreement/vote.go +++ b/agreement/vote.go @@ -18,6 +18,7 @@ package agreement import ( "fmt" + "time" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" @@ -51,6 +52,10 @@ type ( R rawVote `codec:"r"` Cred committee.Credential `codec:"cred"` Sig crypto.OneTimeSignature `codec:"sig,omitempty,omitemptycheckstruct"` + + // validatedAt indicates the time at which this vote was verified (as a voteVerified messageEvent), + // relative to the zero of that round. It is only set for step 0. + validatedAt time.Duration } // unauthenticatedEquivocationVote is a pair of votes which has not diff --git a/buildnumber.dat b/buildnumber.dat index d00491fd7e..573541ac97 100644 --- a/buildnumber.dat +++ b/buildnumber.dat @@ -1 +1 @@ -1 +0 diff --git a/catchup/catchpointService.go b/catchup/catchpointService.go index 801901048b..7ad45305ff 100644 --- a/catchup/catchpointService.go +++ b/catchup/catchpointService.go @@ -290,7 +290,7 @@ func (cs *CatchpointCatchupService) processStageLedgerDownload() (err error) { } // download balances file. - peerSelector := makePeerSelector(cs.net, []peerClass{{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookRelays}}) + peerSelector := cs.makeCatchpointPeerSelector() ledgerFetcher := makeLedgerFetcher(cs.net, cs.ledgerAccessor, cs.log, cs, cs.config) attemptsCount := 0 @@ -796,15 +796,19 @@ func (cs *CatchpointCatchupService) updateBlockRetrievalStatistics(acquiredBlock } func (cs *CatchpointCatchupService) initDownloadPeerSelector() { + cs.blocksDownloadPeerSelector = cs.makeCatchpointPeerSelector() +} + +func (cs *CatchpointCatchupService) makeCatchpointPeerSelector() *peerSelector { if cs.config.EnableCatchupFromArchiveServers { - cs.blocksDownloadPeerSelector = makePeerSelector( + return makePeerSelector( cs.net, []peerClass{ {initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookArchivers}, {initialRank: peerRankInitialSecondPriority, peerClass: network.PeersPhonebookRelays}, }) } else { - cs.blocksDownloadPeerSelector = makePeerSelector( + return makePeerSelector( cs.net, []peerClass{ {initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookRelays}, @@ -820,7 +824,7 @@ func (cs *CatchpointCatchupService) checkLedgerDownload() error { if err != nil { return fmt.Errorf("failed to parse catchpoint label : %v", err) } - peerSelector := makePeerSelector(cs.net, []peerClass{{initialRank: peerRankInitialFirstPriority, peerClass: network.PeersPhonebookRelays}}) + peerSelector := cs.makeCatchpointPeerSelector() ledgerFetcher := makeLedgerFetcher(cs.net, cs.ledgerAccessor, cs.log, cs, cs.config) for i := 0; i < cs.config.CatchupLedgerDownloadRetryAttempts; i++ { psp, peerError := peerSelector.getNextPeer() diff --git a/catchup/pref_test.go b/catchup/pref_test.go index 688958ab26..be3f67f473 100644 --- a/catchup/pref_test.go +++ b/catchup/pref_test.go @@ -61,7 +61,8 @@ func BenchmarkServiceFetchBlocks(b *testing.B) { for i := 0; i < b.N; i++ { inMem := true - local, err := data.LoadLedger(logging.TestingLog(b), b.Name()+"empty"+strconv.Itoa(i), inMem, protocol.ConsensusCurrentVersion, genesisBalances, "", crypto.Digest{}, nil, cfg) + prefix := b.Name() + "empty" + strconv.Itoa(i) + local, err := data.LoadLedger(logging.TestingLog(b), prefix, inMem, protocol.ConsensusCurrentVersion, genesisBalances, "", crypto.Digest{}, nil, cfg) require.NoError(b, err) // Make Service @@ -148,7 +149,8 @@ func benchenv(t testing.TB, numAccounts, numBlocks int) (ledger, emptyLedger *da const inMem = true cfg := config.GetDefaultLocal() cfg.Archival = true - emptyLedger, err = data.LoadLedger(logging.TestingLog(t), t.Name()+"empty", inMem, protocol.ConsensusCurrentVersion, genesisBalances, "", crypto.Digest{}, nil, cfg) + prefix := t.Name() + "empty" + emptyLedger, err = data.LoadLedger(logging.TestingLog(t), prefix, inMem, protocol.ConsensusCurrentVersion, genesisBalances, "", crypto.Digest{}, nil, cfg) require.NoError(t, err) ledger, err = datatest.FabricateLedger(logging.TestingLog(t), t.Name(), parts, genesisBalances, emptyLedger.LastRound()+basics.Round(numBlocks)) diff --git a/catchup/universalFetcher_test.go b/catchup/universalFetcher_test.go index bf74c95702..836360139f 100644 --- a/catchup/universalFetcher_test.go +++ b/catchup/universalFetcher_test.go @@ -94,7 +94,6 @@ func TestUGetBlockHTTP(t *testing.T) { blockServiceConfig := config.GetDefaultLocal() blockServiceConfig.EnableBlockService = true - blockServiceConfig.EnableBlockServiceFallbackToArchiver = false net := &httpTestPeerSource{} ls := rpcs.MakeBlockService(logging.Base(), blockServiceConfig, ledger, net, "test genesisID") diff --git a/cmd/algod/main.go b/cmd/algod/main.go index c9cdc1fe6b..5450ff5b64 100644 --- a/cmd/algod/main.go +++ b/cmd/algod/main.go @@ -26,8 +26,6 @@ import ( "strings" "time" - "github.com/gofrs/flock" - "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/daemon/algod" @@ -40,6 +38,7 @@ import ( "github.com/algorand/go-algorand/util" "github.com/algorand/go-algorand/util/metrics" "github.com/algorand/go-algorand/util/tokens" + "github.com/gofrs/flock" "github.com/algorand/go-deadlock" ) @@ -93,11 +92,13 @@ func run() int { baseHeartbeatEvent.Info.Branch = version.Branch baseHeartbeatEvent.Info.CommitHash = version.GetCommitHash() + // -b will print only the git branch and then exit if *branchCheck { fmt.Println(config.Branch) return 0 } + // -c will print only the release channel and then exit if *channelCheck { fmt.Println(config.Channel) return 0 @@ -115,24 +116,13 @@ func run() int { } genesisPath := *genesisFile - if genesisPath == "" { - genesisPath = filepath.Join(dataDir, config.GenesisJSONFile) - } - - // Load genesis - genesisText, err := os.ReadFile(genesisPath) - if err != nil { - fmt.Fprintf(os.Stderr, "Cannot read genesis file %s: %v\n", genesisPath, err) - return 1 - } - - var genesis bookkeeping.Genesis - err = protocol.DecodeJSON(genesisText, &genesis) + genesis, genesisText, err := loadGenesis(dataDir, genesisPath) if err != nil { - fmt.Fprintf(os.Stderr, "Cannot parse genesis file %s: %v\n", genesisPath, err) + fmt.Fprintf(os.Stderr, "Error loading genesis file (%s): %v", genesisPath, err) return 1 } + // -G will print only the genesis ID and then exit if *genesisPrint { fmt.Println(genesis.ID()) return 0 @@ -286,12 +276,12 @@ func run() int { // make sure that the format of each entry is valid: for idx, peer := range peerOverrideArray { - url, err := network.ParseHostOrURL(peer) - if err != nil { + addr, addrErr := network.ParseHostOrURLOrMultiaddr(peer) + if addrErr != nil { fmt.Fprintf(os.Stderr, "Provided command line parameter '%s' is not a valid host:port pair\n", peer) return 1 } - peerOverrideArray[idx] = url.Host + peerOverrideArray[idx] = addr } } @@ -453,3 +443,19 @@ func resolveDataDir() string { } return dir } + +func loadGenesis(dataDir string, genesisPath string) (bookkeeping.Genesis, string, error) { + if genesisPath == "" { + genesisPath = filepath.Join(dataDir, config.GenesisJSONFile) + } + genesisText, err := os.ReadFile(genesisPath) + if err != nil { + return bookkeeping.Genesis{}, "", err + } + var genesis bookkeeping.Genesis + err = protocol.DecodeJSON(genesisText, &genesis) + if err != nil { + return bookkeeping.Genesis{}, "", err + } + return genesis, string(genesisText), nil +} diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go index 5efecfcfb6..40b93a7f94 100644 --- a/cmd/goal/clerk.go +++ b/cmd/goal/clerk.go @@ -69,6 +69,7 @@ var ( requestFilename string requestOutFilename string + simulateStartRound uint64 simulateAllowEmptySignatures bool simulateAllowMoreLogging bool simulateAllowMoreOpcodeBudget bool @@ -164,6 +165,7 @@ func init() { simulateCmd.Flags().StringVar(&requestFilename, "request", "", "Simulate request object to run. Mutually exclusive with --txfile") simulateCmd.Flags().StringVar(&requestOutFilename, "request-only-out", "", "Filename for writing simulate request object. If provided, the command will only write the request object and exit. No simulation will happen") simulateCmd.Flags().StringVarP(&outFilename, "result-out", "o", "", "Filename for writing simulation result") + simulateCmd.Flags().Uint64Var(&simulateStartRound, "round", 0, "Specify the round after which the simulation will take place. If not specified, the simulation will take place after the latest round.") simulateCmd.Flags().BoolVar(&simulateAllowEmptySignatures, "allow-empty-signatures", false, "Allow transactions without signatures to be simulated as if they had correct signatures") simulateCmd.Flags().BoolVar(&simulateAllowMoreLogging, "allow-more-logging", false, "Lift the limits on log opcode during simulation") simulateCmd.Flags().BoolVar(&simulateAllowMoreOpcodeBudget, "allow-more-opcode-budget", false, "Apply max extra opcode budget for apps per transaction group (default 320000) during simulation") @@ -1008,9 +1010,35 @@ func assembleFile(fname string, printWarnings bool) (program []byte) { return ops.Program } -func assembleFileWithMap(fname string, printWarnings bool) ([]byte, logic.SourceMap) { - ops := assembleFileImpl(fname, printWarnings) - return ops.Program, logic.GetSourceMap([]string{fname}, ops.OffsetToLine) +func assembleFileWithMap(sourceFile string, outFile string, printWarnings bool) ([]byte, logic.SourceMap, error) { + ops := assembleFileImpl(sourceFile, printWarnings) + pathToSourceFromSourceMap, err := determinePathToSourceFromSourceMap(sourceFile, outFile) + if err != nil { + return nil, logic.SourceMap{}, err + } + return ops.Program, logic.GetSourceMap([]string{pathToSourceFromSourceMap}, ops.OffsetToSource), nil +} + +func determinePathToSourceFromSourceMap(sourceFile string, outFile string) (string, error) { + if sourceFile == stdinFileNameValue { + return "", nil + } + sourceFileAbsolute, err := filepath.Abs(sourceFile) + if err != nil { + return "", fmt.Errorf("could not determine absolute path to source file '%s': %w", sourceFile, err) + } + if outFile == stdoutFilenameValue { + return sourceFileAbsolute, nil + } + outFileAbsolute, err := filepath.Abs(outFile) + if err != nil { + return "", fmt.Errorf("could not determine absolute path to output file '%s': %w", outFile, err) + } + pathToSourceFromSourceMap, err := filepath.Rel(filepath.Dir(outFileAbsolute), sourceFileAbsolute) + if err != nil { + return "", fmt.Errorf("could not determine path from source map to source: %w", err) + } + return pathToSourceFromSourceMap, nil } func disassembleFile(fname, outname string) { @@ -1068,7 +1096,10 @@ var compileCmd = &cobra.Command{ } } shouldPrintAdditionalInfo := outname != stdoutFilenameValue - program, sourceMap := assembleFileWithMap(fname, true) + program, sourceMap, err := assembleFileWithMap(fname, outname, true) + if err != nil { + reportErrorf("Could not assemble: %s", err) + } outblob := program if signProgram { dataDir := datadir.EnsureSingleDataDir() @@ -1285,6 +1316,7 @@ var simulateCmd = &cobra.Command{ Txns: txgroup, }, }, + Round: basics.Round(simulateStartRound), AllowEmptySignatures: simulateAllowEmptySignatures, AllowMoreLogging: simulateAllowMoreLogging, AllowUnnamedResources: simulateAllowUnnamedResources, @@ -1310,6 +1342,7 @@ var simulateCmd = &cobra.Command{ Txns: txgroup, }, }, + Round: basics.Round(simulateStartRound), AllowEmptySignatures: simulateAllowEmptySignatures, AllowMoreLogging: simulateAllowMoreLogging, AllowUnnamedResources: simulateAllowUnnamedResources, diff --git a/cmd/goal/clerk_test.go b/cmd/goal/clerk_test.go new file mode 100644 index 0000000000..ab63e699ea --- /dev/null +++ b/cmd/goal/clerk_test.go @@ -0,0 +1,100 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package main + +import ( + "path/filepath" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func abs(t *testing.T, path string) string { + t.Helper() + absPath, err := filepath.Abs(path) + require.NoError(t, err) + return absPath +} + +func TestDeterminePathToSourceFromSourceMap(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testCases := []struct { + name string + sourceFile string + outFile string + + expectedPath string + }{ + { + name: "same directory", + sourceFile: filepath.FromSlash("data/program.teal"), + outFile: filepath.FromSlash("data/program.teal.tok"), + expectedPath: "program.teal", + }, + { + name: "output one level up", + sourceFile: filepath.FromSlash("data/program.teal"), + outFile: filepath.FromSlash("data/output/program.teal.tok"), + expectedPath: filepath.FromSlash("../program.teal"), + }, + { + name: "output one level down", + sourceFile: filepath.FromSlash("data/program.teal"), + outFile: "program.teal.tok", + expectedPath: filepath.FromSlash("data/program.teal"), + }, + { + name: "input stdin", + sourceFile: stdinFileNameValue, + outFile: "program.teal.tok", + expectedPath: "", + }, + { + name: "output stdout", + sourceFile: filepath.FromSlash("data/program.teal"), + outFile: stdoutFilenameValue, + expectedPath: abs(t, filepath.FromSlash("data/program.teal")), + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + sources := []string{tc.sourceFile} + if tc.sourceFile != stdinFileNameValue { + sources = append(sources, abs(t, tc.sourceFile)) + } + outs := []string{tc.outFile} + if tc.outFile != stdoutFilenameValue { + outs = append(outs, abs(t, tc.outFile)) + } + + for sourceIndex, source := range sources { + for outIndex, out := range outs { + actualPath, err := determinePathToSourceFromSourceMap(source, out) + require.NoError(t, err, "sourceIndex: %d, outIndex: %d", sourceIndex, outIndex) + require.Equal(t, tc.expectedPath, actualPath, "sourceIndex: %d, outIndex: %d", sourceIndex, outIndex) + } + } + }) + } +} diff --git a/cmd/goal/network.go b/cmd/goal/network.go index 5c32b68e36..a63bb13f58 100644 --- a/cmd/goal/network.go +++ b/cmd/goal/network.go @@ -28,6 +28,7 @@ import ( "github.com/algorand/go-algorand/cmd/util/datadir" "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/gen" "github.com/algorand/go-algorand/netdeploy" "github.com/algorand/go-algorand/util" ) @@ -40,11 +41,11 @@ var noImportKeys bool var noClean bool var devModeOverride bool var startOnCreation bool +var pregenDir string func init() { networkCmd.AddCommand(networkCreateCmd) networkCmd.PersistentFlags().StringVarP(&networkRootDir, "rootdir", "r", "", "Root directory for the private network directories") - networkCmd.MarkPersistentFlagRequired("rootdir") networkCreateCmd.Flags().StringVarP(&networkName, "network", "n", "", "Specify the name to use for the private network") networkCreateCmd.Flags().StringVarP(&networkTemplateFile, "template", "t", "", "Specify the path to the template file for the network") @@ -52,14 +53,34 @@ func init() { networkCreateCmd.Flags().BoolVar(&noClean, "noclean", false, "Prevents auto-cleanup on error - for diagnosing problems") networkCreateCmd.Flags().BoolVar(&devModeOverride, "devMode", false, "Forces the configuration to enable DevMode, returns an error if the template is not compatible with DevMode.") networkCreateCmd.Flags().BoolVarP(&startOnCreation, "start", "s", false, "Automatically start the network after creating it.") + networkCreateCmd.Flags().StringVarP(&pregenDir, "pregendir", "p", "", "Specify the path to the directory with pregenerated genesis.json, root and partkeys to import into the network directory. By default, the genesis.json and keys will be generated on start. This should only be used on private networks.") + networkCreateCmd.MarkFlagRequired("rootdir") + networkCmd.AddCommand(networkStartCmd) networkStartCmd.Flags().StringVarP(&startNode, "node", "n", "", "Specify the name of a specific node to start") + networkStartCmd.MarkFlagRequired("rootdir") - networkCmd.AddCommand(networkStartCmd) networkCmd.AddCommand(networkRestartCmd) + networkRestartCmd.MarkFlagRequired("rootdir") + networkCmd.AddCommand(networkStopCmd) + networkStopCmd.MarkFlagRequired("rootdir") + networkCmd.AddCommand(networkStatusCmd) + networkStatusCmd.MarkFlagRequired("rootdir") + networkCmd.AddCommand(networkDeleteCmd) + networkDeleteCmd.MarkFlagRequired("rootdir") + + networkCmd.AddCommand(networkPregenCmd) + networkPregenCmd.Flags().StringVarP(&networkTemplateFile, "template", "t", "", "Specify the path to the template file for the network") + networkPregenCmd.Flags().StringVarP(&pregenDir, "pregendir", "p", "", "Specify the path to the directory to export genesis.json, root and partkey files. This should only be used on private networks.") + networkPregenCmd.MarkFlagRequired("pregendir") + // Hide rootdir flag as it is unused and will error if used with this command. + networkPregenCmd.SetHelpFunc(func(command *cobra.Command, strings []string) { + _ = command.Flags().MarkHidden("rootdir") + command.Parent().HelpFunc()(command, strings) + }) } var networkCmd = &cobra.Command{ @@ -112,6 +133,18 @@ var networkCreateCmd = &cobra.Command{ reportErrorf(infoNetworkAlreadyExists, networkRootDir) } + // If pregendir is specified, copy files over + if pregenDir != "" { + pregenDir, err = filepath.Abs(pregenDir) + if err != nil { + panic(err) + } + err = util.CopyFolder(pregenDir, networkRootDir) + if err != nil { + panic(err) + } + } + binDir, err := util.ExeDir() if err != nil { panic(err) @@ -246,3 +279,65 @@ var networkDeleteCmd = &cobra.Command{ reportInfof(infoNetworkDeleted, networkRootDir) }, } + +var networkPregenCmd = &cobra.Command{ + Use: "pregen", + Short: "Pregenerate private network", + Long: "Pregenerates the root and participation keys for a private network. The pregen directory can then be passed to the 'goal network create' to start the network more quickly.", + Args: validateNoPosArgsFn, + Run: func(cmd *cobra.Command, _ []string) { + var err error + if networkRootDir != "" { + reportErrorf("This command does not take a network directory as an argument. Use --pregendir flag instead.") + } + + pregenDir, err = filepath.Abs(pregenDir) + if err != nil { + panic(err) + } + + var templateReader io.Reader + + if networkTemplateFile == "" { + templateReader = strings.NewReader(defaultNetworkTemplate) + } else { + networkTemplateFile, err = filepath.Abs(networkTemplateFile) + if err != nil { + panic(err) + } + file, osErr := os.Open(networkTemplateFile) + if osErr != nil { + reportErrorf(errorCreateNetwork, osErr) + } + + defer file.Close() + templateReader = file + } + + // Make sure target directory does not exist or is empty + if util.FileExists(pregenDir) && !util.IsEmpty(pregenDir) { + reportErrorf(infoNetworkAlreadyExists, pregenDir) + } + + var template netdeploy.NetworkTemplate + err = netdeploy.LoadTemplateFromReader(templateReader, &template) + if err != nil { + reportErrorf("Error in loading template: %v\n", err) + } + + dataDir := datadir.MaybeSingleDataDir() + var consensus config.ConsensusProtocols + if dataDir != "" { + // try to load the consensus from there. If there is none, we can just use the built in one. + consensus, _ = config.PreloadConfigurableConsensusProtocols(dataDir) + } + if err = template.Validate(); err != nil { + reportErrorf("Error in template validation: %v\n", err) + } + + err = gen.GenerateGenesisFiles(template.Genesis, config.Consensus.Merge(consensus), pregenDir, os.Stdout) + if err != nil { + reportErrorf("Cannot write genesis files: %s", err) + } + }, +} diff --git a/cmd/goal/node.go b/cmd/goal/node.go index 7d5f2df148..1258001ed1 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -61,6 +61,7 @@ var newNodeFullConfig bool var watchMillisecond uint64 var abortCatchup bool var fastCatchupForce bool +var minCatchupRounds uint64 const catchpointURL = "https://algorand-catchpoints.s3.us-east-2.amazonaws.com/channel/%s/latest.catchpoint" @@ -116,6 +117,7 @@ func init() { catchupCmd.Flags().BoolVarP(&abortCatchup, "abort", "x", false, "Aborts the current catchup process") catchupCmd.Flags().BoolVar(&fastCatchupForce, "force", false, "Forces fast catchup with implicit catchpoint to start without a consent prompt") + catchupCmd.Flags().Uint64VarP(&minCatchupRounds, "min", "m", 0, "Catchup only if the catchpoint would advance the node by the specified minimum number of rounds") } @@ -161,22 +163,38 @@ var catchupCmd = &cobra.Command{ Example: "goal node catchup 6500000#1234567890ABCDEF01234567890ABCDEF0\tStart catching up to round 6500000 with the provided catchpoint\ngoal node catchup --abort\t\t\t\t\tAbort the current catchup", Args: catchpointCmdArgument, Run: func(cmd *cobra.Command, args []string) { + var catchpoint string + // assume first positional parameter is the catchpoint + if len(args) != 0 { + catchpoint = args[0] + } datadir.OnDataDirs(func(dataDir string) { - if !abortCatchup && len(args) == 0 { - client := ensureAlgodClient(dataDir) + client := ensureAlgodClient(dataDir) + + if abortCatchup { + err := client.AbortCatchup() + if err != nil { + reportErrorf(errorNodeStatus, err) + } + return + } + + // lookup missing catchpoint + if catchpoint == "" { vers, err := client.AlgodVersions() if err != nil { reportErrorf(errorNodeStatus, err) } genesis := strings.Split(vers.GenesisID, "-")[0] URL := fmt.Sprintf(catchpointURL, genesis) - label, err := getMissingCatchpointLabel(URL) + catchpoint, err = getMissingCatchpointLabel(URL) if err != nil { reportErrorf(errorCatchpointLabelMissing, errorUnableToLookupCatchpointLabel, err.Error()) } - args = append(args, label) + + // Prompt user to confirm using an implicit catchpoint. if !fastCatchupForce { - fmt.Printf(nodeConfirmImplicitCatchpoint, label) + fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') text = strings.Replace(text, "\n", "", -1) @@ -185,7 +203,14 @@ var catchupCmd = &cobra.Command{ } } } - catchup(dataDir, args) + + resp, err := client.Catchup(catchpoint, minCatchupRounds) + if err != nil { + reportErrorf(errorNodeStatus, err) + } + if resp.CatchupMessage != catchpoint { + reportInfof("node response: %s", resp.CatchupMessage) + } }) }, } @@ -718,21 +743,6 @@ var createCmd = &cobra.Command{ }, } -func catchup(dataDir string, args []string) { - client := ensureAlgodClient(datadir.EnsureSingleDataDir()) - if abortCatchup { - err := client.AbortCatchup() - if err != nil { - reportErrorf(errorNodeStatus, err) - } - return - } - err := client.Catchup(args[0]) - if err != nil { - reportErrorf(errorNodeStatus, err) - } -} - // verifyPeerDialArg verifies that the peers provided in peerDial are valid peers. func verifyPeerDialArg() bool { if peerDial == "" { @@ -741,7 +751,7 @@ func verifyPeerDialArg() bool { // make sure that the format of each entry is valid: for _, peer := range strings.Split(peerDial, ";") { - _, err := network.ParseHostOrURL(peer) + _, err := network.ParseHostOrURLOrMultiaddr(peer) if err != nil { reportErrorf("Provided peer '%s' is not a valid peer address : %v", peer, err) return false diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go index 9c0bbd86bd..58d32705b2 100644 --- a/cmd/opdoc/opdoc.go +++ b/cmd/opdoc/opdoc.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "io" + "math" "os" "sort" "strings" @@ -29,8 +30,6 @@ import ( "github.com/algorand/go-algorand/protocol" ) -var docVersion = 9 - // OpImmediateNote returns a short string about immediate data which follows the op byte func opImmediateNoteSyntaxMarkdown(name string, oids []logic.OpImmediateDetails) string { if len(oids) == 0 { @@ -63,11 +62,11 @@ func opImmediateNoteEncoding(opcode byte, oids []logic.OpImmediateDetails) strin return fmt.Sprintf("0x%02x {%s}", opcode, strings.Join(notes, "}, {")) } -func opGroupMarkdownTable(names []string, out io.Writer) { +func opGroupMarkdownTable(names []string, out io.Writer, version uint64) { fmt.Fprint(out, `| Opcode | Description | | - | -- | `) - opSpecs := logic.OpsByName[docVersion] + opSpecs := logic.OpsByName[version] for _, opname := range names { spec, ok := opSpecs[opname] if !ok { @@ -113,20 +112,20 @@ func integerConstantsTableMarkdown(out io.Writer) { out.Write([]byte("\n")) } -func fieldGroupMarkdown(out io.Writer, group *logic.FieldGroup) { +func fieldGroupMarkdown(out io.Writer, group *logic.FieldGroup, version uint64) { showTypes := false showVers := false - opVer := uint64(0) + opVer := uint64(math.MaxUint64) for _, name := range group.Names { spec, ok := group.SpecByName(name) // reminder: group.Names can be "sparse" See: logic.TxnaFields - if !ok { + if !ok || spec.Version() > version { continue } if spec.Type().Typed() { showTypes = true } - if opVer == uint64(0) { + if opVer == math.MaxUint64 { opVer = spec.Version() } else if opVer != spec.Version() { showVers = true @@ -147,7 +146,7 @@ func fieldGroupMarkdown(out io.Writer, group *logic.FieldGroup) { fmt.Fprint(out, headers, widths) for i, name := range group.Names { spec, ok := group.SpecByName(name) - if !ok { + if !ok || spec.Version() > version { continue } str := fmt.Sprintf("| %d | %s", i, markdownTableEscape(name)) @@ -212,7 +211,7 @@ func stackMarkdown(op *logic.OpSpec) string { return out + "\n" } -func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bool) (err error) { +func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bool, version uint64) (err error) { deets := logic.OpImmediateDetailsFromSpec(*op) @@ -230,26 +229,9 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo fmt.Fprintf(out, "\n## %s\n\n%s%s\n%s", op.Name, syntax, encoding, stackEffects) fmt.Fprintf(out, "- %s\n", logic.OpDoc(op.Name)) - // if cost changed with versions print all of them - costs := logic.OpAllCosts(op.Name) - if len(costs) > 1 { - fmt.Fprintf(out, "- **Cost**:\n") - for _, cost := range costs { - if cost.From == cost.To { - fmt.Fprintf(out, " - %s (v%d)\n", cost.Cost, cost.To) - } else { - if cost.To < docVersion { - fmt.Fprintf(out, " - %s (v%d - v%d)\n", cost.Cost, cost.From, cost.To) - } else { - fmt.Fprintf(out, " - %s (since v%d)\n", cost.Cost, cost.From) - } - } - } - } else { - cost := costs[0].Cost - if cost != "1" { - fmt.Fprintf(out, "- **Cost**: %s\n", cost) - } + cost := op.DocCost(version) + if cost != "1" { + fmt.Fprintf(out, "- **Cost**: %s\n", cost) } if op.Version > 1 { fmt.Fprintf(out, "- Availability: v%d\n", op.Version) @@ -262,7 +244,7 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo group := op.OpDetails.Immediates[i].Group if group != nil && group.Doc != "" && !groupDocWritten[group.Name] { fmt.Fprintf(out, "\n### %s\n\n%s\n\n", group.Name, group.Doc) - fieldGroupMarkdown(out, group) + fieldGroupMarkdown(out, group, version) groupDocWritten[group.Name] = true } } @@ -273,17 +255,20 @@ func opToMarkdown(out io.Writer, op *logic.OpSpec, groupDocWritten map[string]bo return nil } -func opsToMarkdown(out io.Writer) (err error) { - out.Write([]byte("# Opcodes\n\nOps have a 'cost' of 1 unless otherwise specified.\n\n")) - opSpecs := logic.OpcodesByVersion(uint64(docVersion)) +func opsToMarkdown(out io.Writer, version uint64) error { + _, err := out.Write([]byte(fmt.Sprintf("# v%d Opcodes\n\nOps have a 'cost' of 1 unless otherwise specified.\n\n", version))) + if err != nil { + return err + } + opSpecs := logic.OpcodesByVersion(version) written := make(map[string]bool) for i := range opSpecs { - err = opToMarkdown(out, &opSpecs[i], written) + err := opToMarkdown(out, &opSpecs[i], written, version) if err != nil { - return + return err } } - return + return nil } // OpRecord is a consolidated record of things about an Op @@ -297,6 +282,8 @@ type OpRecord struct { ArgEnum []string `json:",omitempty"` ArgEnumTypes []string `json:",omitempty"` + DocCost string + Doc string DocExtra string `json:",omitempty"` ImmediateNote []logic.OpImmediateDetails `json:",omitempty"` @@ -342,7 +329,7 @@ func (nt namedType) boundString() string { // LanguageSpec records the ops of the language at some version type LanguageSpec struct { - EvalMaxVersion int + Version uint64 LogicSigVersion uint64 NamedTypes []namedType Ops []OpRecord @@ -369,12 +356,12 @@ func typeStrings(types logic.StackTypes) []string { return out } -func fieldsAndTypes(group logic.FieldGroup) ([]string, []string) { +func fieldsAndTypes(group logic.FieldGroup, version uint64) ([]string, []string) { // reminder: group.Names can be "sparse" See: logic.TxnaFields fields := make([]string, 0, len(group.Names)) types := make([]logic.StackType, 0, len(group.Names)) for _, name := range group.Names { - if spec, ok := group.SpecByName(name); ok { + if spec, ok := group.SpecByName(name); ok && spec.Version() <= version { fields = append(fields, name) types = append(types, spec.Type()) } @@ -382,46 +369,46 @@ func fieldsAndTypes(group logic.FieldGroup) ([]string, []string) { return fields, typeStrings(types) } -func argEnums(name string) ([]string, []string) { +func argEnums(name string, version uint64) ([]string, []string) { // reminder: this needs to be manually updated every time // a new opcode is added with an associated FieldGroup // it'd be nice to have this auto-update switch name { case "txn", "gtxn", "gtxns", "itxn", "gitxn": - return fieldsAndTypes(logic.TxnFields) + return fieldsAndTypes(logic.TxnFields, version) case "itxn_field": // itxn_field does not *return* a type depending on its immediate. It *takes* it. // but until a consumer cares, ArgEnumTypes will be overloaded for that meaning. - return fieldsAndTypes(logic.ItxnSettableFields) + return fieldsAndTypes(logic.ItxnSettableFields, version) case "global": - return fieldsAndTypes(logic.GlobalFields) + return fieldsAndTypes(logic.GlobalFields, version) case "txna", "gtxna", "gtxnsa", "txnas", "gtxnas", "gtxnsas", "itxna", "gitxna": - return fieldsAndTypes(logic.TxnArrayFields) + return fieldsAndTypes(logic.TxnArrayFields, version) case "asset_holding_get": - return fieldsAndTypes(logic.AssetHoldingFields) + return fieldsAndTypes(logic.AssetHoldingFields, version) case "asset_params_get": - return fieldsAndTypes(logic.AssetParamsFields) + return fieldsAndTypes(logic.AssetParamsFields, version) case "app_params_get": - return fieldsAndTypes(logic.AppParamsFields) + return fieldsAndTypes(logic.AppParamsFields, version) case "acct_params_get": - return fieldsAndTypes(logic.AcctParamsFields) + return fieldsAndTypes(logic.AcctParamsFields, version) case "block": - return fieldsAndTypes(logic.BlockFields) + return fieldsAndTypes(logic.BlockFields, version) case "json_ref": - return fieldsAndTypes(logic.JSONRefTypes) + return fieldsAndTypes(logic.JSONRefTypes, version) case "base64_decode": - return fieldsAndTypes(logic.Base64Encodings) + return fieldsAndTypes(logic.Base64Encodings, version) case "vrf_verify": - return fieldsAndTypes(logic.VrfStandards) + return fieldsAndTypes(logic.VrfStandards, version) case "ecdsa_pk_recover", "ecdsa_verify", "ecdsa_pk_decompress": - return fieldsAndTypes(logic.EcdsaCurves) + return fieldsAndTypes(logic.EcdsaCurves, version) default: return nil, nil } } -func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType) *LanguageSpec { - opSpecs := logic.OpcodesByVersion(uint64(docVersion)) +func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType, version uint64) *LanguageSpec { + opSpecs := logic.OpcodesByVersion(version) records := make([]OpRecord, len(opSpecs)) for i, spec := range opSpecs { records[i].Opcode = spec.Opcode @@ -429,7 +416,8 @@ func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType) *La records[i].Args = typeStrings(spec.Arg.Types) records[i].Returns = typeStrings(spec.Return.Types) records[i].Size = spec.OpDetails.Size - records[i].ArgEnum, records[i].ArgEnumTypes = argEnums(spec.Name) + records[i].DocCost = spec.DocCost(version) + records[i].ArgEnum, records[i].ArgEnumTypes = argEnums(spec.Name, version) records[i].Doc = strings.ReplaceAll(logic.OpDoc(spec.Name), "
", "\n") records[i].DocExtra = logic.OpDocExtra(spec.Name) records[i].ImmediateNote = logic.OpImmediateDetailsFromSpec(spec) @@ -438,7 +426,7 @@ func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType) *La } return &LanguageSpec{ - EvalMaxVersion: docVersion, + Version: version, LogicSigVersion: config.Consensus[protocol.ConsensusCurrentVersion].LogicSigVersion, NamedTypes: namedTypes, Ops: records, @@ -448,30 +436,26 @@ func buildLanguageSpec(opGroups map[string][]string, namedTypes []namedType) *La func create(file string) *os.File { f, err := os.Create(file) if err != nil { - fmt.Fprintf(os.Stderr, "Unable to create '%s': %v", file, err) + fmt.Fprintf(os.Stderr, "Unable to create '%s': %v\n", file, err) os.Exit(1) } return f } func main() { - opcodesMd := create("TEAL_opcodes.md") - opsToMarkdown(opcodesMd) - opcodesMd.Close() + const docVersion = uint64(10) + opGroups := make(map[string][]string, len(logic.OpSpecs)) for grp, names := range logic.OpGroups { fname := fmt.Sprintf("%s.md", grp) fname = strings.ReplaceAll(fname, " ", "_") fout := create(fname) - opGroupMarkdownTable(names, fout) + opGroupMarkdownTable(names, fout, docVersion) fout.Close() for _, opname := range names { opGroups[opname] = append(opGroups[opname], grp) } } - constants := create("named_integer_constants.md") - integerConstantsTableMarkdown(constants) - constants.Close() named := make([]namedType, 0, len(logic.AllStackTypes)) for abbr, t := range logic.AllStackTypes { @@ -484,6 +468,10 @@ func main() { } sort.Slice(named, func(i, j int) bool { return named[i].Name > named[j].Name }) + constants := create("named_integer_constants.md") + integerConstantsTableMarkdown(constants) + constants.Close() + namedStackTypes := create("named_stack_types.md") namedStackTypesMarkdown(namedStackTypes, named) namedStackTypes.Close() @@ -494,25 +482,37 @@ func main() { for _, imm := range spec.OpDetails.Immediates { if imm.Group != nil && !written[imm.Group.Name] { out := create(strings.ToLower(imm.Group.Name) + "_fields.md") - fieldGroupMarkdown(out, imm.Group) + fieldGroupMarkdown(out, imm.Group, docVersion) out.Close() written[imm.Group.Name] = true } } } - langspecjs := create("langspec.json") - enc := json.NewEncoder(langspecjs) - enc.SetIndent("", " ") - err := enc.Encode(buildLanguageSpec(opGroups, named)) - if err != nil { - panic(err.Error()) - } - langspecjs.Close() - tealtm := create("teal.tmLanguage.json") - enc = json.NewEncoder(tealtm) + enc := json.NewEncoder(tealtm) enc.SetIndent("", " ") - enc.Encode(buildSyntaxHighlight()) + if err := enc.Encode(buildSyntaxHighlight(docVersion)); err != nil { + fmt.Fprintf(os.Stderr, "error encoding teal.tmLanguage.json: % v\n", err) + os.Exit(1) + } tealtm.Close() + + for v := uint64(1); v <= docVersion; v++ { + langspecjs := create(fmt.Sprintf("langspec_v%d.json", v)) + enc := json.NewEncoder(langspecjs) + enc.SetIndent("", " ") + if err := enc.Encode(buildLanguageSpec(opGroups, named, v)); err != nil { + fmt.Fprintf(os.Stderr, "error encoding langspec JSON for version %d: %v\n", v, err) + os.Exit(1) + } + langspecjs.Close() + + opcodesMd := create(fmt.Sprintf("TEAL_opcodes_v%d.md", v)) + if err := opsToMarkdown(opcodesMd, v); err != nil { + fmt.Fprintf(os.Stderr, "error creating markdown for version %d: %v\n", v, err) + os.Exit(1) + } + opcodesMd.Close() + } } diff --git a/cmd/opdoc/tmLanguage.go b/cmd/opdoc/tmLanguage.go index a24c3dfd8f..fb14381016 100644 --- a/cmd/opdoc/tmLanguage.go +++ b/cmd/opdoc/tmLanguage.go @@ -52,7 +52,7 @@ type pattern struct { Patterns []pattern `json:"patterns,omitempty"` } -func buildSyntaxHighlight() *tmLanguage { +func buildSyntaxHighlight(version uint64) *tmLanguage { tm := tmLanguage{ Schema: "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", Name: "Algorand TEAL", @@ -126,11 +126,17 @@ func buildSyntaxHighlight() *tmLanguage { allNamedFields = append(allNamedFields, logic.TxnTypeNames[:]...) allNamedFields = append(allNamedFields, logic.OnCompletionNames[:]...) accumulated := make(map[string]bool) - opSpecs := logic.OpcodesByVersion(uint64(docVersion)) + opSpecs := logic.OpcodesByVersion(version) for _, spec := range opSpecs { for _, imm := range spec.OpDetails.Immediates { if imm.Group != nil && !accumulated[imm.Group.Name] { - allNamedFields = append(allNamedFields, imm.Group.Names...) + for _, name := range imm.Group.Names { + spec, ok := imm.Group.SpecByName(name) + if !ok || spec.Version() > version { + continue + } + allNamedFields = append(allNamedFields, name) + } accumulated[imm.Group.Name] = true } } diff --git a/cmd/tealdbg/debugger.go b/cmd/tealdbg/debugger.go index 3639a41afe..86674a45d9 100644 --- a/cmd/tealdbg/debugger.go +++ b/cmd/tealdbg/debugger.go @@ -85,11 +85,11 @@ func MakeDebugger() *Debugger { } type programMeta struct { - name string - program []byte - source string - offsetToLine map[int]int - states AppState + name string + program []byte + source string + offsetToSource map[int]logic.SourceLocation + states AppState } // debugConfig contains information about control execution and breakpoints. @@ -160,11 +160,11 @@ type session struct { disassembly string lines []string - programName string - program []byte - source string - offsetToLine map[int]int // pc to source line - pcOffset map[int]int // disassembly line to pc + programName string + program []byte + source string + offsetToSource map[int]logic.SourceLocation // pc to source line/col + pcOffset map[int]int // disassembly line to pc breakpoints []breakpoint line atomicInt @@ -364,28 +364,27 @@ func (s *session) GetSourceMap() ([]byte, error) { lines := make([]string, len(s.lines)) const targetCol int = 0 const sourceIdx int = 0 - sourceLine := 0 - const sourceCol int = 0 - prevSourceLine := 0 + prevLoc := logic.SourceLocation{Line: 0, Column: 0} // the very first entry is needed by CDT - lines[0] = logic.MakeSourceMapLine(targetCol, sourceIdx, 0, sourceCol) + lines[0] = logic.MakeSourceMapLine(targetCol, sourceIdx, 0, 0) for targetLine := 1; targetLine < len(s.lines); targetLine++ { if pc, ok := s.pcOffset[targetLine]; ok && pc != 0 { - sourceLine, ok = s.offsetToLine[pc] + source, ok := s.offsetToSource[pc] if !ok { lines[targetLine] = "" } else { - lines[targetLine] = logic.MakeSourceMapLine(targetCol, sourceIdx, sourceLine-prevSourceLine, sourceCol) - prevSourceLine = sourceLine + lines[targetLine] = logic.MakeSourceMapLine(targetCol, sourceIdx, source.Line-prevLoc.Line, source.Column-prevLoc.Column) + prevLoc = source } } else { - delta := 0 + ldelta, cdelta := 0, 0 // the very last empty line, increment by number src number by 1 if targetLine == len(s.lines)-1 { - delta = 1 + ldelta = 1 + cdelta = -prevLoc.Column } - lines[targetLine] = logic.MakeSourceMapLine(targetCol, sourceIdx, delta, sourceCol) + lines[targetLine] = logic.MakeSourceMapLine(targetCol, sourceIdx, ldelta, cdelta) } } @@ -489,7 +488,7 @@ func (d *Debugger) createSession(sid string, disassembly string, line int, pcOff s.programName = meta.name s.program = meta.program s.source = meta.source - s.offsetToLine = meta.offsetToLine + s.offsetToSource = meta.offsetToSource s.pcOffset = pcOffset s.states = meta.states } @@ -513,7 +512,7 @@ func (d *Debugger) AddAdapter(da DebugAdapter) { // SaveProgram stores program, source and offsetToLine for later use func (d *Debugger) SaveProgram( - name string, program []byte, source string, offsetToLine map[int]int, + name string, program []byte, source string, offsetToSource map[int]logic.SourceLocation, states AppState, ) { hash := logic.GetProgramID(program) @@ -523,7 +522,7 @@ func (d *Debugger) SaveProgram( name, program, source, - offsetToLine, + offsetToSource, states, } } diff --git a/cmd/tealdbg/debugger_test.go b/cmd/tealdbg/debugger_test.go index 8bcbd4bbc3..6b6d39e698 100644 --- a/cmd/tealdbg/debugger_test.go +++ b/cmd/tealdbg/debugger_test.go @@ -130,15 +130,15 @@ func createSessionFromSource(t *testing.T, program string) *session { // create a sample disassembly line to pc mapping // this simple source is similar to disassembly except intcblock at the beginning - pcOffset := make(map[int]int, len(ops.OffsetToLine)) - for pc, line := range ops.OffsetToLine { - pcOffset[line+1] = pc + pcOffset := make(map[int]int, len(ops.OffsetToSource)) + for pc, location := range ops.OffsetToSource { + pcOffset[location.Line+1] = pc } s := makeSession(disassembly, 0) s.source = source s.programName = "test" - s.offsetToLine = ops.OffsetToLine + s.offsetToSource = ops.OffsetToSource s.pcOffset = pcOffset return s diff --git a/cmd/tealdbg/local.go b/cmd/tealdbg/local.go index cd319cf6d5..61050348e4 100644 --- a/cmd/tealdbg/local.go +++ b/cmd/tealdbg/local.go @@ -225,16 +225,16 @@ const ( // evaluation is a description of a single debugger run type evaluation struct { - program []byte - source string - offsetToLine map[int]int - name string - groupIndex uint64 - mode modeType - aidx basics.AppIndex - ba apply.Balances - result evalResult - states AppState + program []byte + source string + offsetToSource map[int]logic.SourceLocation + name string + groupIndex uint64 + mode modeType + aidx basics.AppIndex + ba apply.Balances + result evalResult + states AppState } func (e *evaluation) eval(gi int, sep *logic.EvalParams, aep *logic.EvalParams) (pass bool, err error) { @@ -395,7 +395,7 @@ func (r *LocalRunner) Setup(dp *DebugParams) (err error) { } r.runs[i].program = ops.Program if !dp.DisableSourceMap { - r.runs[i].offsetToLine = ops.OffsetToLine + r.runs[i].offsetToSource = ops.OffsetToSource r.runs[i].source = source } } @@ -547,7 +547,7 @@ func (r *LocalRunner) RunAll() error { for i := range r.runs { run := &r.runs[i] if r.debugger != nil { - r.debugger.SaveProgram(run.name, run.program, run.source, run.offsetToLine, run.states) + r.debugger.SaveProgram(run.name, run.program, run.source, run.offsetToSource, run.states) } run.result.pass, run.result.err = run.eval(int(run.groupIndex), sep, aep) diff --git a/cmd/tealdbg/server_test.go b/cmd/tealdbg/server_test.go index 68dad2e781..f9cc023a18 100644 --- a/cmd/tealdbg/server_test.go +++ b/cmd/tealdbg/server_test.go @@ -18,7 +18,6 @@ package main import ( "context" - "math/rand" "strings" "testing" "time" @@ -105,17 +104,9 @@ func tryStartingServerDebug(t *testing.T, ds *DebugServer) (ok bool) { } func serverTestImpl(t *testing.T, run func(t *testing.T, ds *DebugServer) bool, dp *DebugParams) { - maxPortNum := 65000 - minPortNum := 40000 - attempt := 0 - started := false - var ds DebugServer - for attempt < 5 && !started { - port = rand.Intn(maxPortNum-minPortNum) + minPortNum - ds = makeDebugServer("127.0.0.1", port, &mockFactory{}, dp) - started = run(t, &ds) - attempt++ - } + // Using 0 as port should select a random available port. + ds := makeDebugServer("127.0.0.1", 0, &mockFactory{}, dp) + started := run(t, &ds) require.True(t, started) require.NotEmpty(t, ds) diff --git a/config/config.go b/config/config.go index fc2dd30050..ad363f3644 100644 --- a/config/config.go +++ b/config/config.go @@ -89,6 +89,21 @@ const MaxGenesisIDLen = 128 // MaxEvalDeltaTotalLogSize is the maximum size of the sum of all log sizes in a single eval delta. const MaxEvalDeltaTotalLogSize = 1024 +// CatchpointTrackingModeUntracked defines the CatchpointTracking mode that does _not_ track catchpoints +const CatchpointTrackingModeUntracked = -1 + +// CatchpointTrackingModeAutomatic defines the CatchpointTracking mode that automatically determines catchpoint tracking +// and storage based on the Archival property and CatchpointInterval. +const CatchpointTrackingModeAutomatic = 0 + +// CatchpointTrackingModeTracked defines the CatchpointTracking mode that tracks catchpoint +// as long as CatchpointInterval > 0 +const CatchpointTrackingModeTracked = 1 + +// CatchpointTrackingModeStored defines the CatchpointTracking mode that tracks and stores catchpoints +// as long as CatchpointInterval > 0 +const CatchpointTrackingModeStored = 2 + // LoadConfigFromDisk returns a Local config structure based on merging the defaults // with settings loaded from the config file from the custom dir. If the custom file // cannot be loaded, the default config is returned (with the error from loading the diff --git a/config/config_test.go b/config/config_test.go index cc089fc3ab..87b4cc4d43 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -654,3 +654,278 @@ func TestLocal_RecalculateConnectionLimits(t *testing.T) { }) } } + +// Tests that ensureAbsGenesisDir resolves a path to an absolute path, appends the genesis directory, and creates any needed directories +func TestEnsureAbsDir(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + + t1 := filepath.Join(testDirectory, "test1") + t1Abs, err := ensureAbsGenesisDir(t1, "myGenesisID") + require.NoError(t, err) + require.DirExists(t, t1Abs) + require.Equal(t, testDirectory+"/test1/myGenesisID", t1Abs) + + // confirm that relative paths become absolute + t2 := filepath.Join(testDirectory, "test2", "..") + t2Abs, err := ensureAbsGenesisDir(t2, "myGenesisID") + require.NoError(t, err) + require.DirExists(t, t2Abs) + require.Equal(t, testDirectory+"/myGenesisID", t2Abs) +} + +// TestEnsureAndResolveGenesisDirs confirms that paths provided in the config are resolved to absolute paths and are created if relevant +func TestEnsureAndResolveGenesisDirs(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := GetDefaultLocal() + + testDirectory := t.TempDir() + // insert some "Bad" path elements to see them removed when converted to absolute + cfg.TrackerDBDir = filepath.Join(testDirectory, "BAD/../custom_tracker") + cfg.BlockDBDir = filepath.Join(testDirectory, "/BAD/BAD/../../custom_block") + cfg.CrashDBDir = filepath.Join(testDirectory, "custom_crash") + cfg.StateproofDir = filepath.Join(testDirectory, "/RELATIVEPATHS/../RELATIVE/../custom_stateproof") + cfg.CatchpointDir = filepath.Join(testDirectory, "custom_catchpoint") + + paths, err := cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID") + require.NoError(t, err) + + // confirm that the paths are absolute, and contain the genesisID + require.Equal(t, testDirectory+"/custom_tracker/myGenesisID", paths.TrackerGenesisDir) + require.DirExists(t, paths.TrackerGenesisDir) + require.Equal(t, testDirectory+"/custom_block/myGenesisID", paths.BlockGenesisDir) + require.DirExists(t, paths.BlockGenesisDir) + require.Equal(t, testDirectory+"/custom_crash/myGenesisID", paths.CrashGenesisDir) + require.DirExists(t, paths.CrashGenesisDir) + require.Equal(t, testDirectory+"/custom_stateproof/myGenesisID", paths.StateproofGenesisDir) + require.DirExists(t, paths.StateproofGenesisDir) + require.Equal(t, testDirectory+"/custom_catchpoint/myGenesisID", paths.CatchpointGenesisDir) + require.DirExists(t, paths.CatchpointGenesisDir) +} + +// TestEnsureAndResolveGenesisDirs_hierarchy confirms that when only some directories are specified, other directories defer to them +func TestEnsureAndResolveGenesisDirs_hierarchy(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := GetDefaultLocal() + testDirectory := t.TempDir() + paths, err := cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID") + require.NoError(t, err) + // confirm that if only the root is specified, it is used for all directories + require.Equal(t, testDirectory+"/myGenesisID", paths.TrackerGenesisDir) + require.DirExists(t, paths.TrackerGenesisDir) + require.Equal(t, testDirectory+"/myGenesisID", paths.BlockGenesisDir) + require.DirExists(t, paths.BlockGenesisDir) + require.Equal(t, testDirectory+"/myGenesisID", paths.CrashGenesisDir) + require.DirExists(t, paths.CrashGenesisDir) + require.Equal(t, testDirectory+"/myGenesisID", paths.StateproofGenesisDir) + require.DirExists(t, paths.StateproofGenesisDir) + require.Equal(t, testDirectory+"/myGenesisID", paths.CatchpointGenesisDir) + require.DirExists(t, paths.CatchpointGenesisDir) + + cfg = GetDefaultLocal() + testDirectory = t.TempDir() + hot := filepath.Join(testDirectory, "hot") + cold := filepath.Join(testDirectory, "cold") + cfg.HotDataDir = hot + cfg.ColdDataDir = cold + paths, err = cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID") + require.NoError(t, err) + // confirm that if hot/cold are specified, hot/cold are used for appropriate directories + require.Equal(t, hot+"/myGenesisID", paths.TrackerGenesisDir) + require.DirExists(t, paths.TrackerGenesisDir) + require.Equal(t, cold+"/myGenesisID", paths.BlockGenesisDir) + require.DirExists(t, paths.BlockGenesisDir) + require.Equal(t, cold+"/myGenesisID", paths.CrashGenesisDir) + require.DirExists(t, paths.CrashGenesisDir) + require.Equal(t, cold+"/myGenesisID", paths.StateproofGenesisDir) + require.DirExists(t, paths.StateproofGenesisDir) + require.Equal(t, cold+"/myGenesisID", paths.CatchpointGenesisDir) + require.DirExists(t, paths.CatchpointGenesisDir) +} + +// TestEnsureAndResolveGenesisDirsError confirms that if a path can't be created, an error is returned +func TestEnsureAndResolveGenesisDirsError(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := GetDefaultLocal() + + testDirectory := t.TempDir() + // insert some "Bad" path elements to see them removed when converted to absolute + cfg.TrackerDBDir = filepath.Join(testDirectory, "BAD/../custom_tracker") + cfg.BlockDBDir = filepath.Join(testDirectory, "/BAD/BAD/../../custom_block") + cfg.CrashDBDir = filepath.Join(testDirectory, "custom_crash") + cfg.StateproofDir = filepath.Join(testDirectory, "/RELATIVEPATHS/../RELATIVE/../custom_stateproof") + cfg.CatchpointDir = filepath.Join(testDirectory, "custom_catchpoint") + + // first try an error with an empty root dir + paths, err := cfg.EnsureAndResolveGenesisDirs("", "myGenesisID") + require.Empty(t, paths) + require.Error(t, err) + require.Contains(t, err.Error(), "rootDir is required") + + require.NoError(t, os.Chmod(testDirectory, 0200)) + + // now try an error with a root dir that can't be written to + paths, err = cfg.EnsureAndResolveGenesisDirs(testDirectory, "myGenesisID") + require.Empty(t, paths) + require.Error(t, err) + require.Contains(t, err.Error(), "permission denied") +} + +// TestResolveLogPaths confirms that log paths are resolved to the most appropriate data directory of the supplied config +func TestResolveLogPaths(t *testing.T) { + partitiontest.PartitionTest(t) + + // on default settings, the log paths should be in the root directory + cfg := GetDefaultLocal() + log, archive := cfg.ResolveLogPaths("root") + require.Equal(t, "root/node.log", log) + require.Equal(t, "root/node.archive.log", archive) + + // with supplied hot/cold data directories, they resolve to hot/cold + cfg = GetDefaultLocal() + cfg.HotDataDir = "hot" + cfg.ColdDataDir = "cold" + log, archive = cfg.ResolveLogPaths("root") + require.Equal(t, "hot/node.log", log) + require.Equal(t, "cold/node.archive.log", archive) + + // with supplied hot/cold data AND specific paths directories, they resolve to the specific paths + cfg = GetDefaultLocal() + cfg.HotDataDir = "hot" + cfg.ColdDataDir = "cold" + cfg.LogFileDir = "mycoolLogDir" + cfg.LogArchiveDir = "myCoolLogArchive" + log, archive = cfg.ResolveLogPaths("root") + require.Equal(t, "mycoolLogDir/node.log", log) + require.Equal(t, "myCoolLogArchive/node.archive.log", archive) +} + +func TestStoresCatchpoints(t *testing.T) { + partitiontest.PartitionTest(t) + + var tests = []struct { + name string + catchpointTracking int64 + catchpointInterval uint64 + archival bool + expected bool + }{ + { + name: "-1 w/ no catchpoint interval expects false", + catchpointTracking: CatchpointTrackingModeUntracked, + catchpointInterval: 0, + expected: false, + }, + { + name: "-1 expects false", + catchpointTracking: CatchpointTrackingModeUntracked, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "0 expects false", + catchpointTracking: CatchpointTrackingModeAutomatic, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "0 w/ archival expects true", + catchpointTracking: CatchpointTrackingModeAutomatic, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: true, + expected: true, + }, + { + name: "0 w/ archival & catchpointInterval=0 expects false", + catchpointTracking: CatchpointTrackingModeAutomatic, + catchpointInterval: 0, + archival: true, + expected: false, + }, + { + name: "1 expects false", + catchpointTracking: CatchpointTrackingModeTracked, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "1 w/ archival expects true", + catchpointTracking: CatchpointTrackingModeTracked, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: true, + expected: true, + }, + { + name: "1 w/ archival & catchpointInterval=0 expects false", + catchpointTracking: CatchpointTrackingModeTracked, + catchpointInterval: 0, + archival: true, + expected: false, + }, + { + name: "2 w/ catchpointInterval=0 expects false", + catchpointTracking: CatchpointTrackingModeStored, + catchpointInterval: 0, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "2 expects true", + catchpointTracking: CatchpointTrackingModeStored, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: true, + }, + { + name: "99 expects false", + catchpointTracking: 99, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "99 w/ catchpointInterval=0 expects false", + catchpointTracking: 99, + catchpointInterval: 0, + archival: GetDefaultLocal().Archival, + expected: false, + }, + { + name: "27 expects false", + catchpointTracking: 27, + catchpointInterval: GetDefaultLocal().CatchpointInterval, + archival: GetDefaultLocal().Archival, + expected: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cfg := GetDefaultLocal() + cfg.CatchpointTracking = test.catchpointTracking + cfg.CatchpointInterval = test.catchpointInterval + cfg.Archival = test.archival + require.Equal(t, test.expected, cfg.StoresCatchpoints()) + if cfg.StoresCatchpoints() { + require.Equal(t, true, cfg.TracksCatchpoints()) + } + }) + } +} + +func TestTracksCatchpointsWithoutStoring(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := GetDefaultLocal() + cfg.CatchpointTracking = CatchpointTrackingModeTracked + cfg.CatchpointInterval = 10000 + cfg.Archival = false + require.Equal(t, true, cfg.TracksCatchpoints()) + require.Equal(t, false, cfg.StoresCatchpoints()) +} diff --git a/config/consensus.go b/config/consensus.go index 8c217c18c1..190e5ce16a 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -516,6 +516,12 @@ type ConsensusParams struct { // used by agreement for Circulation, and updates the calculation of StateProofOnlineTotalWeight used // by state proofs to use the same method (rather than excluding stake from the top N stakeholders as before). ExcludeExpiredCirculation bool + + // DynamicFilterTimeout indicates whether the filter timeout is set + // dynamically, at run time, according to the recent history of credential + // arrival times or is set to a static value. Even if this flag disables the + // dynamic filter, it will be calculated and logged (but not used). + DynamicFilterTimeout bool } // PaysetCommitType enumerates possible ways for the block header to commit to @@ -1357,11 +1363,16 @@ func initConsensusProtocols() { // ConsensusFuture is used to test features that are implemented // but not yet released in a production protocol version. vFuture := v38 + vFuture.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} vFuture.LogicSigVersion = 10 // When moving this to a release, put a new higher LogicSigVersion here vFuture.EnableLogicSigCostPooling = true + // Setting DynamicFilterTimeout in vFuture will impact e2e test performance + // by reducing round time. Hence, it is commented out for now. + // vFuture.DynamicFilterTimeout = true + Consensus[protocol.ConsensusFuture] = vFuture // vAlphaX versions are an separate series of consensus parameters and versions for alphanet diff --git a/config/localTemplate.go b/config/localTemplate.go index a72ac2fcec..8a5dc0a5d4 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -17,6 +17,7 @@ package config import ( + "fmt" "os" "path/filepath" "strings" @@ -41,42 +42,91 @@ type Local struct { // Version tracks the current version of the defaults so we can migrate old -> new // This is specifically important whenever we decide to change the default value // for an existing parameter. This field tag must be updated any time we add a new version. - Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26" version[27]:"27" version[28]:"28" version[29]:"29" version[30]:"30"` + Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26" version[27]:"27" version[28]:"28" version[29]:"29" version[30]:"30" version[31]:"31"` - // environmental (may be overridden) - // When enabled, stores blocks indefinitely, otherwise, only the most recent blocks - // are being kept around. ( the precise number of recent blocks depends on the consensus parameters ) + // Archival nodes retain a full copy of the block history. Non-Archival nodes will delete old blocks and only retain what's need to properly validate blockchain messages (the precise number of recent blocks depends on the consensus parameters. Currently the last 1321 blocks are required). This means that non-Archival nodes require significantly less storage than Archival nodes. Relays (nodes with a valid NetAddress) are always Archival, regardless of this setting. This may change in the future. If setting this to true for the first time, the existing ledger may need to be deleted to get the historical values stored as the setting only effects current blocks forward. To do this, shutdown the node and delete all .sqlite files within the data/testnet-version directory, except the crash.sqlite file. Restart the node and wait for the node to sync. Archival bool `version[0]:"false"` - // gossipNode.go - // how many peers to propagate to? - GossipFanout int `version[0]:"4"` - NetAddress string `version[0]:""` + // GossipFanout sets the maximum number of peers the node will connect to with outgoing connections. If the list of peers is less than this setting, fewer connections will be made. The node will not connect to the same peer multiple times (with outgoing connections). + GossipFanout int `version[0]:"4"` - // 1 * time.Minute = 60000000000 ns + // NetAddress is the address and/or port on which the relay node listens for incoming connections, or blank to ignore incoming connections. Specify an IP and port or just a port. For example, 127.0.0.1:0 will listen on a random port on the localhost. + NetAddress string `version[0]:""` + + // ReconnectTime is deprecated and unused. ReconnectTime time.Duration `version[0]:"60" version[1]:"60000000000"` - // The public address to connect to that is advertised to other nodes. + // PublicAddress is the public address to connect to that is advertised to other nodes. // For MainNet relays, make sure this entry includes the full SRV host name // plus the publicly-accessible port number. // A valid entry will avoid "self-gossip" and is used for identity exchange - // to deduplicate redundant connections + // to de-duplicate redundant connections PublicAddress string `version[0]:""` + // MaxConnectionsPerIP is the maximum number of connections allowed per IP address. MaxConnectionsPerIP int `version[3]:"30" version[27]:"15"` - // 0 == disable + // PeerPingPeriodSeconds is deprecated and unused. PeerPingPeriodSeconds int `version[0]:"0"` - // for https serving + // TLSCertFile is the certificate file used for the websocket network if povided. TLSCertFile string `version[0]:""` - TLSKeyFile string `version[0]:""` - // Logging + // TLSKeyFile is the key file used for the websocket network if povided. + TLSKeyFile string `version[0]:""` + + // BaseLoggerDebugLevel specifies the logging level for algod (node.log). The levels range from 0 (critical error / silent) to 5 (debug / verbose). The default value is 4 (‘Info’ - fairly verbose). BaseLoggerDebugLevel uint32 `version[0]:"1" version[1]:"4"` - // if this is 0, do not produce agreement.cadaver + + // CadaverSizeTarget specifies the maximum size of the agreement.cfv file in bytes. Once full the file will be renamed to agreement.archive.log and a new agreement.cdv will be created. CadaverSizeTarget uint64 `version[0]:"1073741824" version[24]:"0"` - CadaverDirectory string `version[27]:""` + + // if this is not set, MakeService will attempt to use ColdDataDir instead + CadaverDirectory string `version[27]:""` + + // HotDataDir is an optional directory to store data that is frequently accessed by the node. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the runtime supplied datadir to store this data. + // Individual resources may have their own override specified, which would override this setting for that resource. + // Setting HotDataDir to a dedicated high performance disk allows for basic disc tuning. + HotDataDir string `version[31]:""` + + // ColdDataDir is an optional directory to store data that is infrequently accessed by the node. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the runtime supplied datadir. + // Individual resources may have their own override specified, which would override this setting for that resource. + // Setting ColdDataDir to a less critical or cheaper disk allows for basic disc tuning. + ColdDataDir string `version[31]:""` + + // TrackerDbDir is an optional directory to store the tracker database. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the HotDataDir. + TrackerDBDir string `version[31]:""` + // BlockDBDir is an optional directory to store the block database. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the ColdDataDir. + BlockDBDir string `version[31]:""` + // CatchpointDir is an optional directory to store catchpoint files, + // except for the in-progress temp file, which will use the HotDataDir and is not separately configurable. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the ColdDataDir. + CatchpointDir string `version[31]:""` + // StateproofDir is an optional directory to store stateproof data. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the ColdDataDir. + StateproofDir string `version[31]:""` + // CrashDBDir is an optional directory to store the crash database. + // For isolation, the node will create a subdirectory in this location, named by the genesis-id of the network. + // If not specified, the node will use the ColdDataDir. + CrashDBDir string `version[31]:""` + + // LogFileDir is an optional directory to store the log, node.log + // If not specified, the node will use the HotDataDir. + // The -o command line option can be used to override this output location. + LogFileDir string `version[31]:""` + // LogArchiveDir is an optional directory to store the log archive. + // If not specified, the node will use the ColdDataDir. + LogArchiveDir string `version[31]:""` // IncomingConnectionsLimit specifies the max number of long-lived incoming // connections. 0 means no connections allowed. Must be non-negative. @@ -84,12 +134,12 @@ type Local struct { IncomingConnectionsLimit int `version[0]:"-1" version[1]:"10000" version[17]:"800" version[27]:"2400"` // BroadcastConnectionsLimit specifies the number of connections that - // will receive broadcast (gossip) messages from this node. If the + // will receive broadcast (gossip) messages from this node. If the // node has more connections than this number, it will send broadcasts // to the top connections by priority (outgoing connections first, then - // by money held by peers based on their participation key). 0 means + // by money held by peers based on their participation key). 0 means // no outgoing messages (not even transaction broadcasting to outgoing - // peers). -1 means unbounded (default). + // peers). -1 means unbounded (default). BroadcastConnectionsLimit int `version[4]:"-1"` // AnnounceParticipationKey specifies that this node should announce its @@ -103,7 +153,7 @@ type Local struct { // outgoing broadcast messages from this node. PriorityPeers map[string]bool `version[4]:""` - // To make sure the algod process does not run out of FDs, algod ensures + // ReservedFDs is used to make sure the algod process does not run out of file descriptors (FDs). Algod ensures // that RLIMIT_NOFILE >= IncomingConnectionsLimit + RestConnectionsHardLimit + // ReservedFDs. ReservedFDs are meant to leave room for short-lived FDs like // DNS queries, SQLite files, etc. This parameter shouldn't be changed. @@ -111,21 +161,27 @@ type Local struct { // then either RestConnectionsHardLimit or IncomingConnectionsLimit decreased. ReservedFDs uint64 `version[2]:"256"` - // local server - // API endpoint address + // EndpointAddress configures the address the node listens to for REST API calls. Specify an IP and port or just port. For example, 127.0.0.1:0 will listen on a random port on the localhost (preferring 8080). EndpointAddress string `version[0]:"127.0.0.1:0"` - // timeouts passed to the rest http.Server implementation - RestReadTimeoutSeconds int `version[4]:"15"` + // RestReadTimeoutSeconds is passed to the API servers rest http.Server implementation. + RestReadTimeoutSeconds int `version[4]:"15"` + + // RestWriteTimeoutSeconds is passed to the API servers rest http.Server implementation. RestWriteTimeoutSeconds int `version[4]:"120"` - // SRV-based phonebook + // DNSBootstrapID specifies the names of a set of DNS SRV records that identify the set of nodes available to connect to. + // When resolving the bootstrap ID will be replaced by the genesis block's network name. This string uses a URL + // parsing library and supports optional backup and dedup parameters. 'backup' is used to provide a second DNS entry to use + // in case the primary is unavailable. dedup is intended to be used to deduplicate SRV records returned from the primary + // and backup DNS address. If the macro is used in the dedup mask, it must be at the beginning of the expression. + // This is not typically something a user would configure. For more information see config/dnsbootstrap.go. DNSBootstrapID string `version[0]:".algorand.network" version[28]:".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)"` - // Log file size limit in bytes. When set to 0 logs will be written to stdout. + // LogSizeLimit is the log file size limit in bytes. When set to 0 logs will be written to stdout. LogSizeLimit uint64 `version[0]:"1073741824"` - // text/template for creating log archive filename. + // LogArchiveName text/template for creating log archive filename. // Available template vars: // Time at start of log: {{.Year}} {{.Month}} {{.Day}} {{.Hour}} {{.Minute}} {{.Second}} // Time at end of log: {{.EndYear}} {{.EndMonth}} {{.EndDay}} {{.EndHour}} {{.EndMinute}} {{.EndSecond}} @@ -139,33 +195,34 @@ type Local struct { // Valid units are 's' seconds, 'm' minutes, 'h' hours LogArchiveMaxAge string `version[4]:""` - // number of consecutive attempts to catchup after which we replace the peers we're connected to + // CatchupFailurePeerRefreshRate is the maximum number of consecutive attempts to catchup after which we replace the peers we're connected to. CatchupFailurePeerRefreshRate int `version[0]:"10"` - // where should the node exporter listen for metrics + // NodeExporterListenAddress is used to set the specific address for publishing metrics; the Prometheus server connects to this incoming port to retrieve metrics. NodeExporterListenAddress string `version[0]:":9100"` - // enable metric reporting flag + // EnableMetricReporting determines if the metrics service for a node is to be enabled. This setting controls metrics being collected from this specific instance of algod. If any instance has metrics enabled, machine-wide metrics are also collected. EnableMetricReporting bool `version[0]:"false"` - // enable top accounts reporting flag + // EnableTopAccountsReporting enable top accounts reporting flag. Deprecated, do not use. EnableTopAccountsReporting bool `version[0]:"false"` - // enable agreement reporting flag. Currently only prints additional period events. + // EnableAgreementReporting controls the agreement reporting flag. Currently only prints additional period events. EnableAgreementReporting bool `version[3]:"false"` - // enable agreement timing metrics flag + // EnableAgreementTimeMetrics controls the agreement timing metrics flag. EnableAgreementTimeMetrics bool `version[3]:"false"` - // The path to the node exporter. + // NodeExporterPath is the path to the node_exporter binary. NodeExporterPath string `version[0]:"./node_exporter"` - // The fallback DNS resolver address that would be used if the system resolver would fail to retrieve SRV records + // FallbackDNSResolverAddress defines the fallback DNS resolver address that would be used if the system resolver would fail to retrieve SRV records. FallbackDNSResolverAddress string `version[0]:""` - // exponential increase factor of transaction pool's fee threshold, should always be 2 in production + // TxPoolExponentialIncreaseFactor exponential increase factor of transaction pool's fee threshold, should always be 2 in production. TxPoolExponentialIncreaseFactor uint64 `version[0]:"2"` + // SuggestedFeeBlockHistory is deprecated and unused. SuggestedFeeBlockHistory int `version[0]:"3"` // TxBacklogServiceRateWindowSeconds is the window size used to determine the service rate of the txBacklog @@ -182,77 +239,76 @@ type Local struct { // if EnableTxBacklogRateLimiting enabled, the over-all size will be larger by MAX_PEERS*TxBacklogReservedCapacityPerPeer TxBacklogSize int `version[27]:"26000"` - // TxPoolSize is the number of transactions that fit in the transaction pool + // TxPoolSize is the number of transactions in the transaction pool buffer. TxPoolSize int `version[0]:"50000" version[5]:"15000" version[23]:"75000"` // number of seconds allowed for syncing transactions TxSyncTimeoutSeconds int64 `version[0]:"30"` - // number of seconds between transaction synchronizations + // TxSyncIntervalSeconds number of seconds between transaction synchronizations. TxSyncIntervalSeconds int64 `version[0]:"60"` - // the number of incoming message hashes buckets. + // IncomingMessageFilterBucketCount is the number of incoming message hash buckets. IncomingMessageFilterBucketCount int `version[0]:"5"` - // the size of each incoming message hash bucket. + // IncomingMessageFilterBucketSize is the size of each incoming message hash bucket. IncomingMessageFilterBucketSize int `version[0]:"512"` - // the number of outgoing message hashes buckets. + // OutgoingMessageFilterBucketCount is the number of outgoing message hash buckets. OutgoingMessageFilterBucketCount int `version[0]:"3"` - // the size of each outgoing message hash bucket. + // OutgoingMessageFilterBucketSize is the size of each outgoing message hash bucket. OutgoingMessageFilterBucketSize int `version[0]:"128"` - // enable the filtering of outgoing messages + // EnableOutgoingNetworkMessageFiltering enable the filtering of outgoing messages EnableOutgoingNetworkMessageFiltering bool `version[0]:"true"` - // enable the filtering of incoming messages + // EnableIncomingMessageFilter enable the filtering of incoming messages. EnableIncomingMessageFilter bool `version[0]:"false"` - // control enabling / disabling deadlock detection. + // DeadlockDetection controls enabling or disabling deadlock detection. // negative (-1) to disable, positive (1) to enable, 0 for default. DeadlockDetection int `version[1]:"0"` - // The threshold used for deadlock detection, in seconds. + // DeadlockDetectionThreshold is the threshold used for deadlock detection, in seconds. DeadlockDetectionThreshold int `version[20]:"30"` - // Prefer to run algod Hosted (under algoh) - // Observed by `goal` for now. + // RunHosted configures whether to run algod in Hosted mode (under algoh). Observed by `goal` for now. RunHosted bool `version[3]:"false"` - // The maximal number of blocks that catchup will fetch in parallel. + // CatchupParallelBlocks is the maximum number of blocks that catchup will fetch in parallel. // If less than Protocol.SeedLookback, then Protocol.SeedLookback will be used as to limit the catchup. // Setting this variable to 0 would disable the catchup CatchupParallelBlocks uint64 `version[3]:"50" version[5]:"16"` - // Generate AssembleBlockMetrics telemetry event + // EnableAssembleStats specifies whether or not to emit the AssembleBlockMetrics telemetry event. EnableAssembleStats bool `version[0]:""` - // Generate ProcessBlockMetrics telemetry event + // EnableProcessBlockStats specifies whether or not to emit the ProcessBlockMetrics telemetry event. EnableProcessBlockStats bool `version[0]:""` - // SuggestedFeeSlidingWindowSize is number of past blocks that will be considered in computing the suggested fee + // SuggestedFeeSlidingWindowSize is deprecated and unused. SuggestedFeeSlidingWindowSize uint32 `version[3]:"50"` - // the max size the sync server would return + // TxSyncServeResponseSize the max size the sync server would return. TxSyncServeResponseSize int `version[3]:"1000000"` - // UseXForwardedForAddress indicates whether or not the node should use the X-Forwarded-For HTTP Header when + // UseXForwardedForAddressField indicates whether or not the node should use the X-Forwarded-For HTTP Header when // determining the source of a connection. If used, it should be set to the string "X-Forwarded-For", unless the // proxy vendor provides another header field. In the case of CloudFlare proxy, the "CF-Connecting-IP" header // field can be used. UseXForwardedForAddressField string `version[0]:""` - // ForceRelayMessages indicates whether the network library relay messages even in the case that no NetAddress was specified. + // ForceRelayMessages indicates whether the network library should relay messages even in the case that no NetAddress was specified. ForceRelayMessages bool `version[0]:"false"` - // ConnectionsRateLimitingWindowSeconds is being used in conjunction with ConnectionsRateLimitingCount; + // ConnectionsRateLimitingWindowSeconds is being used along with ConnectionsRateLimitingCount; // see ConnectionsRateLimitingCount description for further information. Providing a zero value // in this variable disables the connection rate limiting. ConnectionsRateLimitingWindowSeconds uint `version[4]:"1"` // ConnectionsRateLimitingCount is being used along with ConnectionsRateLimitingWindowSeconds to determine if - // a connection request should be accepted or not. The gossip network examine all the incoming requests in the past + // a connection request should be accepted or not. The gossip network examines all the incoming requests in the past // ConnectionsRateLimitingWindowSeconds seconds that share the same origin. If the total count exceed the ConnectionsRateLimitingCount // value, the connection is refused. ConnectionsRateLimitingCount uint `version[4]:"60"` @@ -260,12 +316,12 @@ type Local struct { // EnableRequestLogger enabled the logging of the incoming requests to the telemetry server. EnableRequestLogger bool `version[4]:"false"` - // PeerConnectionsUpdateInterval defines the interval at which the peer connections information is being sent to the - // telemetry ( when enabled ). Defined in seconds. + // PeerConnectionsUpdateInterval defines the interval at which the peer connections information is sent to + // telemetry (when enabled). Defined in seconds. PeerConnectionsUpdateInterval int `version[5]:"3600"` // HeartbeatUpdateInterval defines the interval at which the heartbeat information is being sent to the - // telemetry ( when enabled ). Defined in seconds. Minimum value is 60. + // telemetry (when enabled). Defined in seconds. Minimum value is 60. HeartbeatUpdateInterval int `version[27]:"600"` // EnableProfiler enables the go pprof endpoints, should be false if @@ -275,7 +331,7 @@ type Local struct { // EnableRuntimeMetrics exposes Go runtime metrics in /metrics and via node_exporter. EnableRuntimeMetrics bool `version[22]:"false"` - // TelemetryToLog records messages to node.log that are normally sent to remote event monitoring + // TelemetryToLog configures whether to record messages to node.log that are normally only sent to remote event monitoring. TelemetryToLog bool `version[5]:"true"` // DNSSecurityFlags instructs algod validating DNS responses. @@ -291,7 +347,7 @@ type Local struct { EnablePingHandler bool `version[6]:"true"` // DisableOutgoingConnectionThrottling disables the connection throttling of the network library, which - // allow the network library to continuesly disconnect relays based on their relative ( and absolute ) performance. + // allow the network library to continuously disconnect relays based on their relative (and absolute) performance. DisableOutgoingConnectionThrottling bool `version[5]:"false"` // NetworkProtocolVersion overrides network protocol version ( if present ) @@ -301,16 +357,16 @@ type Local struct { // See CatchpointTracking for more details. CatchpointInterval uint64 `version[7]:"10000"` - // CatchpointFileHistoryLength defines how many catchpoint files we want to store back. - // 0 means don't store any, -1 mean unlimited and positive number suggest the number of most recent catchpoint files. + // CatchpointFileHistoryLength defines how many catchpoint files to store. + // 0 means don't store any, -1 mean unlimited and positive number suggest the maximum number of most recent catchpoint files to store. CatchpointFileHistoryLength int `version[7]:"365"` // EnableLedgerService enables the ledger serving service. The functionality of this depends on NetAddress, which must also be provided. // This functionality is required for the catchpoint catchup. EnableLedgerService bool `version[7]:"false"` - // EnableBlockService enables the block serving service. The functionality of this depends on NetAddress, which must also be provided. - // This functionality is required for the catchup. + // EnableBlockService controls whether to enables the block serving service. The functionality of this depends on NetAddress, which must also be provided. + // This functionality is required for catchup. EnableBlockService bool `version[7]:"false"` // EnableGossipBlockService enables the block serving service over the gossip network. The functionality of this depends on NetAddress, which must also be provided. @@ -326,10 +382,10 @@ type Local struct { // CatchupLedgerDownloadRetryAttempts controls the number of attempt the ledger fetching would be attempted before giving up catching up to the provided catchpoint. CatchupLedgerDownloadRetryAttempts int `version[9]:"50"` - // CatchupLedgerDownloadRetryAttempts controls the number of attempt the block fetching would be attempted before giving up catching up to the provided catchpoint. + // CatchupBlockDownloadRetryAttempts controls the number of attempts the block fetcher would make before giving up on a provided catchpoint. CatchupBlockDownloadRetryAttempts int `version[9]:"1000"` - // EnableDeveloperAPI enables teal/compile, teal/dryrun API endpoints. + // EnableDeveloperAPI enables teal/compile and teal/dryrun API endpoints. // This functionality is disabled by default. EnableDeveloperAPI bool `version[9]:"false"` @@ -342,7 +398,7 @@ type Local struct { // A value of 1 means "track catchpoints as long as CatchpointInterval > 0". // A value of 2 means "track catchpoints and always generate catchpoint files as long as CatchpointInterval > 0". // A value of 0 means automatic, which is the default value. In this mode, a non archival node would not track the catchpoints, and an archival node would track the catchpoints as long as CatchpointInterval > 0. - // Other values of CatchpointTracking would give a warning in the log file, and would behave as if the default value was provided. + // Other values of CatchpointTracking would behave as if the default value was provided. CatchpointTracking int64 `version[11]:"0"` // LedgerSynchronousMode defines the synchronous mode used by the ledger database. The supported options are: @@ -353,8 +409,8 @@ type Local struct { // for further information see the description of SynchronousMode in dbutil.go LedgerSynchronousMode int `version[12]:"2"` - // AccountsRebuildSynchronousMode defines the synchronous mode used by the ledger database while the account database is being rebuilt. This is not a typical operational usecase, - // and is expected to happen only on either startup ( after enabling the catchpoint interval, or on certain database upgrades ) or during fast catchup. The values specified here + // AccountsRebuildSynchronousMode defines the synchronous mode used by the ledger database while the account database is being rebuilt. This is not a typical operational use-case, + // and is expected to happen only on either startup (after enabling the catchpoint interval, or on certain database upgrades) or during fast catchup. The values specified here // and their meanings are identical to the ones in LedgerSynchronousMode. AccountsRebuildSynchronousMode int `version[12]:"1"` @@ -368,7 +424,7 @@ type Local struct { // the default of 20480 would be used. MinCatchpointFileDownloadBytesPerSecond uint64 `version[13]:"20480"` - // TraceServer is a host:port to report graph propagation trace info to. + // NetworkMessageTraceServer is a host:port address to report graph propagation trace info to. NetworkMessageTraceServer string `version[13]:""` // VerifiedTranscationsCacheSize defines the number of transactions that the verified transactions cache would hold before cycling the cache storage in a round-robin fashion. @@ -376,7 +432,7 @@ type Local struct { // EnableCatchupFromArchiveServers controls which peers the catchup service would use in order to catchup. // When enabled, the catchup service would use the archive servers before falling back to the relays. - // On networks that doesn't have archive servers, this becomes a no-op, as the catchup service would have no + // On networks that don't have archive servers, this becomes a no-op, as the catchup service would have no // archive server to pick from, and therefore automatically selects one of the relay nodes. EnableCatchupFromArchiveServers bool `version[15]:"false"` @@ -394,7 +450,7 @@ type Local struct { // an archiver or return StatusNotFound (404) when in does not have the requested round, and // BlockServiceCustomFallbackEndpoints is empty. // The archiver is randomly selected, if none is available, will return StatusNotFound (404). - EnableBlockServiceFallbackToArchiver bool `version[16]:"true"` + EnableBlockServiceFallbackToArchiver bool `version[16]:"true" version[31]:"false"` // CatchupBlockValidateMode is a development and testing configuration used by the catchup service. // It can be used to omit certain validations to speed up the catchup process, or to apply extra validations which are redundant in normal operation. @@ -410,25 +466,25 @@ type Local struct { // previously used executabled, and would not provide any additional security guarantees. CatchupBlockValidateMode int `version[16]:"0"` - // Generate AccountUpdates telemetry event + // EnableAccountUpdatesStats specifies whether or not to emit the AccountUpdates telemetry event. EnableAccountUpdatesStats bool `version[16]:"false"` - // Time interval in nanoseconds for generating accountUpdates telemetry event + // AccountUpdatesStatsInterval is the time interval in nanoseconds between accountUpdates telemetry events. AccountUpdatesStatsInterval time.Duration `version[16]:"5000000000"` // ParticipationKeysRefreshInterval is the duration between two consecutive checks to see if new participation - // keys have been placed on the genesis directory. + // keys have been placed on the genesis directory. Deprecated and unused. ParticipationKeysRefreshInterval time.Duration `version[16]:"60000000000"` // DisableNetworking disables all the incoming and outgoing communication a node would perform. This is useful - // when we have a single-node private network, where there is no other nodes that need to be communicated with. - // features like catchpoint catchup would be rendered completly non-operational, and many of the node inner - // working would be completly dis-functional. + // when we have a single-node private network, where there are no other nodes that need to be communicated with. + // Features like catchpoint catchup would be rendered completely non-operational, and many of the node inner + // working would be completely dis-functional. DisableNetworking bool `version[16]:"false"` // ForceFetchTransactions allows to explicitly configure a node to retrieve all the transactions // into it's transaction pool, even if those would not be required as the node doesn't - // participate in the consensus or used to relay transactions. + // participate in consensus and is not used to relay transactions. ForceFetchTransactions bool `version[17]:"false"` // EnableVerbosedTransactionSyncLogging enables the transaction sync to write extensive @@ -449,11 +505,12 @@ type Local struct { // ProposalAssemblyTime is the max amount of time to spend on generating a proposal block. ProposalAssemblyTime time.Duration `version[19]:"250000000" version[23]:"500000000"` + // RestConnectionsSoftLimit is the maximum number of active requests the API server // When the number of http connections to the REST layer exceeds the soft limit, // we start returning http code 429 Too Many Requests. RestConnectionsSoftLimit uint64 `version[20]:"1024"` - // The http server does not accept new connections as long we have this many - // (hard limit) connections already. + + // RestConnectionsHardLimit is the maximum number of active connections the API server will accept before closing requests with no response. RestConnectionsHardLimit uint64 `version[20]:"2048"` // MaxAPIResourcesPerAccount sets the maximum total number of resources (created assets, created apps, @@ -474,6 +531,10 @@ type Local struct { // i.e. the ledger can answer account states questions for the range Latest-MaxAcctLookback...Latest MaxAcctLookback uint64 `version[23]:"4"` + // BlockHistoryLookback sets the max lookback range for block information. + // i.e. the block DB can return transaction IDs for questions for the range Latest-MaxBlockHistoryLookback...Latest + MaxBlockHistoryLookback uint64 `version[31]:"0"` + // EnableUsageLog enables 10Hz log of CPU and RAM usage. // Also adds 'algod_ram_usage` (number of bytes in use) to /metrics EnableUsageLog bool `version[24]:"false"` @@ -521,12 +582,12 @@ type Local struct { // When it exceeds this capacity, it redirects the block requests to a different node BlockServiceMemCap uint64 `version[28]:"500000000"` - // P2PEnable turns on the peer to peer network - P2PEnable bool `version[29]:"false"` + // EnableP2P turns on the peer to peer network + EnableP2P bool `version[31]:"false"` // P2PPersistPeerID will write the private key used for the node's PeerID to the P2PPrivateKeyLocation. // This is only used when P2PEnable is true. If P2PPrivateKey is not specified, it uses the default location. - P2PPersistPeerID bool `version[29]:"true"` + P2PPersistPeerID bool `version[29]:"false"` // P2PPrivateKeyLocation allows the user to specify a custom path to the private key used for the node's PeerID. // The private key provided must be an ed25519 private key. @@ -645,6 +706,150 @@ func (cfg Local) IsGossipServer() bool { return cfg.NetAddress != "" } +// ensureAbsGenesisDir will convert a path to absolute, and will attempt to make a genesis directory there +func ensureAbsGenesisDir(path string, genesisID string) (string, error) { + pathAbs, err := filepath.Abs(path) + if err != nil { + return "", err + } + genesisDir := filepath.Join(pathAbs, genesisID) + err = os.MkdirAll(genesisDir, 0700) + if err != nil && !os.IsExist(err) { + return "", err + } + return genesisDir, nil +} + +// ResolvedGenesisDirs is a collection of directories including Genesis ID +// Subdirectories for execution of a node +type ResolvedGenesisDirs struct { + RootGenesisDir string + HotGenesisDir string + ColdGenesisDir string + TrackerGenesisDir string + BlockGenesisDir string + CatchpointGenesisDir string + StateproofGenesisDir string + CrashGenesisDir string +} + +// String returns the Genesis Directory values as a string +func (rgd ResolvedGenesisDirs) String() string { + ret := "" + ret += fmt.Sprintf("RootGenesisDir: %s\n", rgd.RootGenesisDir) + ret += fmt.Sprintf("HotGenesisDir: %s\n", rgd.HotGenesisDir) + ret += fmt.Sprintf("ColdGenesisDir: %s\n", rgd.ColdGenesisDir) + ret += fmt.Sprintf("TrackerGenesisDir: %s\n", rgd.TrackerGenesisDir) + ret += fmt.Sprintf("BlockGenesisDir: %s\n", rgd.BlockGenesisDir) + ret += fmt.Sprintf("CatchpointGenesisDir: %s\n", rgd.CatchpointGenesisDir) + ret += fmt.Sprintf("StateproofGenesisDir: %s\n", rgd.StateproofGenesisDir) + ret += fmt.Sprintf("CrashGenesisDir: %s\n", rgd.CrashGenesisDir) + return ret +} + +// ResolveLogPaths will return the most appropriate location for liveLog and archive, given user config +func (cfg *Local) ResolveLogPaths(rootDir string) (liveLog, archive string) { + // the default locations of log and archive are root + liveLog = filepath.Join(rootDir, "node.log") + archive = filepath.Join(rootDir, cfg.LogArchiveName) + // if hot data dir is set, use it for the base of logs + if cfg.HotDataDir != "" { + liveLog = filepath.Join(cfg.HotDataDir, "node.log") + } + // if cold data dir is set, use it for the base of archives + if cfg.ColdDataDir != "" { + archive = filepath.Join(cfg.ColdDataDir, cfg.LogArchiveName) + } + // if LogFileDir is set, use it instead + if cfg.LogFileDir != "" { + liveLog = filepath.Join(cfg.LogFileDir, "node.log") + } + // if LogArchivePath is set, use it instead + if cfg.LogArchiveDir != "" { + archive = filepath.Join(cfg.LogArchiveDir, cfg.LogArchiveName) + } + return liveLog, archive +} + +// EnsureAndResolveGenesisDirs will resolve the supplied config paths to absolute paths, and will create the genesis directories of each +// returns a ResolvedGenesisDirs struct with the resolved paths for use during runtime +func (cfg *Local) EnsureAndResolveGenesisDirs(rootDir, genesisID string) (ResolvedGenesisDirs, error) { + var resolved ResolvedGenesisDirs + var err error + if rootDir != "" { + resolved.RootGenesisDir, err = ensureAbsGenesisDir(rootDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + return ResolvedGenesisDirs{}, fmt.Errorf("rootDir is required") + } + // if HotDataDir is not set, use RootDataDir + if cfg.HotDataDir != "" { + resolved.HotGenesisDir, err = ensureAbsGenesisDir(cfg.HotDataDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.HotGenesisDir = resolved.RootGenesisDir + } + // if ColdDataDir is not set, use RootDataDir + if cfg.ColdDataDir != "" { + resolved.ColdGenesisDir, err = ensureAbsGenesisDir(cfg.ColdDataDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.ColdGenesisDir = resolved.RootGenesisDir + } + // if TrackerDBDir is not set, use HotDataDir + if cfg.TrackerDBDir != "" { + resolved.TrackerGenesisDir, err = ensureAbsGenesisDir(cfg.TrackerDBDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.TrackerGenesisDir = resolved.HotGenesisDir + } + // if BlockDBDir is not set, use ColdDataDir + if cfg.BlockDBDir != "" { + resolved.BlockGenesisDir, err = ensureAbsGenesisDir(cfg.BlockDBDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.BlockGenesisDir = resolved.ColdGenesisDir + } + // if CatchpointDir is not set, use ColdDataDir + if cfg.CatchpointDir != "" { + resolved.CatchpointGenesisDir, err = ensureAbsGenesisDir(cfg.CatchpointDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.CatchpointGenesisDir = resolved.ColdGenesisDir + } + // if StateproofDir is not set, use ColdDataDir + if cfg.StateproofDir != "" { + resolved.StateproofGenesisDir, err = ensureAbsGenesisDir(cfg.StateproofDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.StateproofGenesisDir = resolved.ColdGenesisDir + } + // if CrashDBDir is not set, use ColdDataDir + if cfg.CrashDBDir != "" { + resolved.CrashGenesisDir, err = ensureAbsGenesisDir(cfg.CrashDBDir, genesisID) + if err != nil { + return ResolvedGenesisDirs{}, err + } + } else { + resolved.CrashGenesisDir = resolved.ColdGenesisDir + } + return resolved, nil +} + // AdjustConnectionLimits updates RestConnectionsSoftLimit, RestConnectionsHardLimit, IncomingConnectionsLimit // if requiredFDs greater than maxFDs func (cfg *Local) AdjustConnectionLimits(requiredFDs, maxFDs uint64) bool { @@ -672,3 +877,34 @@ func (cfg *Local) AdjustConnectionLimits(requiredFDs, maxFDs uint64) bool { return true } + +// StoresCatchpoints returns true if the node is configured to store catchpoints +func (cfg *Local) StoresCatchpoints() bool { + if cfg.CatchpointInterval <= 0 { + return false + } + switch cfg.CatchpointTracking { + case CatchpointTrackingModeUntracked: + // No catchpoints. + default: + fallthrough + case CatchpointTrackingModeAutomatic, CatchpointTrackingModeTracked: + if cfg.Archival { + return true + } + case CatchpointTrackingModeStored: + return true + } + return false +} + +// TracksCatchpoints returns true if the node is configured to track catchpoints +func (cfg *Local) TracksCatchpoints() bool { + if cfg.StoresCatchpoints() { + return true + } + if cfg.CatchpointTracking == CatchpointTrackingModeTracked && cfg.CatchpointInterval > 0 { + return true + } + return false +} diff --git a/config/local_defaults.go b/config/local_defaults.go index 21a296b970..12689e4e9b 100644 --- a/config/local_defaults.go +++ b/config/local_defaults.go @@ -20,7 +20,7 @@ package config var defaultLocal = Local{ - Version: 30, + Version: 31, AccountUpdatesStatsInterval: 5000000000, AccountsRebuildSynchronousMode: 1, AgreementIncomingBundlesQueueLength: 15, @@ -29,11 +29,13 @@ var defaultLocal = Local{ AnnounceParticipationKey: true, Archival: false, BaseLoggerDebugLevel: 4, + BlockDBDir: "", BlockServiceCustomFallbackEndpoints: "", BlockServiceMemCap: 500000000, BroadcastConnectionsLimit: -1, CadaverDirectory: "", CadaverSizeTarget: 0, + CatchpointDir: "", CatchpointFileHistoryLength: 365, CatchpointInterval: 10000, CatchpointTracking: 0, @@ -44,8 +46,10 @@ var defaultLocal = Local{ CatchupHTTPBlockFetchTimeoutSec: 4, CatchupLedgerDownloadRetryAttempts: 50, CatchupParallelBlocks: 16, + ColdDataDir: "", ConnectionsRateLimitingCount: 60, ConnectionsRateLimitingWindowSeconds: 1, + CrashDBDir: "", DNSBootstrapID: ".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)", DNSSecurityFlags: 1, DeadlockDetection: 0, @@ -60,7 +64,7 @@ var defaultLocal = Local{ EnableAgreementTimeMetrics: false, EnableAssembleStats: false, EnableBlockService: false, - EnableBlockServiceFallbackToArchiver: true, + EnableBlockServiceFallbackToArchiver: false, EnableCatchupFromArchiveServers: false, EnableDeveloperAPI: false, EnableExperimentalAPI: false, @@ -70,6 +74,7 @@ var defaultLocal = Local{ EnableLedgerService: false, EnableMetricReporting: false, EnableOutgoingNetworkMessageFiltering: true, + EnableP2P: false, EnablePingHandler: true, EnableProcessBlockStats: false, EnableProfiler: false, @@ -86,16 +91,20 @@ var defaultLocal = Local{ ForceRelayMessages: false, GossipFanout: 4, HeartbeatUpdateInterval: 600, + HotDataDir: "", IncomingConnectionsLimit: 2400, IncomingMessageFilterBucketCount: 5, IncomingMessageFilterBucketSize: 512, LedgerSynchronousMode: 2, + LogArchiveDir: "", LogArchiveMaxAge: "", LogArchiveName: "node.archive.log", + LogFileDir: "", LogSizeLimit: 1073741824, MaxAPIBoxPerApplication: 100000, MaxAPIResourcesPerAccount: 100000, MaxAcctLookback: 4, + MaxBlockHistoryLookback: 0, MaxCatchpointDownloadDuration: 43200000000000, MaxConnectionsPerIP: 15, MinCatchpointFileDownloadBytesPerSecond: 20480, @@ -107,8 +116,7 @@ var defaultLocal = Local{ OptimizeAccountsDatabaseOnStartup: false, OutgoingMessageFilterBucketCount: 3, OutgoingMessageFilterBucketSize: 128, - P2PEnable: false, - P2PPersistPeerID: true, + P2PPersistPeerID: false, P2PPrivateKeyLocation: "", ParticipationKeysRefreshInterval: 60000000000, PeerConnectionsUpdateInterval: 3600, @@ -123,12 +131,14 @@ var defaultLocal = Local{ RestReadTimeoutSeconds: 15, RestWriteTimeoutSeconds: 120, RunHosted: false, + StateproofDir: "", StorageEngine: "sqlite", SuggestedFeeBlockHistory: 3, SuggestedFeeSlidingWindowSize: 50, TLSCertFile: "", TLSKeyFile: "", TelemetryToLog: true, + TrackerDBDir: "", TransactionSyncDataExchangeRate: 0, TransactionSyncSignificantMessageThreshold: 0, TxBacklogReservedCapacityPerPeer: 20, diff --git a/config/version.go b/config/version.go index d1954ab315..1c3cb549f6 100644 --- a/config/version.go +++ b/config/version.go @@ -33,7 +33,7 @@ const VersionMajor = 3 // VersionMinor is the Minor semantic version number (x.#.z) - changed when backwards-compatible features are introduced. // Not enforced until after initial public release (x > 0). -const VersionMinor = 18 +const VersionMinor = 19 // Version is the type holding our full version information. type Version struct { diff --git a/crypto/merklearray/msgp_gen.go b/crypto/merklearray/msgp_gen.go index 20e8175898..db308439e0 100644 --- a/crypto/merklearray/msgp_gen.go +++ b/crypto/merklearray/msgp_gen.go @@ -73,11 +73,11 @@ func (_ Layer) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Layer) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0002 int var zb0003 bool zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) @@ -191,11 +191,11 @@ func (_ *Proof) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Proof) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -413,11 +413,11 @@ func (_ *SingleLeafProof) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SingleLeafProof) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -642,11 +642,11 @@ func (_ *Tree) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Tree) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int diff --git a/crypto/merklearray/proof.go b/crypto/merklearray/proof.go index 0489312035..3455baa08f 100644 --- a/crypto/merklearray/proof.go +++ b/crypto/merklearray/proof.go @@ -149,7 +149,7 @@ func (p *SingleLeafProof) GetConcatenatedProof() []byte { } // ProofDataToSingleLeafProof receives serialized proof data and uses it to construct a proof object. -func ProofDataToSingleLeafProof(hashTypeData string, treeDepth uint64, proofBytes []byte) (SingleLeafProof, error) { +func ProofDataToSingleLeafProof(hashTypeData string, proofBytes []byte) (SingleLeafProof, error) { hashType, err := crypto.UnmarshalHashType(hashTypeData) if err != nil { return SingleLeafProof{}, err @@ -158,7 +158,7 @@ func ProofDataToSingleLeafProof(hashTypeData string, treeDepth uint64, proofByte var proof SingleLeafProof proof.HashFactory = crypto.HashFactory{HashType: hashType} - proof.TreeDepth = uint8(treeDepth) + proof.TreeDepth = 0 digestSize := proof.HashFactory.NewHash().Size() if len(proofBytes)%digestSize != 0 { @@ -172,6 +172,7 @@ func ProofDataToSingleLeafProof(hashTypeData string, treeDepth uint64, proofByte copy(d[:], proofBytes) proofPath = append(proofPath, d[:]) proofBytes = proofBytes[len(d):] + proof.TreeDepth++ } proof.Path = proofPath diff --git a/crypto/merklearray/proof_test.go b/crypto/merklearray/proof_test.go index 9591d6ee41..a160371550 100644 --- a/crypto/merklearray/proof_test.go +++ b/crypto/merklearray/proof_test.go @@ -157,8 +157,9 @@ func TestConcatenatedProofsMissingChild(t *testing.T) { err = Verify(tree.Root(), map[uint64]crypto.Hashable{6: array[6]}, newP.ToProof()) a.NoError(err) - recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), uint64(p.TreeDepth), concatenatedProof) + recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), concatenatedProof) a.NoError(err) + a.Equal(recomputedProof.TreeDepth, p.TreeDepth) // verify that we can reconstruct the original singleLeafProof from the concatenated proof err = Verify(tree.Root(), map[uint64]crypto.Hashable{6: array[6]}, recomputedProof.ToProof()) @@ -189,8 +190,9 @@ func TestConcatenatedProofsFullTree(t *testing.T) { err = Verify(tree.Root(), map[uint64]crypto.Hashable{6: array[6]}, newP.ToProof()) a.NoError(err) - recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), uint64(p.TreeDepth), concatenatedProof) + recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), concatenatedProof) a.NoError(err) + a.Equal(recomputedProof.TreeDepth, p.TreeDepth) // verify that we can reconstruct the original singleLeafProof from the concatenated proof err = Verify(tree.Root(), map[uint64]crypto.Hashable{6: array[6]}, recomputedProof.ToProof()) @@ -218,8 +220,9 @@ func TestConcatenatedProofsOneLeaf(t *testing.T) { err = Verify(tree.Root(), map[uint64]crypto.Hashable{0: array[0]}, newP.ToProof()) a.NoError(err) - recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), uint64(p.TreeDepth), concatenatedProof) + recomputedProof, err := ProofDataToSingleLeafProof(p.HashFactory.HashType.String(), concatenatedProof) a.NoError(err) + a.Equal(recomputedProof.TreeDepth, p.TreeDepth) // verify that we can reconstruct the original singleLeafProof from the concatenated proof err = Verify(tree.Root(), map[uint64]crypto.Hashable{0: array[0]}, recomputedProof.ToProof()) @@ -230,7 +233,7 @@ func TestProofDeserializationError(t *testing.T) { partitiontest.PartitionTest(t) a := require.New(t) - _, err := ProofDataToSingleLeafProof(crypto.Sha256.String(), 1, []byte{1}) + _, err := ProofDataToSingleLeafProof(crypto.Sha256.String(), []byte{1}) a.ErrorIs(err, ErrProofLengthDigestSizeMismatch) } diff --git a/crypto/merklesignature/msgp_gen.go b/crypto/merklesignature/msgp_gen.go index 9329caf04b..8cac8a7395 100644 --- a/crypto/merklesignature/msgp_gen.go +++ b/crypto/merklesignature/msgp_gen.go @@ -85,11 +85,11 @@ func (_ *Commitment) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Commitment) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -167,11 +167,11 @@ func (_ *KeyRoundPair) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *KeyRoundPair) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -347,11 +347,11 @@ func (_ *Secrets) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Secrets) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -522,11 +522,11 @@ func (_ *Signature) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Signature) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -702,11 +702,11 @@ func (_ *SignerContext) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SignerContext) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -859,11 +859,11 @@ func (_ *Verifier) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Verifier) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int diff --git a/crypto/merklesignature/persistentMerkleSignatureScheme.go b/crypto/merklesignature/persistentMerkleSignatureScheme.go index c862dcb965..a5e114a46a 100644 --- a/crypto/merklesignature/persistentMerkleSignatureScheme.go +++ b/crypto/merklesignature/persistentMerkleSignatureScheme.go @@ -98,7 +98,8 @@ func (s *Secrets) Persist(store db.Accessor) error { return fmt.Errorf("Secrets.Persist: %w", ErrKeyLifetimeIsZero) } round := indexToRound(s.FirstValid, s.KeyLifetime, 0) - encodedKey := protocol.GetEncodingBuf() + encodedBuf := protocol.GetEncodingBuf() + encodedKey := encodedBuf.Bytes() err := store.Atomic(func(ctx context.Context, tx *sql.Tx) error { err := InstallStateProofTable(tx) // assumes schema table already exists (created by partInstallDatabase) if err != nil { @@ -126,7 +127,7 @@ func (s *Secrets) Persist(store db.Accessor) error { return nil }) - protocol.PutEncodingBuf(encodedKey) + protocol.PutEncodingBuf(encodedBuf.Update(encodedKey)) if err != nil { return fmt.Errorf("Secrets.Persist: %w", err) } diff --git a/crypto/msgp_gen.go b/crypto/msgp_gen.go index f143ef3e99..ab5bdceb88 100644 --- a/crypto/msgp_gen.go +++ b/crypto/msgp_gen.go @@ -366,11 +366,11 @@ func (_ *Digest) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Digest) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -420,11 +420,11 @@ func (_ *FalconPrivateKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconPrivateKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -474,11 +474,11 @@ func (_ *FalconPublicKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconPublicKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -528,11 +528,11 @@ func (_ *FalconSeed) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconSeed) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -585,11 +585,11 @@ func (_ FalconSignature) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconSignature) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 []byte var zb0002 int @@ -676,11 +676,11 @@ func (_ *FalconSigner) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconSigner) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -815,11 +815,11 @@ func (_ *FalconVerifier) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *FalconVerifier) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -925,11 +925,11 @@ func (_ GenericDigest) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *GenericDigest) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 []byte var zb0002 int @@ -1007,11 +1007,11 @@ func (_ *HashFactory) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *HashFactory) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1126,11 +1126,11 @@ func (_ HashType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *HashType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint16 zb0001, bts, err = msgp.ReadUint16Bytes(bts) @@ -1183,11 +1183,11 @@ func (_ *MasterDerivationKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *MasterDerivationKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -1277,11 +1277,11 @@ func (_ *MultisigSig) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *MultisigSig) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -1481,11 +1481,11 @@ func (_ *MultisigSubsig) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *MultisigSubsig) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -1623,11 +1623,11 @@ func (_ *OneTimeSignature) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignature) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0007 int @@ -1898,11 +1898,11 @@ func (_ *OneTimeSignatureSecrets) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignatureSecrets) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0006 int @@ -2256,11 +2256,11 @@ func (_ *OneTimeSignatureSecretsPersistent) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignatureSecretsPersistent) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0006 int @@ -2537,11 +2537,11 @@ func (_ *OneTimeSignatureSubkeyBatchID) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignatureSubkeyBatchID) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -2668,11 +2668,11 @@ func (_ *OneTimeSignatureSubkeyOffsetID) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignatureSubkeyOffsetID) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -2804,11 +2804,11 @@ func (_ *OneTimeSignatureVerifier) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OneTimeSignatureVerifier) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -2858,11 +2858,11 @@ func (_ *PrivateKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *PrivateKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -2912,11 +2912,11 @@ func (_ *PublicKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *PublicKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -2966,11 +2966,11 @@ func (_ *Seed) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Seed) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3020,11 +3020,11 @@ func (_ *Signature) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Signature) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3080,11 +3080,11 @@ func (_ *SignatureSecrets) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SignatureSecrets) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -3210,11 +3210,11 @@ func (_ *VRFSecrets) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *VRFSecrets) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -3334,11 +3334,11 @@ func (_ *VrfOutput) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *VrfOutput) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3388,11 +3388,11 @@ func (_ *VrfPrivkey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *VrfPrivkey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3442,11 +3442,11 @@ func (_ *VrfProof) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *VrfProof) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3496,11 +3496,11 @@ func (_ *VrfPubkey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *VrfPubkey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3550,11 +3550,11 @@ func (_ *ed25519PrivateKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ed25519PrivateKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3604,11 +3604,11 @@ func (_ *ed25519PublicKey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ed25519PublicKey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3658,11 +3658,11 @@ func (_ *ed25519Seed) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ed25519Seed) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3712,11 +3712,11 @@ func (_ *ed25519Signature) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ed25519Signature) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -3778,11 +3778,11 @@ func (_ *ephemeralSubkey) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ephemeralSubkey) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0005 int diff --git a/crypto/stateproof/coinGenerator.go b/crypto/stateproof/coinGenerator.go index 1c019c869f..eb73e10f86 100644 --- a/crypto/stateproof/coinGenerator.go +++ b/crypto/stateproof/coinGenerator.go @@ -76,7 +76,7 @@ func makeCoinGenerator(choice *coinChoiceSeed) coinGenerator { choice.version = VersionForCoinGenerator rep := crypto.HashRep(choice) shk := sha3.NewShake256() - shk.Write(rep) //nolint:errcheck // ShakeHash.Write may panic, but does not return error + shk.Write(rep) // hash.Hash interface's Write may panic, but does not return error threshold := prepareRejectionSamplingThreshold(choice.signedWeight) return coinGenerator{shkContext: shk, signedWeight: choice.signedWeight, threshold: threshold} diff --git a/crypto/stateproof/msgp_gen.go b/crypto/stateproof/msgp_gen.go index bd50401899..51b3dc3848 100644 --- a/crypto/stateproof/msgp_gen.go +++ b/crypto/stateproof/msgp_gen.go @@ -89,11 +89,11 @@ func (_ *MessageHash) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *MessageHash) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -223,11 +223,11 @@ func (_ *Prover) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Prover) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -574,11 +574,11 @@ func (_ *ProverPersistedFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ProverPersistedFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -891,11 +891,11 @@ func (_ *Reveal) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Reveal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1229,11 +1229,11 @@ func (_ *StateProof) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProof) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -1562,11 +1562,11 @@ func (_ *sigslotCommit) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *sigslotCommit) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 7337b2a98a..b28e35abaa 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -2508,6 +2508,12 @@ "parameters": [ { "$ref": "#/parameters/catchpoint" + }, + { + "name": "min", + "description": "Specify the minimum number of blocks which the ledger must be advanced by in order to start the catchup. This is useful for simplifying tools which support fast catchup, they can run the catchup unconditionally and the node will skip the catchup if it is not needed.", + "in": "query", + "type": "integer" } ], "responses": { @@ -3289,6 +3295,23 @@ } } }, + "AvmKeyValue": { + "description": "Represents an AVM key-value pair in an application store.", + "type": "object", + "required": [ + "key", + "value" + ], + "properties": { + "key": { + "type": "string", + "format": "byte" + }, + "value": { + "$ref": "#/definitions/AvmValue" + } + } + }, "StateDelta": { "description": "Application state delta.", "type": "array", @@ -3629,6 +3652,10 @@ "$ref": "#/definitions/SimulateRequestTransactionGroup" } }, + "round": { + "description": "If provided, specifies the round preceding the simulation. State changes through this round will be used to run this simulation. Usually only the 4 most recent rounds will be available (controlled by the node config value MaxAcctLookback). If not specified, defaults to the latest available round.", + "type": "integer" + }, "allow-empty-signatures": { "description": "Allows transactions without signatures to be simulated as if they had correct signatures.", "type": "boolean" @@ -4127,6 +4154,52 @@ } } }, + "ApplicationKVStorage": { + "description": "An application's global/local/box state.", + "required": [ + "kvs" + ], + "properties": { + "kvs": { + "description": "Key-Value pairs representing application states.", + "type": "array", + "items": { + "$ref": "#/definitions/AvmKeyValue" + } + }, + "account": { + "description": "The address of the account associated with the local state.", + "type": "string", + "x-algorand-format": "Address" + } + } + }, + "ApplicationInitialStates": { + "description": "An application's initial global/local/box states that were accessed during simulation.", + "required": [ + "id" + ], + "properties": { + "id": { + "description": "Application index.", + "type": "integer", + "x-algorand-format": "uint64" + }, + "app-locals": { + "description": "An application's initial local states tied to different accounts.", + "type": "array", + "items": { + "$ref": "#/definitions/ApplicationKVStorage" + } + }, + "app-globals": { + "$ref": "#/definitions/ApplicationKVStorage" + }, + "app-boxes": { + "$ref": "#/definitions/ApplicationKVStorage" + } + } + }, "SimulationOpcodeTraceUnit": { "description": "The set of trace information and effect from evaluating a single opcode.", "type": "object", @@ -4275,6 +4348,19 @@ } } } + }, + "SimulateInitialStates": { + "description": "Initial states of resources that were accessed during simulation.", + "type": "object", + "properties": { + "app-initial-states": { + "description": "The initial states of accessed application before simulation. The order of this array is arbitrary.", + "type": "array", + "items": { + "$ref": "#/definitions/ApplicationInitialStates" + } + } + } } }, "parameters": { @@ -4925,6 +5011,9 @@ }, "exec-trace-config": { "$ref": "#/definitions/SimulateTraceConfig" + }, + "initial-states": { + "$ref": "#/definitions/SimulateInitialStates" } } } diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index ebe8cdc0df..ecb205869d 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -815,6 +815,9 @@ "exec-trace-config": { "$ref": "#/components/schemas/SimulateTraceConfig" }, + "initial-states": { + "$ref": "#/components/schemas/SimulateInitialStates" + }, "last-round": { "description": "The round immediately preceding this simulation. State changes through this round were used to run this simulation.", "type": "integer" @@ -1217,6 +1220,54 @@ ], "type": "object" }, + "ApplicationInitialStates": { + "description": "An application's initial global/local/box states that were accessed during simulation.", + "properties": { + "app-boxes": { + "$ref": "#/components/schemas/ApplicationKVStorage" + }, + "app-globals": { + "$ref": "#/components/schemas/ApplicationKVStorage" + }, + "app-locals": { + "description": "An application's initial local states tied to different accounts.", + "items": { + "$ref": "#/components/schemas/ApplicationKVStorage" + }, + "type": "array" + }, + "id": { + "description": "Application index.", + "type": "integer", + "x-algorand-format": "uint64" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "ApplicationKVStorage": { + "description": "An application's global/local/box state.", + "properties": { + "account": { + "description": "The address of the account associated with the local state.", + "type": "string", + "x-algorand-format": "Address" + }, + "kvs": { + "description": "Key-Value pairs representing application states.", + "items": { + "$ref": "#/components/schemas/AvmKeyValue" + }, + "type": "array" + } + }, + "required": [ + "kvs" + ], + "type": "object" + }, "ApplicationLocalReference": { "description": "References an account's local state for an application.", "properties": { @@ -1495,6 +1546,24 @@ ], "type": "object" }, + "AvmKeyValue": { + "description": "Represents an AVM key-value pair in an application store.", + "properties": { + "key": { + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "value": { + "$ref": "#/components/schemas/AvmValue" + } + }, + "required": [ + "key", + "value" + ], + "type": "object" + }, "AvmValue": { "description": "Represents an AVM value.", "properties": { @@ -2066,6 +2135,19 @@ ], "type": "object" }, + "SimulateInitialStates": { + "description": "Initial states of resources that were accessed during simulation.", + "properties": { + "app-initial-states": { + "description": "The initial states of accessed application before simulation. The order of this array is arbitrary.", + "items": { + "$ref": "#/components/schemas/ApplicationInitialStates" + }, + "type": "array" + } + }, + "type": "object" + }, "SimulateRequest": { "description": "Request type for simulation endpoint.", "properties": { @@ -2088,6 +2170,10 @@ "description": "Applies extra opcode budget during simulation for each transaction group.", "type": "integer" }, + "round": { + "description": "If provided, specifies the round preceding the simulation. State changes through this round will be used to run this simulation. Usually only the 4 most recent rounds will be available (controlled by the node config value MaxAcctLookback). If not specified, defaults to the latest available round.", + "type": "integer" + }, "txn-groups": { "description": "The transaction groups to simulate.", "items": { @@ -4216,6 +4302,14 @@ "x-algorand-format": "Catchpoint String" }, "x-algorand-format": "Catchpoint String" + }, + { + "description": "Specify the minimum number of blocks which the ledger must be advanced by in order to start the catchup. This is useful for simplifying tools which support fast catchup, they can run the catchup unconditionally and the node will skip the catchup if it is not needed.", + "in": "query", + "name": "min", + "schema": { + "type": "integer" + } } ], "responses": { @@ -6816,6 +6910,9 @@ "exec-trace-config": { "$ref": "#/components/schemas/SimulateTraceConfig" }, + "initial-states": { + "$ref": "#/components/schemas/SimulateInitialStates" + }, "last-round": { "description": "The round immediately preceding this simulation. State changes through this round were used to run this simulation.", "type": "integer" @@ -6849,6 +6946,9 @@ "exec-trace-config": { "$ref": "#/components/schemas/SimulateTraceConfig" }, + "initial-states": { + "$ref": "#/components/schemas/SimulateInitialStates" + }, "last-round": { "description": "The round immediately preceding this simulation. State changes through this round were used to run this simulation.", "type": "integer" diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go index 0448f650c4..6be81c35f5 100644 --- a/daemon/algod/api/client/restClient.go +++ b/daemon/algod/api/client/restClient.go @@ -389,6 +389,10 @@ type accountInformationParams struct { Exclude string `url:"exclude"` } +type catchupParams struct { + Min uint64 `url:"min"` +} + // PendingTransactionsByAddr returns all the pending transactions for an addr. func (client RestClient) PendingTransactionsByAddr(addr string, max uint64) (response model.PendingTransactionsResponse, err error) { err = client.get(&response, fmt.Sprintf("/v2/accounts/%s/transactions/pending", addr), pendingTransactionsByAddrParams{max}) @@ -575,8 +579,8 @@ func (client RestClient) AbortCatchup(catchpointLabel string) (response model.Ca } // Catchup start catching up to the give catchpoint label -func (client RestClient) Catchup(catchpointLabel string) (response model.CatchpointStartResponse, err error) { - err = client.submitForm(&response, fmt.Sprintf("/v2/catchup/%s", catchpointLabel), nil, nil, "POST", false, true, false) +func (client RestClient) Catchup(catchpointLabel string, minRounds uint64) (response model.CatchpointStartResponse, err error) { + err = client.submitForm(&response, fmt.Sprintf("/v2/catchup/%s", catchpointLabel), catchupParams{Min: minRounds}, nil, "POST", false, true, false) return } diff --git a/daemon/algod/api/server/v2/errors.go b/daemon/algod/api/server/v2/errors.go index 18a389edf6..d85ac7c0bc 100644 --- a/daemon/algod/api/server/v2/errors.go +++ b/daemon/algod/api/server/v2/errors.go @@ -44,6 +44,7 @@ var ( errFailedToParseCatchpoint = "failed to parse catchpoint" errFailedToAbortCatchup = "failed to abort catchup : %v" errFailedToStartCatchup = "failed to start catchup : %v" + errCatchpointWouldNotInitialize = "the node has already been initialized" errOperationNotAvailableDuringCatchup = "operation not available during catchup" errRESTPayloadZeroLength = "payload was of zero length" errRoundGreaterThanTheLatest = "given round is greater than the latest round" diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go index 3af90c3d66..a17b5f1ca8 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -114,204 +114,209 @@ 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/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go index 2448cb966d..77f2fccbea 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -90,205 +90,210 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "AAAA///Cen75tekAAA==", + "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", + "/fj5/wYAAP//423gG8XxAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go index 6fab39c06e..4be4676f9f 100644 --- a/daemon/algod/api/server/v2/generated/model/types.go +++ b/daemon/algod/api/server/v2/generated/model/types.go @@ -280,6 +280,30 @@ type Application struct { Params ApplicationParams `json:"params"` } +// ApplicationInitialStates An application's initial global/local/box states that were accessed during simulation. +type ApplicationInitialStates struct { + // AppBoxes An application's global/local/box state. + AppBoxes *ApplicationKVStorage `json:"app-boxes,omitempty"` + + // AppGlobals An application's global/local/box state. + AppGlobals *ApplicationKVStorage `json:"app-globals,omitempty"` + + // AppLocals An application's initial local states tied to different accounts. + AppLocals *[]ApplicationKVStorage `json:"app-locals,omitempty"` + + // Id Application index. + Id uint64 `json:"id"` +} + +// ApplicationKVStorage An application's global/local/box state. +type ApplicationKVStorage struct { + // Account The address of the account associated with the local state. + Account *string `json:"account,omitempty"` + + // Kvs Key-Value pairs representing application states. + Kvs []AvmKeyValue `json:"kvs"` +} + // ApplicationLocalReference References an account's local state for an application. type ApplicationLocalReference struct { // Account Address of the account with the local state. @@ -443,6 +467,14 @@ type AssetParams struct { UrlB64 *[]byte `json:"url-b64,omitempty"` } +// AvmKeyValue Represents an AVM key-value pair in an application store. +type AvmKeyValue struct { + Key []byte `json:"key"` + + // Value Represents an AVM value. + Value AvmValue `json:"value"` +} + // AvmValue Represents an AVM value. type AvmValue struct { // Bytes bytes value. @@ -696,6 +728,12 @@ type ScratchChange struct { Slot uint64 `json:"slot"` } +// SimulateInitialStates Initial states of resources that were accessed during simulation. +type SimulateInitialStates struct { + // AppInitialStates The initial states of accessed application before simulation. The order of this array is arbitrary. + AppInitialStates *[]ApplicationInitialStates `json:"app-initial-states,omitempty"` +} + // SimulateRequest Request type for simulation endpoint. type SimulateRequest struct { // AllowEmptySignatures Allows transactions without signatures to be simulated as if they had correct signatures. @@ -713,6 +751,9 @@ type SimulateRequest struct { // ExtraOpcodeBudget Applies extra opcode budget during simulation for each transaction group. ExtraOpcodeBudget *uint64 `json:"extra-opcode-budget,omitempty"` + // Round If provided, specifies the round preceding the simulation. State changes through this round will be used to run this simulation. Usually only the 4 most recent rounds will be available (controlled by the node config value MaxAcctLookback). If not specified, defaults to the latest available round. + Round *uint64 `json:"round,omitempty"` + // TxnGroups The transaction groups to simulate. TxnGroups []SimulateRequestTransactionGroup `json:"txn-groups"` } @@ -1240,6 +1281,9 @@ type SimulateResponse struct { // ExecTraceConfig An object that configures simulation execution trace. ExecTraceConfig *SimulateTraceConfig `json:"exec-trace-config,omitempty"` + // InitialStates Initial states of resources that were accessed during simulation. + InitialStates *SimulateInitialStates `json:"initial-states,omitempty"` + // LastRound The round immediately preceding this simulation. State changes through this round were used to run this simulation. LastRound uint64 `json:"last-round"` @@ -1408,6 +1452,12 @@ type GetTransactionProofParamsHashtype string // GetTransactionProofParamsFormat defines parameters for GetTransactionProof. type GetTransactionProofParamsFormat string +// StartCatchupParams defines parameters for StartCatchup. +type StartCatchupParams struct { + // Min Specify the minimum number of blocks which the ledger must be advanced by in order to start the catchup. This is useful for simplifying tools which support fast catchup, they can run the catchup unconditionally and the node will skip the catchup if it is not needed. + Min *uint64 `form:"min,omitempty" json:"min,omitempty"` +} + // GetLedgerStateDeltaForTransactionGroupParams defines parameters for GetLedgerStateDeltaForTransactionGroup. type GetLedgerStateDeltaForTransactionGroupParams struct { // Format Configures whether the response object is JSON or MessagePack encoded. If not provided, defaults to JSON. diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go index 91b3b3617f..04a1236519 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go @@ -26,7 +26,7 @@ type ServerInterface interface { AbortCatchup(ctx echo.Context, catchpoint string) error // Starts a catchpoint catchup. // (POST /v2/catchup/{catchpoint}) - StartCatchup(ctx echo.Context, catchpoint string) error + StartCatchup(ctx echo.Context, catchpoint string, params StartCatchupParams) error // (POST /v2/shutdown) ShutdownNode(ctx echo.Context, params ShutdownNodeParams) error @@ -68,8 +68,17 @@ func (w *ServerInterfaceWrapper) StartCatchup(ctx echo.Context) error { ctx.Set(Api_keyScopes, []string{""}) + // Parameter object where we will unmarshal all parameters from the context + var params StartCatchupParams + // ------------- Optional query parameter "min" ------------- + + err = runtime.BindQueryParameter("form", true, false, "min", ctx.QueryParams(), ¶ms.Min) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter min: %s", err)) + } + // Invoke the callback with all the unmarshalled arguments - err = w.Handler.StartCatchup(ctx, catchpoint) + err = w.Handler.StartCatchup(ctx, catchpoint, params) return err } @@ -130,205 +139,212 @@ 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/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go index d5c1c489b6..223956bf33 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -724,281 +724,286 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "nRuL5tP7T/83AAD//2UrnPtBngEA", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index 37433a761d..49c6517c19 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -158,208 +158,213 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "AA==", + "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", + "16auAQEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go index 207ec3f291..3003f8b27d 100644 --- a/daemon/algod/api/server/v2/generated/participating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go @@ -232,161 +232,166 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 4b50e84f95..bd40224f90 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -1003,6 +1003,7 @@ type PreEncodedSimulateResponse struct { TxnGroups []PreEncodedSimulateTxnGroupResult `codec:"txn-groups"` EvalOverrides *model.SimulationEvalOverrides `codec:"eval-overrides,omitempty"` ExecTraceConfig simulation.ExecTraceConfig `codec:"exec-trace-config,omitempty"` + InitialStates *model.SimulateInitialStates `codec:"initial-states,omitempty"` } // PreEncodedSimulateRequestTransactionGroup mirrors model.SimulateRequestTransactionGroup @@ -1013,6 +1014,7 @@ type PreEncodedSimulateRequestTransactionGroup struct { // PreEncodedSimulateRequest mirrors model.SimulateRequest type PreEncodedSimulateRequest struct { TxnGroups []PreEncodedSimulateRequestTransactionGroup `codec:"txn-groups"` + Round basics.Round `codec:"round,omitempty"` AllowEmptySignatures bool `codec:"allow-empty-signatures,omitempty"` AllowMoreLogging bool `codec:"allow-more-logging,omitempty"` AllowUnnamedResources bool `codec:"allow-unnamed-resources,omitempty"` @@ -1391,12 +1393,22 @@ func (v2 *Handlers) getPendingTransactions(ctx echo.Context, max *uint64, format } // startCatchup Given a catchpoint, it starts catching up to this catchpoint -func (v2 *Handlers) startCatchup(ctx echo.Context, catchpoint string) error { - _, _, err := ledgercore.ParseCatchpointLabel(catchpoint) +func (v2 *Handlers) startCatchup(ctx echo.Context, catchpoint string, minRounds uint64) error { + catchpointRound, _, err := ledgercore.ParseCatchpointLabel(catchpoint) if err != nil { return badRequest(ctx, err, errFailedToParseCatchpoint, v2.Log) } + if minRounds > 0 { + ledgerRound := v2.Node.LedgerForAPI().Latest() + if catchpointRound < (ledgerRound + basics.Round(minRounds)) { + v2.Log.Infof("Skipping catchup. Catchpoint round %d is not %d rounds ahead of the current round %d.", catchpointRound, minRounds, ledgerRound) + return ctx.JSON(http.StatusOK, model.CatchpointStartResponse{ + CatchupMessage: errCatchpointWouldNotInitialize, + }) + } + } + // Select 200/201, or return an error var code int err = v2.Node.StartCatchup(catchpoint) @@ -1598,8 +1610,9 @@ func (v2 *Handlers) GetPendingTransactionsByAddress(ctx echo.Context, addr strin // StartCatchup Given a catchpoint, it starts catching up to this catchpoint // (POST /v2/catchup/{catchpoint}) -func (v2 *Handlers) StartCatchup(ctx echo.Context, catchpoint string) error { - return v2.startCatchup(ctx, catchpoint) +func (v2 *Handlers) StartCatchup(ctx echo.Context, catchpoint string, params model.StartCatchupParams) error { + min := nilToZero(params.Min) + return v2.startCatchup(ctx, catchpoint, min) } // AbortCatchup Given a catchpoint, it aborts catching up to this catchpoint @@ -1647,7 +1660,7 @@ func (v2 *Handlers) TealCompile(ctx echo.Context, params model.TealCompileParams // If source map flag is enabled, then return the map. var sourcemap *logic.SourceMap if *params.Sourcemap { - rawmap := logic.GetSourceMap([]string{}, ops.OffsetToLine) + rawmap := logic.GetSourceMap([]string{""}, ops.OffsetToSource) sourcemap = &rawmap } diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index d59d9c971c..afe074edab 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -1308,6 +1308,10 @@ func TestSimulateTransactionMultipleGroups(t *testing.T) { } func startCatchupTest(t *testing.T, catchpoint string, nodeError error, expectedCode int) { + startCatchupTestFull(t, catchpoint, nodeError, expectedCode, 0, "") +} + +func startCatchupTestFull(t *testing.T, catchpoint string, nodeError error, expectedCode int, minRounds uint64, response string) { numAccounts := 1 numTransactions := 1 offlineAccounts := true @@ -1320,9 +1324,30 @@ func startCatchupTest(t *testing.T, catchpoint string, nodeError error, expected req := httptest.NewRequest(http.MethodPost, "/", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) - err := handler.StartCatchup(c, catchpoint) + var err error + if minRounds != 0 { + err = handler.StartCatchup(c, catchpoint, model.StartCatchupParams{Min: &minRounds}) + } else { + err = handler.StartCatchup(c, catchpoint, model.StartCatchupParams{}) + } require.NoError(t, err) require.Equal(t, expectedCode, rec.Code) + if response != "" { + require.Contains(t, rec.Body.String(), response) + } +} + +func TestStartCatchupInit(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + minRoundsToInitialize := uint64(1_000_000) + + tooSmallCatchpoint := fmt.Sprintf("%d#DVFRZUYHEFKRLK5N6DNJRR4IABEVN2D6H76F3ZSEPIE6MKXMQWQA", minRoundsToInitialize-1) + startCatchupTestFull(t, tooSmallCatchpoint, nil, 200, minRoundsToInitialize, "the node has already been initialized") + + catchpointOK := fmt.Sprintf("%d#DVFRZUYHEFKRLK5N6DNJRR4IABEVN2D6H76F3ZSEPIE6MKXMQWQA", minRoundsToInitialize) + startCatchupTestFull(t, catchpointOK, nil, 201, minRoundsToInitialize, catchpointOK) } func TestStartCatchup(t *testing.T) { @@ -1427,7 +1452,7 @@ int 1 assert int 1`, logic.AssemblerMaxVersion) ops, _ := logic.AssembleString(goodProgram) - expectedSourcemap := logic.GetSourceMap([]string{}, ops.OffsetToLine) + expectedSourcemap := logic.GetSourceMap([]string{""}, ops.OffsetToSource) goodProgramBytes := []byte(goodProgram) // Test good program with params @@ -1810,9 +1835,11 @@ func TestGetProofDefault(t *testing.T) { blkHdr, err := l.BlockHdr(1) a.NoError(err) - singleLeafProof, err := merklearray.ProofDataToSingleLeafProof(string(resp.Hashtype), resp.Treedepth, resp.Proof) + singleLeafProof, err := merklearray.ProofDataToSingleLeafProof(string(resp.Hashtype), resp.Proof) a.NoError(err) + a.Equal(uint64(singleLeafProof.TreeDepth), resp.Treedepth) + element := TxnMerkleElemRaw{Txn: crypto.Digest(txid)} copy(element.Stib[:], resp.Stibhash[:]) elems := make(map[uint64]crypto.Hashable) diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go index 85b44c1668..bc7e71527c 100644 --- a/daemon/algod/api/server/v2/utils.go +++ b/daemon/algod/api/server/v2/utils.go @@ -79,6 +79,16 @@ func convertSlice[X any, Y any](input []X, fn func(X) Y) []Y { return output } +func convertMap[X comparable, Y, Z any](input map[X]Y, fn func(X, Y) Z) []Z { + output := make([]Z, len(input)) + counter := 0 + for x, y := range input { + output[counter] = fn(x, y) + counter++ + } + return output +} + func uint64Slice[T ~uint64](s []T) []uint64 { return convertSlice(s, func(t T) uint64 { return uint64(t) }) } @@ -494,6 +504,51 @@ func convertUnnamedResourcesAccessed(resources *simulation.ResourceTracker) *mod } } +func convertAppKVStorePtr(address basics.Address, appKVPairs simulation.AppKVPairs) *model.ApplicationKVStorage { + if len(appKVPairs) == 0 && address.IsZero() { + return nil + } + return &model.ApplicationKVStorage{ + Account: addrOrNil(address), + Kvs: convertMap(appKVPairs, func(key string, value basics.TealValue) model.AvmKeyValue { + return model.AvmKeyValue{ + Key: []byte(key), + Value: convertToAVMValue(value), + } + }), + } +} + +func convertAppKVStoreInstance(address basics.Address, appKVPairs simulation.AppKVPairs) model.ApplicationKVStorage { + return model.ApplicationKVStorage{ + Account: addrOrNil(address), + Kvs: convertMap(appKVPairs, func(key string, value basics.TealValue) model.AvmKeyValue { + return model.AvmKeyValue{ + Key: []byte(key), + Value: convertToAVMValue(value), + } + }), + } +} + +func convertApplicationInitialStates(appID basics.AppIndex, states simulation.SingleAppInitialStates) model.ApplicationInitialStates { + return model.ApplicationInitialStates{ + Id: uint64(appID), + AppBoxes: convertAppKVStorePtr(basics.Address{}, states.AppBoxes), + AppGlobals: convertAppKVStorePtr(basics.Address{}, states.AppGlobals), + AppLocals: sliceOrNil(convertMap(states.AppLocals, convertAppKVStoreInstance)), + } +} + +func convertSimulateInitialStates(initialStates *simulation.ResourcesInitialStates) *model.SimulateInitialStates { + if initialStates == nil { + return nil + } + return &model.SimulateInitialStates{ + AppInitialStates: sliceOrNil(convertMap(initialStates.AllAppsInitialStates, convertApplicationInitialStates)), + } +} + func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult) PreEncodedSimulateTxnGroupResult { txnResults := make([]PreEncodedSimulateTxnResult, len(txnGroupResult.Txns)) for i, txnResult := range txnGroupResult.Txns { @@ -528,19 +583,14 @@ func convertSimulationResult(result simulation.Result) PreEncodedSimulateRespons } } - encodedSimulationResult := PreEncodedSimulateResponse{ + return PreEncodedSimulateResponse{ Version: result.Version, LastRound: uint64(result.LastRound), - TxnGroups: make([]PreEncodedSimulateTxnGroupResult, len(result.TxnGroups)), + TxnGroups: convertSlice(result.TxnGroups, convertTxnGroupResult), EvalOverrides: evalOverrides, ExecTraceConfig: result.TraceConfig, + InitialStates: convertSimulateInitialStates(result.InitialStates), } - - for i, txnGroup := range result.TxnGroups { - encodedSimulationResult.TxnGroups[i] = convertTxnGroupResult(txnGroup) - } - - return encodedSimulationResult } func convertSimulationRequest(request PreEncodedSimulateRequest) simulation.Request { @@ -550,6 +600,7 @@ func convertSimulationRequest(request PreEncodedSimulateRequest) simulation.Requ } return simulation.Request{ TxnGroups: txnGroups, + Round: request.Round, AllowEmptySignatures: request.AllowEmptySignatures, AllowMoreLogging: request.AllowMoreLogging, AllowUnnamedResources: request.AllowUnnamedResources, diff --git a/daemon/algod/api/spec/v2/msgp_gen.go b/daemon/algod/api/spec/v2/msgp_gen.go index 45304fe948..f8afb04767 100644 --- a/daemon/algod/api/spec/v2/msgp_gen.go +++ b/daemon/algod/api/spec/v2/msgp_gen.go @@ -76,11 +76,11 @@ func (_ *AccountApplicationModel) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AccountApplicationModel) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -285,11 +285,11 @@ func (_ *AccountAssetModel) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AccountAssetModel) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/daemon/algod/server.go b/daemon/algod/server.go index 80d0afeed3..1b40e98bfb 100644 --- a/daemon/algod/server.go +++ b/daemon/algod/server.go @@ -81,8 +81,8 @@ func (s *Server) Initialize(cfg config.Local, phonebookAddresses []string, genes lib.GenesisJSONText = genesisText - liveLog := filepath.Join(s.RootPath, "node.log") - archive := filepath.Join(s.RootPath, cfg.LogArchiveName) + liveLog, archive := cfg.ResolveLogPaths(s.RootPath) + var maxLogAge time.Duration var err error if cfg.LogArchiveMaxAge != "" { diff --git a/data/account/msgp_gen.go b/data/account/msgp_gen.go index b7e6423046..36459a83f3 100644 --- a/data/account/msgp_gen.go +++ b/data/account/msgp_gen.go @@ -106,11 +106,11 @@ func (_ *ParticipationKeyIdentity) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ParticipationKeyIdentity) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -291,11 +291,11 @@ func (_ StateProofKeys) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProofKeys) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0002 int var zb0003 bool zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) diff --git a/data/basics/msgp_gen.go b/data/basics/msgp_gen.go index a317fda37e..06190153d6 100644 --- a/data/basics/msgp_gen.go +++ b/data/basics/msgp_gen.go @@ -543,11 +543,11 @@ func (_ *AccountData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0009 int @@ -1500,11 +1500,11 @@ func (_ AppIndex) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AppIndex) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -1618,11 +1618,11 @@ func (_ *AppLocalState) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AppLocalState) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -2052,11 +2052,11 @@ func (_ *AppParams) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AppParams) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -2617,11 +2617,11 @@ func (_ *AssetHolding) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetHolding) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -2739,11 +2739,11 @@ func (_ AssetIndex) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetIndex) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -2901,11 +2901,11 @@ func (_ *AssetParams) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetParams) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -3501,11 +3501,11 @@ func (_ *BalanceRecord) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0009 int @@ -4436,11 +4436,11 @@ func (_ CreatableIndex) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *CreatableIndex) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -4496,11 +4496,11 @@ func (_ CreatableType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *CreatableType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -4556,11 +4556,11 @@ func (_ DeltaAction) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *DeltaAction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -4637,11 +4637,11 @@ func (_ *Participant) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Participant) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -4759,11 +4759,11 @@ func (_ Round) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Round) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -4819,11 +4819,11 @@ func (_ RoundInterval) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *RoundInterval) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -4894,11 +4894,11 @@ func (_ StateDelta) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateDelta) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0003 int var zb0004 bool zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) @@ -5010,11 +5010,11 @@ func (_ *StateSchema) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateSchema) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -5197,11 +5197,11 @@ func (_ *StateSchemas) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateSchemas) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -5575,11 +5575,11 @@ func (_ Status) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Status) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 byte zb0001, bts, err = msgp.ReadByteBytes(bts) @@ -5650,11 +5650,11 @@ func (_ TealKeyValue) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TealKeyValue) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0003 int var zb0004 bool zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) @@ -5745,11 +5745,11 @@ func (_ TealType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TealType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -5835,11 +5835,11 @@ func (_ *TealValue) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TealValue) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -6011,11 +6011,11 @@ func (_ *ValueDelta) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ValueDelta) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/data/basics/units.go b/data/basics/units.go index d94ca95b6e..c1b8f413b2 100644 --- a/data/basics/units.go +++ b/data/basics/units.go @@ -99,7 +99,7 @@ func (a *MicroAlgos) UnmarshalMsg(bts []byte) (o []byte, err error) { // UnmarshalMsgWithState implements msgp.Unmarshaler func (a *MicroAlgos) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { return nil, msgp.ErrMaxDepthExceeded{} } a.Raw, o, err = msgp.ReadUint64Bytes(bts) diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index 3ed5667dd8..7eda4de555 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -415,11 +415,11 @@ func (_ *Block) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -1300,11 +1300,11 @@ func (_ *BlockHeader) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -1972,11 +1972,11 @@ func (_ *Genesis) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Genesis) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -2324,11 +2324,11 @@ func (_ *GenesisAccountData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *GenesisAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -2536,11 +2536,11 @@ func (_ *GenesisAllocation) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *GenesisAllocation) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -2715,11 +2715,11 @@ func (_ *LightBlockHeader) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *LightBlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -2884,11 +2884,11 @@ func (_ *ParticipationUpdates) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -3096,11 +3096,11 @@ func (_ *RewardsState) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *RewardsState) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3304,11 +3304,11 @@ func (_ *StateProofTrackingData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProofTrackingData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3461,11 +3461,11 @@ func (_ *TxnCommitments) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TxnCommitments) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3613,11 +3613,11 @@ func (_ *UpgradeVote) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *UpgradeVote) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/data/committee/msgp_gen.go b/data/committee/msgp_gen.go index e028a24054..9af6fdd4d8 100644 --- a/data/committee/msgp_gen.go +++ b/data/committee/msgp_gen.go @@ -116,11 +116,11 @@ func (_ *Credential) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Credential) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -277,11 +277,11 @@ func (_ *Seed) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Seed) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -346,11 +346,11 @@ func (_ *UnauthenticatedCredential) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *UnauthenticatedCredential) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -484,11 +484,11 @@ func (_ *hashableCredential) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *hashableCredential) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/data/hashable/msgp_gen.go b/data/hashable/msgp_gen.go index 09b46dd2c5..6906f7528f 100644 --- a/data/hashable/msgp_gen.go +++ b/data/hashable/msgp_gen.go @@ -47,11 +47,11 @@ func (_ *Message) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Message) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/data/ledger.go b/data/ledger.go index 27949168fe..141fae6cf8 100644 --- a/data/ledger.go +++ b/data/ledger.go @@ -78,8 +78,8 @@ type roundSeed struct { // LoadLedger creates a Ledger object to represent the ledger with the // specified database file prefix, initializing it if necessary. -func LoadLedger( - log logging.Logger, dbFilenamePrefix string, memory bool, +func LoadLedger[T string | ledger.DirsAndPrefix]( + log logging.Logger, dir T, memory bool, genesisProto protocol.ConsensusVersion, genesisBal bookkeeping.GenesisBalances, genesisID string, genesisHash crypto.Digest, blockListeners []ledgercore.BlockListener, cfg config.Local, ) (*Ledger, error) { @@ -107,9 +107,9 @@ func LoadLedger( Accounts: genesisBal.Balances, GenesisHash: genesisHash, } - l.log.Debugf("Initializing Ledger(%s)", dbFilenamePrefix) + l.log.Debugf("Initializing Ledger(%v)", dir) - ll, err := ledger.OpenLedger(log, dbFilenamePrefix, memory, genesisInitState, cfg) + ll, err := ledger.OpenLedger(log, dir, memory, genesisInitState, cfg) if err != nil { return nil, err } diff --git a/data/stateproofmsg/msgp_gen.go b/data/stateproofmsg/msgp_gen.go index 6bcfccd18a..c6d6396a2b 100644 --- a/data/stateproofmsg/msgp_gen.go +++ b/data/stateproofmsg/msgp_gen.go @@ -85,11 +85,11 @@ func (_ *Message) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Message) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/data/transactions/logic/Makefile b/data/transactions/logic/Makefile index ca21e852da..4ed18ecbc1 100644 --- a/data/transactions/logic/Makefile +++ b/data/transactions/logic/Makefile @@ -7,15 +7,13 @@ DOCS := ../../../../docs TEAL_opcodes.md teal.tmLanguage.json: fields_string.go ../../../cmd/opdoc/opdoc.go ../../../cmd/opdoc/tmLanguage.go eval.go assembler.go doc.go opcodes.go go run ../../../cmd/opdoc/opdoc.go ../../../cmd/opdoc/tmLanguage.go + @cp `ls TEAL_opcodes_v??.md | tail -1` TEAL_opcodes.md @if [ -e $(SPECS)/dev/TEAL_opcodes.md ]; then \ sed '/^$$/q' $(SPECS)/dev/TEAL_opcodes.md | cat - TEAL_opcodes.md > opcodes.spec; \ mv opcodes.spec $(SPECS)/dev/TEAL_opcodes.md; \ echo "TEAL_opcodes.md updated in specs repo"; \ fi - @if [ -e $(DOCS)/docs/reference/teal/opcodes.md ]; then \ - sed 's/^# /title: /g' TEAL_opcodes.md > $(DOCS)/docs/reference/teal/opcodes.md; \ - echo "opcodes.md updated in docs repo"; \ - fi + @rm TEAL_opcodes.md fields_string.go: fields.go go generate @@ -24,10 +22,9 @@ README.md: TEAL_opcodes.md README_in.md python merge.py > README.md @if [ -e $(SPECS)/dev/TEAL.md ]; then \ sed '/^$$/q' $(SPECS)/dev/TEAL.md | cat - README.md > teal.spec; \ - mv teal.spec $(SPECS)/dev/TEAL.md; \ + mv teal.spec $(SPECS)/dev/TEAL.md; \ echo "TEAL.md updated in specs repo"; \ fi - @if [ -e $(DOCS)/docs/reference/teal/specification.md ]; then \ - sed 's/^# /title: /g' README.md > $(DOCS)/docs/reference/teal/specification.md; \ - echo "specification.md updated in docs repo"; \ - fi + +clean: + rm -f TEAL_opcodes*.md langspec_v*.json README.md fields_string.go diff --git a/data/transactions/logic/README.md b/data/transactions/logic/README.md index 735bdc45d4..07b4a078be 100644 --- a/data/transactions/logic/README.md +++ b/data/transactions/logic/README.md @@ -373,6 +373,12 @@ return stack matches the name of the input value. | `ecdsa_pk_recover v` | for (data A, recovery id B, signature C, D) recover a public key | | `ecdsa_pk_decompress v` | decompress pubkey A into components X, Y | | `vrf_verify s` | Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. | +| `ec_add g` | for curve points A and B, return the curve point A + B | +| `ec_scalar_mul g` | for curve point A and scalar B, return the curve point BA, the point A multiplied by the scalar B. | +| `ec_pairing_check g` | 1 if the product of the pairing of each point in A with its respective point in B is equal to the identity element of the target group Gt, else 0 | +| `ec_multi_scalar_mul g` | for curve points A and scalars B, return curve point B0A0 + B1A1 + B2A2 + ... + BnAn | +| `ec_subgroup_check g` | 1 if A is in the main prime-order subgroup of G (including the point at infinity) else 0. Program fails if A is not in G at all. | +| `ec_map_to g` | maps field element A to group G | | `+` | A plus B. Fail on overflow. | | `-` | A minus B. Fail if B > A. | | `/` | A divided by B (truncated division). Fail if B == 0. | diff --git a/data/transactions/logic/TEAL_opcodes_v1.md b/data/transactions/logic/TEAL_opcodes_v1.md new file mode 100644 index 0000000000..f9ef580cfc --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v1.md @@ -0,0 +1,401 @@ +# v1 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 7 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 26 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 9 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 +- Mode: Signature + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | Sender | address | 32 byte address | +| 1 | Fee | uint64 | microalgos | +| 2 | FirstValid | uint64 | round number | +| 4 | LastValid | uint64 | round number | +| 5 | Note | []byte | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | 32 byte lease value | +| 7 | Receiver | address | 32 byte address | +| 8 | Amount | uint64 | microalgos | +| 9 | CloseRemainderTo | address | 32 byte address | +| 10 | VotePK | [32]byte | 32 byte address | +| 11 | SelectionPK | [32]byte | 32 byte address | +| 12 | VoteFirst | uint64 | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | Dilution for the 2-level participation key | +| 15 | Type | []byte | Transaction type as bytes | +| 16 | TypeEnum | uint64 | Transaction type as integer | +| 17 | XferAsset | uint64 | Asset ID | +| 18 | AssetAmount | uint64 | value in Asset's units | +| 19 | AssetSender | address | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | 32 byte address | +| 21 | AssetCloseTo | address | 32 byte address | +| 22 | GroupIndex | uint64 | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | The computed ID for this transaction. 32 bytes. | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | MinTxnFee | uint64 | microalgos | +| 1 | MinBalance | uint64 | microalgos | +| 2 | MaxTxnLife | uint64 | rounds | +| 3 | ZeroAddress | address | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | Number of transactions in this atomic transaction group. At least 1 | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A diff --git a/data/transactions/logic/TEAL_opcodes_v10.md b/data/transactions/logic/TEAL_opcodes_v10.md new file mode 100644 index 0000000000..31beb6c388 --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v10.md @@ -0,0 +1,1729 @@ +# v10 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## ecdsa_verify + +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool +- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: Secp256k1=1700; Secp256r1=2500 +- Availability: v5 + +### ECDSA + +Curves + +| Index | Name | In | Notes | +| - | ------ | - | --------- | +| 0 | Secp256k1 | | secp256k1 curve, used in Bitcoin | +| 1 | Secp256r1 | v7 | secp256r1 curve, NIST standard | + + +The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. + +## ecdsa_pk_decompress + +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} +- Stack: ..., A: []byte → ..., X: []byte, Y: []byte +- decompress pubkey A into components X, Y +- **Cost**: Secp256k1=650; Secp256r1=2400 +- Availability: v5 + +The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. + +## ecdsa_pk_recover + +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} +- Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte +- for (data A, recovery id B, signature C, D) recover a public key +- **Cost**: 2000 +- Availability: v5 + +S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | +| 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | +| 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | +| 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | +| 62 | LastLog | []byte | v6 | The last message emitted. Empty bytes if none were emitted. Application mode only | +| 63 | StateProofPK | []byte | v6 | 64 byte state proof public key | +| 65 | NumApprovalProgramPages | uint64 | v7 | Number of Approval Program pages | +| 67 | NumClearStateProgramPages | uint64 | v7 | Number of ClearState Program pages | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | +| 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | +| 15 | AssetCreateMinBalance | uint64 | v10 | The additional minimum balance required to create (and opt-in to) an asset. | +| 16 | AssetOptInMinBalance | uint64 | v10 | The additional minimum balance required to opt-in to an asset. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | +| 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | +| 64 | ApprovalProgramPages | []byte | v7 | Approval Program as an array of pages | +| 66 | ClearStateProgramPages | []byte | v7 | ClearState Program as an array of pages | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## loads + +- Bytecode: 0x3e +- Stack: ..., A: uint64 → ..., any +- Ath scratch space value. All scratch spaces are 0 at program start. +- Availability: v5 + +## stores + +- Bytecode: 0x3f +- Stack: ..., A: uint64, B → ... +- store B to the Ath scratch space +- Availability: v5 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## bury + +- Syntax: `bury N` ∋ N: depth +- Bytecode: 0x45 {uint8} +- Stack: ..., A → ... +- replace the Nth value from the top of the stack with A. bury 0 fails. +- Availability: v8 + +## popn + +- Syntax: `popn N` ∋ N: stack depth +- Bytecode: 0x46 {uint8} +- Stack: ..., [N items] → ... +- remove N values from the top of the stack +- Availability: v8 + +## dupn + +- Syntax: `dupn N` ∋ N: copy count +- Bytecode: 0x47 {uint8} +- Stack: ..., A → ..., A, [N copies of A] +- duplicate A, N times +- Availability: v8 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## cover + +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} +- Stack: ..., [N items], A → ..., A, [N items] +- remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. +- Availability: v5 + +## uncover + +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} +- Stack: ..., A, [N items] → ..., [N items], A +- remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. +- Availability: v5 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## extract + +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails +- Availability: v5 + +## extract3 + +- Bytecode: 0x58 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. +- Availability: v5 + +## extract_uint16 + +- Bytecode: 0x59 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint32 + +- Bytecode: 0x5a +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint64 + +- Bytecode: 0x5b +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails +- Availability: v5 + +## replace2 + +- Syntax: `replace2 S` ∋ S: start position +- Bytecode: 0x5c {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)
`replace2` can be called using `replace` with 1 immediate. +- Availability: v7 + +## replace3 + +- Bytecode: 0x5d +- Stack: ..., A: []byte, B: uint64, C: []byte → ..., []byte +- Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)
`replace3` can be called using `replace` with no immediates. +- Availability: v7 + +## base64_decode + +- Syntax: `base64_decode E` ∋ E: [base64](#field-group-base64) +- Bytecode: 0x5e {uint8} +- Stack: ..., A: []byte → ..., []byte +- decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E +- **Cost**: 1 + 1 per 16 bytes of A +- Availability: v7 + +### base64 + +Encodings + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | URLEncoding | | +| 1 | StdEncoding | | + + +*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings. This opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings. + + Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`. + +## json_ref + +- Syntax: `json_ref R` ∋ R: [json_ref](#field-group-json_ref) +- Bytecode: 0x5f {uint8} +- Stack: ..., A: []byte, B: []byte → ..., any +- key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A +- **Cost**: 25 + 2 per 7 bytes of A +- Availability: v7 + +### json_ref + +Types + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | JSONString | []byte | | +| 1 | JSONUint64 | uint64 | | +| 2 | JSONObject | []byte | | + + +*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size. + +Almost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON. + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AssetTotal | uint64 | | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | +| 3 | AssetUnitName | []byte | | Asset unit name | +| 4 | AssetName | []byte | | Asset name | +| 5 | AssetURL | []byte | | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## app_params_get + +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from app A. Y is 1 if A exists, else 0 +- Availability: v5 +- Mode: Application + +### app_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AppApprovalProgram | []byte | Bytecode of Approval Program | +| 1 | AppClearStateProgram | []byte | Bytecode of Clear State Program | +| 2 | AppGlobalNumUint | uint64 | Number of uint64 values allowed in Global State | +| 3 | AppGlobalNumByteSlice | uint64 | Number of byte array values allowed in Global State | +| 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | +| 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | +| 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | + + +params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. + +## acct_params_get + +- Syntax: `acct_params_get F` ∋ F: [acct_params](#field-group-acct_params) +- Bytecode: 0x73 {uint8} +- Stack: ..., A → ..., X: any, Y: bool +- X is field F from account A. Y is 1 if A owns positive algos, else 0 +- Availability: v6 +- Mode: Application + +### acct_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AcctBalance | uint64 | | Account balance in microalgos | +| 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | +| 2 | AcctAuthAddr | address | | Address the account is rekeyed to. | +| 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | +| 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | +| 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | +| 6 | AcctTotalAppsCreated | uint64 | v8 | The number of existing apps created by this account. | +| 7 | AcctTotalAppsOptedIn | uint64 | v8 | The number of apps this account is opted into. | +| 8 | AcctTotalAssetsCreated | uint64 | v8 | The number of existing ASAs created by this account. | +| 9 | AcctTotalAssets | uint64 | v8 | The numbers of ASAs held by this account (including ASAs this account created). | +| 10 | AcctTotalBoxes | uint64 | v8 | The number of existing boxes created by this account's app. | +| 11 | AcctTotalBoxBytes | uint64 | v8 | The total number of bytes used by this account's app's box keys and values. | + + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## pushbytess + +- Syntax: `pushbytess BYTES ...` ∋ BYTES ...: a list of byte constants +- Bytecode: 0x82 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ..., [N items] +- push sequences of immediate byte arrays to stack (first byte array being deepest) +- Availability: v8 + +pushbytess args are not added to the bytecblock during assembly processes + +## pushints + +- Syntax: `pushints UINT ...` ∋ UINT ...: a list of int constants +- Bytecode: 0x83 {varuint count, [varuint ...]} +- Stack: ... → ..., [N items] +- push sequence of immediate uints to stack in the order they appear (first uint being deepest) +- Availability: v8 + +pushints args are not added to the intcblock during assembly processes + +## ed25519verify_bare + +- Bytecode: 0x84 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: 1900 +- Availability: v7 + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## proto + +- Syntax: `proto A R` ∋ A: number of arguments, R: number of return values +- Bytecode: 0x8a {uint8}, {uint8} +- Stack: ... → ... +- Prepare top call frame for a retsub that will assume A args and R return values. +- Availability: v8 + +Fails unless the last instruction executed was a `callsub`. + +## frame_dig + +- Syntax: `frame_dig I` ∋ I: frame slot +- Bytecode: 0x8b {int8} +- Stack: ... → ..., any +- Nth (signed) value from the frame pointer. +- Availability: v8 + +## frame_bury + +- Syntax: `frame_bury I` ∋ I: frame slot +- Bytecode: 0x8c {int8} +- Stack: ..., A → ... +- replace the Nth (signed) value from the frame pointer in the stack with A +- Availability: v8 + +## switch + +- Syntax: `switch TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8d {varuint count, [int16 (big-endian) ...]} +- Stack: ..., A: uint64 → ... +- branch to the Ath label. Continue at following instruction if index A exceeds the number of labels. +- Availability: v8 + +## match + +- Syntax: `match TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8e {varuint count, [int16 (big-endian) ...]} +- Stack: ..., [A1, A2, ..., AN], B → ... +- given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found. +- Availability: v8 + +`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## bsqrt + +- Bytecode: 0x96 +- Stack: ..., A: []byte → ..., []byte +- The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers +- **Cost**: 40 +- Availability: v6 + +## divw + +- Bytecode: 0x97 +- Stack: ..., A: uint64, B: uint64, C: uint64 → ..., uint64 +- A,B / C. Fail if C == 0 or if result overflows. +- Availability: v6 + +The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low. + +## sha3_256 + +- Bytecode: 0x98 +- Stack: ..., A: []byte → ..., []byte +- SHA3_256 hash of value A, yields [32]byte +- **Cost**: 130 +- Availability: v7 + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 + +## log + +- Bytecode: 0xb0 +- Stack: ..., A: []byte → ... +- write A to log state of the current application +- Availability: v5 +- Mode: Application + +`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes. + +## itxn_begin + +- Bytecode: 0xb1 +- Stack: ... → ... +- begin preparation of a new inner transaction in a new transaction group +- Availability: v5 +- Mode: Application + +`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. + +## itxn_field + +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} +- Stack: ..., A → ... +- set field F of the current inner transaction to A +- Availability: v5 +- Mode: Application + +`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.) + +## itxn_submit + +- Bytecode: 0xb3 +- Stack: ... → ... +- execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. +- Availability: v5 +- Mode: Application + +`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. + +## itxn + +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} +- Stack: ... → ..., any +- field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxna + +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxn_next + +- Bytecode: 0xb6 +- Stack: ... → ... +- begin preparation of a new inner transaction in the same transaction group +- Availability: v6 +- Mode: Application + +`itxn_next` initializes the transaction exactly as `itxn_begin` does + +## gitxn + +- Syntax: `gitxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0xb7 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## gitxna + +- Syntax: `gitxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0xb8 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## box_create + +- Bytecode: 0xb9 +- Stack: ..., A: boxName, B: uint64 → ..., bool +- create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 +- Availability: v8 +- Mode: Application + +Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`. + +## box_extract + +- Bytecode: 0xba +- Stack: ..., A: boxName, B: uint64, C: uint64 → ..., []byte +- read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. +- Availability: v8 +- Mode: Application + +## box_replace + +- Bytecode: 0xbb +- Stack: ..., A: boxName, B: uint64, C: []byte → ... +- write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. +- Availability: v8 +- Mode: Application + +## box_del + +- Bytecode: 0xbc +- Stack: ..., A: boxName → ..., bool +- delete box named A if it exists. Return 1 if A existed, 0 otherwise +- Availability: v8 +- Mode: Application + +## box_len + +- Bytecode: 0xbd +- Stack: ..., A: boxName → ..., X: uint64, Y: bool +- X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. +- Availability: v8 +- Mode: Application + +## box_get + +- Bytecode: 0xbe +- Stack: ..., A: boxName → ..., X: []byte, Y: bool +- X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. +- Availability: v8 +- Mode: Application + +For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace` + +## box_put + +- Bytecode: 0xbf +- Stack: ..., A: boxName, B: []byte → ... +- replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist +- Availability: v8 +- Mode: Application + +For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace` + +## txnas + +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the current transaction +- Availability: v5 + +## gtxnas + +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the current group +- Availability: v5 + +## gtxnsas + +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth value of the array field F from the Ath transaction in the current group +- Availability: v5 + +## args + +- Bytecode: 0xc3 +- Stack: ..., A: uint64 → ..., []byte +- Ath LogicSig argument +- Availability: v5 +- Mode: Signature + +## gloadss + +- Bytecode: 0xc4 +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth scratch space value of the Ath transaction in the current group +- Availability: v6 +- Mode: Application + +## itxnas + +- Syntax: `itxnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc5 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the last inner transaction +- Availability: v6 +- Mode: Application + +## gitxnas + +- Syntax: `gitxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc6 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## vrf_verify + +- Syntax: `vrf_verify S` ∋ S: [vrf_verify](#field-group-vrf_verify) +- Bytecode: 0xd0 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: bool +- Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. +- **Cost**: 5700 +- Availability: v7 + +### vrf_verify + +Standards + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | VrfAlgorand | | + + +`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/). + +## block + +- Syntax: `block F` ∋ F: [block](#field-group-block) +- Bytecode: 0xd1 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive) +- Availability: v7 + +### block + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | BlkSeed | []byte | | +| 1 | BlkTimestamp | uint64 | | + + +## ec_add + +- Syntax: `ec_add G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe0 {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- for curve points A and B, return the curve point A + B +- **Cost**: BN254g1=125; BN254g2=170; BLS12_381g1=205; BLS12_381g2=290 +- Availability: v10 + +### EC + +Groups + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | BN254g1 | G1 of the BN254 curve. Points encoded as 32 byte X following by 32 byte Y | +| 1 | BN254g2 | G2 of the BN254 curve. Points encoded as 64 byte X following by 64 byte Y | +| 2 | BLS12_381g1 | G1 of the BLS 12-381 curve. Points encoded as 48 byte X following by 48 byte Y | +| 3 | BLS12_381g2 | G2 of the BLS 12-381 curve. Points encoded as 96 byte X following by 96 byte Y | + + +A and B are curve points in affine representation: field element X concatenated with field element Y. Field element `Z` is encoded as follows. +For the base field elements (Fp), `Z` is encoded as a big-endian number and must be lower than the field modulus. +For the quadratic field extension (Fp2), `Z` is encoded as the concatenation of the individual encoding of the coefficients. For an Fp2 element of the form `Z = Z0 + Z1 i`, where `i` is a formal quadratic non-residue, the encoding of Z is the concatenation of the encoding of `Z0` and `Z1` in this order. (`Z0` and `Z1` must be less than the field modulus). + +The point at infinity is encoded as `(X,Y) = (0,0)`. +Groups G1 and G2 are denoted additively. + +Fails if A or B is not in G. +A and/or B are allowed to be the point at infinity. +Does _not_ check if A and B are in the main prime-order subgroup. + +## ec_scalar_mul + +- Syntax: `ec_scalar_mul G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe1 {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- for curve point A and scalar B, return the curve point BA, the point A multiplied by the scalar B. +- **Cost**: BN254g1=1810; BN254g2=3430; BLS12_381g1=2950; BLS12_381g2=6530 +- Availability: v10 + +A is a curve point encoded and checked as described in `ec_add`. Scalar B is interpreted as a big-endian unsigned integer. Fails if B exceeds 32 bytes. + +## ec_pairing_check + +- Syntax: `ec_pairing_check G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe2 {uint8} +- Stack: ..., A: []byte, B: []byte → ..., bool +- 1 if the product of the pairing of each point in A with its respective point in B is equal to the identity element of the target group Gt, else 0 +- **Cost**: BN254g1=8000 + 7400 per 64 bytes of B; BN254g2=8000 + 7400 per 128 bytes of B; BLS12_381g1=13000 + 10000 per 96 bytes of B; BLS12_381g2=13000 + 10000 per 192 bytes of B +- Availability: v10 + +A and B are concatenated points, encoded and checked as described in `ec_add`. A contains points of the group G, B contains points of the associated group (G2 if G is G1, and vice versa). Fails if A and B have a different number of points, or if any point is not in its described group or outside the main prime-order subgroup - a stronger condition than other opcodes. AVM values are limited to 4096 bytes, so `ec_pairing_check` is limited by the size of the points in the groups being operated upon. + +## ec_multi_scalar_mul + +- Syntax: `ec_multi_scalar_mul G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe3 {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- for curve points A and scalars B, return curve point B0A0 + B1A1 + B2A2 + ... + BnAn +- **Cost**: BN254g1=3600 + 90 per 32 bytes of B; BN254g2=7200 + 270 per 32 bytes of B; BLS12_381g1=6500 + 95 per 32 bytes of B; BLS12_381g2=14850 + 485 per 32 bytes of B +- Availability: v10 + +A is a list of concatenated points, encoded and checked as described in `ec_add`. B is a list of concatenated scalars which, unlike ec_scalar_mul, must all be exactly 32 bytes long. +The name `ec_multi_scalar_mul` was chosen to reflect common usage, but a more consistent name would be `ec_multi_scalar_mul`. AVM values are limited to 4096 bytes, so `ec_multi_scalar_mul` is limited by the size of the points in the group being operated upon. + +## ec_subgroup_check + +- Syntax: `ec_subgroup_check G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe4 {uint8} +- Stack: ..., A: []byte → ..., bool +- 1 if A is in the main prime-order subgroup of G (including the point at infinity) else 0. Program fails if A is not in G at all. +- **Cost**: BN254g1=20; BN254g2=3100; BLS12_381g1=1850; BLS12_381g2=2340 +- Availability: v10 + +## ec_map_to + +- Syntax: `ec_map_to G` ∋ G: [EC](#field-group-ec) +- Bytecode: 0xe5 {uint8} +- Stack: ..., A: []byte → ..., []byte +- maps field element A to group G +- **Cost**: BN254g1=630; BN254g2=3300; BLS12_381g1=1950; BLS12_381g2=8150 +- Availability: v10 + +BN254 points are mapped by the SVDW map. BLS12-381 points are mapped by the SSWU map. +G1 element inputs are base field elements and G2 element inputs are quadratic field elements, with nearly the same encoding rules (for field elements) as defined in `ec_add`. There is one difference of encoding rule: G1 element inputs do not need to be 0-padded if they fit in less than 32 bytes for BN254 and less than 48 bytes for BLS12-381. (As usual, the empty byte array represents 0.) G2 elements inputs need to be always have the required size. diff --git a/data/transactions/logic/TEAL_opcodes_v2.md b/data/transactions/logic/TEAL_opcodes_v2.md new file mode 100644 index 0000000000..52e65591bc --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v2.md @@ -0,0 +1,671 @@ +# v2 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 +- Mode: Signature + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 26 | ApplicationArgs | []byte | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | Accounts listed in the ApplicationCall transaction | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## balance + +- Bytecode: 0x60 +- Stack: ..., A: uint64 → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A: uint64, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A: uint64, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A: uint64, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A: uint64, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetTotal | uint64 | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | Frozen by default or not | +| 3 | AssetUnitName | []byte | Asset unit name | +| 4 | AssetName | []byte | Asset name | +| 5 | AssetURL | []byte | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | Arbitrary commitment | +| 7 | AssetManager | address | Manager address | +| 8 | AssetReserve | address | Reserve address | +| 9 | AssetFreeze | address | Freeze address | +| 10 | AssetClawback | address | Clawback address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. diff --git a/data/transactions/logic/TEAL_opcodes_v3.md b/data/transactions/logic/TEAL_opcodes_v3.md new file mode 100644 index 0000000000..c09a75df18 --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v3.md @@ -0,0 +1,789 @@ +# v3 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 +- Mode: Signature + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## balance + +- Bytecode: 0x60 +- Stack: ..., A: uint64 → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A: uint64, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A: uint64, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A: uint64, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A: uint64, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetTotal | uint64 | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | Frozen by default or not | +| 3 | AssetUnitName | []byte | Asset unit name | +| 4 | AssetName | []byte | Asset name | +| 5 | AssetURL | []byte | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | Arbitrary commitment | +| 7 | AssetManager | address | Manager address | +| 8 | AssetReserve | address | Reserve address | +| 9 | AssetFreeze | address | Freeze address | +| 10 | AssetClawback | address | Clawback address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A: uint64 → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes diff --git a/data/transactions/logic/TEAL_opcodes_v4.md b/data/transactions/logic/TEAL_opcodes_v4.md new file mode 100644 index 0000000000..96e51dee04 --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v4.md @@ -0,0 +1,1029 @@ +# v4 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 +- Mode: Signature + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetTotal | uint64 | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | Frozen by default or not | +| 3 | AssetUnitName | []byte | Asset unit name | +| 4 | AssetName | []byte | Asset name | +| 5 | AssetURL | []byte | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | Arbitrary commitment | +| 7 | AssetManager | address | Manager address | +| 8 | AssetReserve | address | Reserve address | +| 9 | AssetFreeze | address | Freeze address | +| 10 | AssetClawback | address | Clawback address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 diff --git a/data/transactions/logic/TEAL_opcodes_v5.md b/data/transactions/logic/TEAL_opcodes_v5.md new file mode 100644 index 0000000000..012d504ce1 --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v5.md @@ -0,0 +1,1263 @@ +# v5 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## ecdsa_verify + +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool +- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: Secp256k1=1700 +- Availability: v5 + +### ECDSA + +Curves + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | Secp256k1 | secp256k1 curve, used in Bitcoin | + + +The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. + +## ecdsa_pk_decompress + +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} +- Stack: ..., A: []byte → ..., X: []byte, Y: []byte +- decompress pubkey A into components X, Y +- **Cost**: Secp256k1=650 +- Availability: v5 + +The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. + +## ecdsa_pk_recover + +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} +- Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte +- for (data A, recovery id B, signature C, D) recover a public key +- **Cost**: 2000 +- Availability: v5 + +S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | +| 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | +| 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | +| 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | +| 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## loads + +- Bytecode: 0x3e +- Stack: ..., A: uint64 → ..., any +- Ath scratch space value. All scratch spaces are 0 at program start. +- Availability: v5 + +## stores + +- Bytecode: 0x3f +- Stack: ..., A: uint64, B → ... +- store B to the Ath scratch space +- Availability: v5 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## cover + +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} +- Stack: ..., [N items], A → ..., A, [N items] +- remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. +- Availability: v5 + +## uncover + +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} +- Stack: ..., A, [N items] → ..., [N items], A +- remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. +- Availability: v5 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## extract + +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails +- Availability: v5 + +## extract3 + +- Bytecode: 0x58 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. +- Availability: v5 + +## extract_uint16 + +- Bytecode: 0x59 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint32 + +- Bytecode: 0x5a +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint64 + +- Bytecode: 0x5b +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails +- Availability: v5 + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AssetTotal | uint64 | | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | +| 3 | AssetUnitName | []byte | | Asset unit name | +| 4 | AssetName | []byte | | Asset name | +| 5 | AssetURL | []byte | | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## app_params_get + +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from app A. Y is 1 if A exists, else 0 +- Availability: v5 +- Mode: Application + +### app_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AppApprovalProgram | []byte | Bytecode of Approval Program | +| 1 | AppClearStateProgram | []byte | Bytecode of Clear State Program | +| 2 | AppGlobalNumUint | uint64 | Number of uint64 values allowed in Global State | +| 3 | AppGlobalNumByteSlice | uint64 | Number of byte array values allowed in Global State | +| 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | +| 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | +| 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | + + +params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 + +## log + +- Bytecode: 0xb0 +- Stack: ..., A: []byte → ... +- write A to log state of the current application +- Availability: v5 +- Mode: Application + +`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes. + +## itxn_begin + +- Bytecode: 0xb1 +- Stack: ... → ... +- begin preparation of a new inner transaction in a new transaction group +- Availability: v5 +- Mode: Application + +`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. + +## itxn_field + +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} +- Stack: ..., A → ... +- set field F of the current inner transaction to A +- Availability: v5 +- Mode: Application + +`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.) + +## itxn_submit + +- Bytecode: 0xb3 +- Stack: ... → ... +- execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. +- Availability: v5 +- Mode: Application + +`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. + +## itxn + +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} +- Stack: ... → ..., any +- field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxna + +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## txnas + +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the current transaction +- Availability: v5 + +## gtxnas + +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the current group +- Availability: v5 + +## gtxnsas + +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth value of the array field F from the Ath transaction in the current group +- Availability: v5 + +## args + +- Bytecode: 0xc3 +- Stack: ..., A: uint64 → ..., []byte +- Ath LogicSig argument +- Availability: v5 +- Mode: Signature diff --git a/data/transactions/logic/TEAL_opcodes_v6.md b/data/transactions/logic/TEAL_opcodes_v6.md new file mode 100644 index 0000000000..d21931430c --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v6.md @@ -0,0 +1,1359 @@ +# v6 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## ecdsa_verify + +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool +- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: Secp256k1=1700 +- Availability: v5 + +### ECDSA + +Curves + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | Secp256k1 | secp256k1 curve, used in Bitcoin | + + +The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. + +## ecdsa_pk_decompress + +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} +- Stack: ..., A: []byte → ..., X: []byte, Y: []byte +- decompress pubkey A into components X, Y +- **Cost**: Secp256k1=650 +- Availability: v5 + +The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. + +## ecdsa_pk_recover + +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} +- Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte +- for (data A, recovery id B, signature C, D) recover a public key +- **Cost**: 2000 +- Availability: v5 + +S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | +| 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | +| 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | +| 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | +| 62 | LastLog | []byte | v6 | The last message emitted. Empty bytes if none were emitted. Application mode only | +| 63 | StateProofPK | []byte | v6 | 64 byte state proof public key | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | +| 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | +| 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## loads + +- Bytecode: 0x3e +- Stack: ..., A: uint64 → ..., any +- Ath scratch space value. All scratch spaces are 0 at program start. +- Availability: v5 + +## stores + +- Bytecode: 0x3f +- Stack: ..., A: uint64, B → ... +- store B to the Ath scratch space +- Availability: v5 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## cover + +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} +- Stack: ..., [N items], A → ..., A, [N items] +- remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. +- Availability: v5 + +## uncover + +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} +- Stack: ..., A, [N items] → ..., [N items], A +- remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. +- Availability: v5 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## extract + +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails +- Availability: v5 + +## extract3 + +- Bytecode: 0x58 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. +- Availability: v5 + +## extract_uint16 + +- Bytecode: 0x59 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint32 + +- Bytecode: 0x5a +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint64 + +- Bytecode: 0x5b +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails +- Availability: v5 + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AssetTotal | uint64 | | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | +| 3 | AssetUnitName | []byte | | Asset unit name | +| 4 | AssetName | []byte | | Asset name | +| 5 | AssetURL | []byte | | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## app_params_get + +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from app A. Y is 1 if A exists, else 0 +- Availability: v5 +- Mode: Application + +### app_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AppApprovalProgram | []byte | Bytecode of Approval Program | +| 1 | AppClearStateProgram | []byte | Bytecode of Clear State Program | +| 2 | AppGlobalNumUint | uint64 | Number of uint64 values allowed in Global State | +| 3 | AppGlobalNumByteSlice | uint64 | Number of byte array values allowed in Global State | +| 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | +| 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | +| 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | + + +params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. + +## acct_params_get + +- Syntax: `acct_params_get F` ∋ F: [acct_params](#field-group-acct_params) +- Bytecode: 0x73 {uint8} +- Stack: ..., A → ..., X: any, Y: bool +- X is field F from account A. Y is 1 if A owns positive algos, else 0 +- Availability: v6 +- Mode: Application + +### acct_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AcctBalance | uint64 | Account balance in microalgos | +| 1 | AcctMinBalance | uint64 | Minimum required balance for account, in microalgos | +| 2 | AcctAuthAddr | address | Address the account is rekeyed to. | + + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## bsqrt + +- Bytecode: 0x96 +- Stack: ..., A: []byte → ..., []byte +- The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers +- **Cost**: 40 +- Availability: v6 + +## divw + +- Bytecode: 0x97 +- Stack: ..., A: uint64, B: uint64, C: uint64 → ..., uint64 +- A,B / C. Fail if C == 0 or if result overflows. +- Availability: v6 + +The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low. + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 + +## log + +- Bytecode: 0xb0 +- Stack: ..., A: []byte → ... +- write A to log state of the current application +- Availability: v5 +- Mode: Application + +`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes. + +## itxn_begin + +- Bytecode: 0xb1 +- Stack: ... → ... +- begin preparation of a new inner transaction in a new transaction group +- Availability: v5 +- Mode: Application + +`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. + +## itxn_field + +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} +- Stack: ..., A → ... +- set field F of the current inner transaction to A +- Availability: v5 +- Mode: Application + +`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.) + +## itxn_submit + +- Bytecode: 0xb3 +- Stack: ... → ... +- execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. +- Availability: v5 +- Mode: Application + +`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. + +## itxn + +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} +- Stack: ... → ..., any +- field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxna + +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxn_next + +- Bytecode: 0xb6 +- Stack: ... → ... +- begin preparation of a new inner transaction in the same transaction group +- Availability: v6 +- Mode: Application + +`itxn_next` initializes the transaction exactly as `itxn_begin` does + +## gitxn + +- Syntax: `gitxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0xb7 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## gitxna + +- Syntax: `gitxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0xb8 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## txnas + +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the current transaction +- Availability: v5 + +## gtxnas + +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the current group +- Availability: v5 + +## gtxnsas + +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth value of the array field F from the Ath transaction in the current group +- Availability: v5 + +## args + +- Bytecode: 0xc3 +- Stack: ..., A: uint64 → ..., []byte +- Ath LogicSig argument +- Availability: v5 +- Mode: Signature + +## gloadss + +- Bytecode: 0xc4 +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth scratch space value of the Ath transaction in the current group +- Availability: v6 +- Mode: Application + +## itxnas + +- Syntax: `itxnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc5 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the last inner transaction +- Availability: v6 +- Mode: Application + +## gitxnas + +- Syntax: `gitxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc6 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application diff --git a/data/transactions/logic/TEAL_opcodes_v7.md b/data/transactions/logic/TEAL_opcodes_v7.md new file mode 100644 index 0000000000..41b1468ae5 --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v7.md @@ -0,0 +1,1481 @@ +# v7 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## ecdsa_verify + +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool +- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: Secp256k1=1700; Secp256r1=2500 +- Availability: v5 + +### ECDSA + +Curves + +| Index | Name | In | Notes | +| - | ------ | - | --------- | +| 0 | Secp256k1 | | secp256k1 curve, used in Bitcoin | +| 1 | Secp256r1 | v7 | secp256r1 curve, NIST standard | + + +The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. + +## ecdsa_pk_decompress + +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} +- Stack: ..., A: []byte → ..., X: []byte, Y: []byte +- decompress pubkey A into components X, Y +- **Cost**: Secp256k1=650; Secp256r1=2400 +- Availability: v5 + +The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. + +## ecdsa_pk_recover + +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} +- Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte +- for (data A, recovery id B, signature C, D) recover a public key +- **Cost**: 2000 +- Availability: v5 + +S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | +| 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | +| 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | +| 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | +| 62 | LastLog | []byte | v6 | The last message emitted. Empty bytes if none were emitted. Application mode only | +| 63 | StateProofPK | []byte | v6 | 64 byte state proof public key | +| 65 | NumApprovalProgramPages | uint64 | v7 | Number of Approval Program pages | +| 67 | NumClearStateProgramPages | uint64 | v7 | Number of ClearState Program pages | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | +| 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | +| 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | +| 64 | ApprovalProgramPages | []byte | v7 | Approval Program as an array of pages | +| 66 | ClearStateProgramPages | []byte | v7 | ClearState Program as an array of pages | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## loads + +- Bytecode: 0x3e +- Stack: ..., A: uint64 → ..., any +- Ath scratch space value. All scratch spaces are 0 at program start. +- Availability: v5 + +## stores + +- Bytecode: 0x3f +- Stack: ..., A: uint64, B → ... +- store B to the Ath scratch space +- Availability: v5 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## cover + +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} +- Stack: ..., [N items], A → ..., A, [N items] +- remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. +- Availability: v5 + +## uncover + +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} +- Stack: ..., A, [N items] → ..., [N items], A +- remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. +- Availability: v5 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## extract + +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails +- Availability: v5 + +## extract3 + +- Bytecode: 0x58 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. +- Availability: v5 + +## extract_uint16 + +- Bytecode: 0x59 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint32 + +- Bytecode: 0x5a +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint64 + +- Bytecode: 0x5b +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails +- Availability: v5 + +## replace2 + +- Syntax: `replace2 S` ∋ S: start position +- Bytecode: 0x5c {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)
`replace2` can be called using `replace` with 1 immediate. +- Availability: v7 + +## replace3 + +- Bytecode: 0x5d +- Stack: ..., A: []byte, B: uint64, C: []byte → ..., []byte +- Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)
`replace3` can be called using `replace` with no immediates. +- Availability: v7 + +## base64_decode + +- Syntax: `base64_decode E` ∋ E: [base64](#field-group-base64) +- Bytecode: 0x5e {uint8} +- Stack: ..., A: []byte → ..., []byte +- decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E +- **Cost**: 1 + 1 per 16 bytes of A +- Availability: v7 + +### base64 + +Encodings + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | URLEncoding | | +| 1 | StdEncoding | | + + +*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings. This opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings. + + Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`. + +## json_ref + +- Syntax: `json_ref R` ∋ R: [json_ref](#field-group-json_ref) +- Bytecode: 0x5f {uint8} +- Stack: ..., A: []byte, B: []byte → ..., any +- key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A +- **Cost**: 25 + 2 per 7 bytes of A +- Availability: v7 + +### json_ref + +Types + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | JSONString | []byte | | +| 1 | JSONUint64 | uint64 | | +| 2 | JSONObject | []byte | | + + +*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size. + +Almost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON. + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AssetTotal | uint64 | | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | +| 3 | AssetUnitName | []byte | | Asset unit name | +| 4 | AssetName | []byte | | Asset name | +| 5 | AssetURL | []byte | | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## app_params_get + +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from app A. Y is 1 if A exists, else 0 +- Availability: v5 +- Mode: Application + +### app_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AppApprovalProgram | []byte | Bytecode of Approval Program | +| 1 | AppClearStateProgram | []byte | Bytecode of Clear State Program | +| 2 | AppGlobalNumUint | uint64 | Number of uint64 values allowed in Global State | +| 3 | AppGlobalNumByteSlice | uint64 | Number of byte array values allowed in Global State | +| 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | +| 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | +| 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | + + +params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. + +## acct_params_get + +- Syntax: `acct_params_get F` ∋ F: [acct_params](#field-group-acct_params) +- Bytecode: 0x73 {uint8} +- Stack: ..., A → ..., X: any, Y: bool +- X is field F from account A. Y is 1 if A owns positive algos, else 0 +- Availability: v6 +- Mode: Application + +### acct_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AcctBalance | uint64 | Account balance in microalgos | +| 1 | AcctMinBalance | uint64 | Minimum required balance for account, in microalgos | +| 2 | AcctAuthAddr | address | Address the account is rekeyed to. | + + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## ed25519verify_bare + +- Bytecode: 0x84 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: 1900 +- Availability: v7 + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## bsqrt + +- Bytecode: 0x96 +- Stack: ..., A: []byte → ..., []byte +- The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers +- **Cost**: 40 +- Availability: v6 + +## divw + +- Bytecode: 0x97 +- Stack: ..., A: uint64, B: uint64, C: uint64 → ..., uint64 +- A,B / C. Fail if C == 0 or if result overflows. +- Availability: v6 + +The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low. + +## sha3_256 + +- Bytecode: 0x98 +- Stack: ..., A: []byte → ..., []byte +- SHA3_256 hash of value A, yields [32]byte +- **Cost**: 130 +- Availability: v7 + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 + +## log + +- Bytecode: 0xb0 +- Stack: ..., A: []byte → ... +- write A to log state of the current application +- Availability: v5 +- Mode: Application + +`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes. + +## itxn_begin + +- Bytecode: 0xb1 +- Stack: ... → ... +- begin preparation of a new inner transaction in a new transaction group +- Availability: v5 +- Mode: Application + +`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. + +## itxn_field + +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} +- Stack: ..., A → ... +- set field F of the current inner transaction to A +- Availability: v5 +- Mode: Application + +`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.) + +## itxn_submit + +- Bytecode: 0xb3 +- Stack: ... → ... +- execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. +- Availability: v5 +- Mode: Application + +`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. + +## itxn + +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} +- Stack: ... → ..., any +- field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxna + +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxn_next + +- Bytecode: 0xb6 +- Stack: ... → ... +- begin preparation of a new inner transaction in the same transaction group +- Availability: v6 +- Mode: Application + +`itxn_next` initializes the transaction exactly as `itxn_begin` does + +## gitxn + +- Syntax: `gitxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0xb7 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## gitxna + +- Syntax: `gitxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0xb8 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## txnas + +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the current transaction +- Availability: v5 + +## gtxnas + +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the current group +- Availability: v5 + +## gtxnsas + +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth value of the array field F from the Ath transaction in the current group +- Availability: v5 + +## args + +- Bytecode: 0xc3 +- Stack: ..., A: uint64 → ..., []byte +- Ath LogicSig argument +- Availability: v5 +- Mode: Signature + +## gloadss + +- Bytecode: 0xc4 +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth scratch space value of the Ath transaction in the current group +- Availability: v6 +- Mode: Application + +## itxnas + +- Syntax: `itxnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc5 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the last inner transaction +- Availability: v6 +- Mode: Application + +## gitxnas + +- Syntax: `gitxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc6 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## vrf_verify + +- Syntax: `vrf_verify S` ∋ S: [vrf_verify](#field-group-vrf_verify) +- Bytecode: 0xd0 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: bool +- Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. +- **Cost**: 5700 +- Availability: v7 + +### vrf_verify + +Standards + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | VrfAlgorand | | + + +`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/). + +## block + +- Syntax: `block F` ∋ F: [block](#field-group-block) +- Bytecode: 0xd1 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive) +- Availability: v7 + +### block + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | BlkSeed | []byte | | +| 1 | BlkTimestamp | uint64 | | + diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes_v8.md similarity index 99% rename from data/transactions/logic/TEAL_opcodes.md rename to data/transactions/logic/TEAL_opcodes_v8.md index 1d5bba1461..b2d1b851d6 100644 --- a/data/transactions/logic/TEAL_opcodes.md +++ b/data/transactions/logic/TEAL_opcodes_v8.md @@ -1,4 +1,4 @@ -# Opcodes +# v8 Opcodes Ops have a 'cost' of 1 unless otherwise specified. @@ -14,27 +14,21 @@ Ops have a 'cost' of 1 unless otherwise specified. - Bytecode: 0x01 - Stack: ..., A: []byte → ..., [32]byte - SHA256 hash of value A, yields [32]byte -- **Cost**: - - 7 (v1) - - 35 (since v2) +- **Cost**: 35 ## keccak256 - Bytecode: 0x02 - Stack: ..., A: []byte → ..., [32]byte - Keccak256 hash of value A, yields [32]byte -- **Cost**: - - 26 (v1) - - 130 (since v2) +- **Cost**: 130 ## sha512_256 - Bytecode: 0x03 - Stack: ..., A: []byte → ..., [32]byte - SHA512_256 hash of value A, yields [32]byte -- **Cost**: - - 9 (v1) - - 45 (since v2) +- **Cost**: 45 ## ed25519verify @@ -51,7 +45,7 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte - Bytecode: 0x05 {uint8} - Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool - for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} -- **Cost**: Secp256k1=1700 Secp256r1=2500 +- **Cost**: Secp256k1=1700; Secp256r1=2500 - Availability: v5 ### ECDSA @@ -72,7 +66,7 @@ The 32 byte Y-component of a public key is the last element on the stack, preced - Bytecode: 0x06 {uint8} - Stack: ..., A: []byte → ..., X: []byte, Y: []byte - decompress pubkey A into components X, Y -- **Cost**: Secp256k1=650 Secp256r1=2400 +- **Cost**: Secp256k1=650; Secp256r1=2400 - Availability: v5 The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. @@ -465,8 +459,6 @@ Fields | 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | | 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | | 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | -| 15 | AssetCreateMinBalance | uint64 | v10 | The additional minimum balance required to create (and opt-in to) an asset. | -| 16 | AssetOptInMinBalance | uint64 | v10 | The additional minimum balance required to opt-in to an asset. | ## gtxn @@ -886,7 +878,7 @@ Almost all smart contracts should use simpler and smaller methods (such as the [ - Availability: v2 - Mode: Application -params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value. +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. ## app_opted_in @@ -1097,7 +1089,7 @@ Fields - Availability: v3 - Mode: Application -params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value. +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. ## pushbytes diff --git a/data/transactions/logic/TEAL_opcodes_v9.md b/data/transactions/logic/TEAL_opcodes_v9.md new file mode 100644 index 0000000000..231343564d --- /dev/null +++ b/data/transactions/logic/TEAL_opcodes_v9.md @@ -0,0 +1,1640 @@ +# v9 Opcodes + +Ops have a 'cost' of 1 unless otherwise specified. + + +## err + +- Bytecode: 0x00 +- Stack: ... → _exits_ +- Fail immediately. + +## sha256 + +- Bytecode: 0x01 +- Stack: ..., A: []byte → ..., [32]byte +- SHA256 hash of value A, yields [32]byte +- **Cost**: 35 + +## keccak256 + +- Bytecode: 0x02 +- Stack: ..., A: []byte → ..., [32]byte +- Keccak256 hash of value A, yields [32]byte +- **Cost**: 130 + +## sha512_256 + +- Bytecode: 0x03 +- Stack: ..., A: []byte → ..., [32]byte +- SHA512_256 hash of value A, yields [32]byte +- **Cost**: 45 + +## ed25519verify + +- Bytecode: 0x04 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of ("ProgData" || program_hash || data) against the pubkey => {0 or 1} +- **Cost**: 1900 + +The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack. + +## ecdsa_verify + +- Syntax: `ecdsa_verify V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x05 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte, D: []byte, E: []byte → ..., bool +- for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: Secp256k1=1700; Secp256r1=2500 +- Availability: v5 + +### ECDSA + +Curves + +| Index | Name | In | Notes | +| - | ------ | - | --------- | +| 0 | Secp256k1 | | secp256k1 curve, used in Bitcoin | +| 1 | Secp256r1 | v7 | secp256r1 curve, NIST standard | + + +The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted. + +## ecdsa_pk_decompress + +- Syntax: `ecdsa_pk_decompress V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x06 {uint8} +- Stack: ..., A: []byte → ..., X: []byte, Y: []byte +- decompress pubkey A into components X, Y +- **Cost**: Secp256k1=650; Secp256r1=2400 +- Availability: v5 + +The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded. + +## ecdsa_pk_recover + +- Syntax: `ecdsa_pk_recover V` ∋ V: [ECDSA](#field-group-ecdsa) +- Bytecode: 0x07 {uint8} +- Stack: ..., A: []byte, B: uint64, C: []byte, D: []byte → ..., X: []byte, Y: []byte +- for (data A, recovery id B, signature C, D) recover a public key +- **Cost**: 2000 +- Availability: v5 + +S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long. + +## + + +- Bytecode: 0x08 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A plus B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`. + +## - + +- Bytecode: 0x09 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A minus B. Fail if B > A. + +## / + +- Bytecode: 0x0a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by B (truncated division). Fail if B == 0. + +`divmodw` is available to divide the two-element values produced by `mulw` and `addw`. + +## * + +- Bytecode: 0x0b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times B. Fail on overflow. + +Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`. + +## < + +- Bytecode: 0x0c +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than B => {0 or 1} + +## > + +- Bytecode: 0x0d +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than B => {0 or 1} + +## <= + +- Bytecode: 0x0e +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A less than or equal to B => {0 or 1} + +## >= + +- Bytecode: 0x0f +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A greater than or equal to B => {0 or 1} + +## && + +- Bytecode: 0x10 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero and B is not zero => {0 or 1} + +## || + +- Bytecode: 0x11 +- Stack: ..., A: uint64, B: uint64 → ..., bool +- A is not zero or B is not zero => {0 or 1} + +## == + +- Bytecode: 0x12 +- Stack: ..., A, B → ..., bool +- A is equal to B => {0 or 1} + +## != + +- Bytecode: 0x13 +- Stack: ..., A, B → ..., bool +- A is not equal to B => {0 or 1} + +## ! + +- Bytecode: 0x14 +- Stack: ..., A: uint64 → ..., uint64 +- A == 0 yields 1; else 0 + +## len + +- Bytecode: 0x15 +- Stack: ..., A: []byte → ..., uint64 +- yields length of byte value A + +## itob + +- Bytecode: 0x16 +- Stack: ..., A: uint64 → ..., []byte +- converts uint64 A to big-endian byte array, always of length 8 + +## btoi + +- Bytecode: 0x17 +- Stack: ..., A: []byte → ..., uint64 +- converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8. + +`btoi` fails if the input is longer than 8 bytes. + +## % + +- Bytecode: 0x18 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A modulo B. Fail if B == 0. + +## | + +- Bytecode: 0x19 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-or B + +## & + +- Bytecode: 0x1a +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-and B + +## ^ + +- Bytecode: 0x1b +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A bitwise-xor B + +## ~ + +- Bytecode: 0x1c +- Stack: ..., A: uint64 → ..., uint64 +- bitwise invert value A + +## mulw + +- Bytecode: 0x1d +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low + +## addw + +- Bytecode: 0x1e +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits. +- Availability: v2 + +## divmodw + +- Bytecode: 0x1f +- Stack: ..., A: uint64, B: uint64, C: uint64, D: uint64 → ..., W: uint64, X: uint64, Y: uint64, Z: uint64 +- W,X = (A,B / C,D); Y,Z = (A,B modulo C,D) +- **Cost**: 20 +- Availability: v4 + +The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low. + +## intcblock + +- Syntax: `intcblock UINT ...` ∋ UINT ...: a block of int constant values +- Bytecode: 0x20 {varuint count, [varuint ...]} +- Stack: ... → ... +- prepare block of uint64 constants for use by intc + +`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script. + +## intc + +- Syntax: `intc I` ∋ I: an index in the intcblock +- Bytecode: 0x21 {uint8} +- Stack: ... → ..., uint64 +- Ith constant from intcblock + +## intc_0 + +- Bytecode: 0x22 +- Stack: ... → ..., uint64 +- constant 0 from intcblock + +## intc_1 + +- Bytecode: 0x23 +- Stack: ... → ..., uint64 +- constant 1 from intcblock + +## intc_2 + +- Bytecode: 0x24 +- Stack: ... → ..., uint64 +- constant 2 from intcblock + +## intc_3 + +- Bytecode: 0x25 +- Stack: ... → ..., uint64 +- constant 3 from intcblock + +## bytecblock + +- Syntax: `bytecblock BYTES ...` ∋ BYTES ...: a block of byte constant values +- Bytecode: 0x26 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ... +- prepare block of byte-array constants for use by bytec + +`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script. + +## bytec + +- Syntax: `bytec I` ∋ I: an index in the bytecblock +- Bytecode: 0x27 {uint8} +- Stack: ... → ..., []byte +- Ith constant from bytecblock + +## bytec_0 + +- Bytecode: 0x28 +- Stack: ... → ..., []byte +- constant 0 from bytecblock + +## bytec_1 + +- Bytecode: 0x29 +- Stack: ... → ..., []byte +- constant 1 from bytecblock + +## bytec_2 + +- Bytecode: 0x2a +- Stack: ... → ..., []byte +- constant 2 from bytecblock + +## bytec_3 + +- Bytecode: 0x2b +- Stack: ... → ..., []byte +- constant 3 from bytecblock + +## arg + +- Syntax: `arg N` ∋ N: an arg index +- Bytecode: 0x2c {uint8} +- Stack: ... → ..., []byte +- Nth LogicSig argument +- Mode: Signature + +## arg_0 + +- Bytecode: 0x2d +- Stack: ... → ..., []byte +- LogicSig argument 0 +- Mode: Signature + +## arg_1 + +- Bytecode: 0x2e +- Stack: ... → ..., []byte +- LogicSig argument 1 +- Mode: Signature + +## arg_2 + +- Bytecode: 0x2f +- Stack: ... → ..., []byte +- LogicSig argument 2 +- Mode: Signature + +## arg_3 + +- Bytecode: 0x30 +- Stack: ... → ..., []byte +- LogicSig argument 3 +- Mode: Signature + +## txn + +- Syntax: `txn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x31 {uint8} +- Stack: ... → ..., any +- field F of current transaction + +### txn + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | Sender | address | | 32 byte address | +| 1 | Fee | uint64 | | microalgos | +| 2 | FirstValid | uint64 | | round number | +| 3 | FirstValidTime | uint64 | v7 | UNIX timestamp of block before txn.FirstValid. Fails if negative | +| 4 | LastValid | uint64 | | round number | +| 5 | Note | []byte | | Any data up to 1024 bytes | +| 6 | Lease | [32]byte | | 32 byte lease value | +| 7 | Receiver | address | | 32 byte address | +| 8 | Amount | uint64 | | microalgos | +| 9 | CloseRemainderTo | address | | 32 byte address | +| 10 | VotePK | [32]byte | | 32 byte address | +| 11 | SelectionPK | [32]byte | | 32 byte address | +| 12 | VoteFirst | uint64 | | The first round that the participation key is valid. | +| 13 | VoteLast | uint64 | | The last round that the participation key is valid. | +| 14 | VoteKeyDilution | uint64 | | Dilution for the 2-level participation key | +| 15 | Type | []byte | | Transaction type as bytes | +| 16 | TypeEnum | uint64 | | Transaction type as integer | +| 17 | XferAsset | uint64 | | Asset ID | +| 18 | AssetAmount | uint64 | | value in Asset's units | +| 19 | AssetSender | address | | 32 byte address. Source of assets if Sender is the Asset's Clawback address. | +| 20 | AssetReceiver | address | | 32 byte address | +| 21 | AssetCloseTo | address | | 32 byte address | +| 22 | GroupIndex | uint64 | | Position of this transaction within an atomic transaction group. A stand-alone transaction is implicitly element 0 in a group of 1 | +| 23 | TxID | [32]byte | | The computed ID for this transaction. 32 bytes. | +| 24 | ApplicationID | uint64 | v2 | ApplicationID from ApplicationCall transaction | +| 25 | OnCompletion | uint64 | v2 | ApplicationCall transaction on completion action | +| 27 | NumAppArgs | uint64 | v2 | Number of ApplicationArgs | +| 29 | NumAccounts | uint64 | v2 | Number of Accounts | +| 30 | ApprovalProgram | []byte | v2 | Approval program | +| 31 | ClearStateProgram | []byte | v2 | Clear state program | +| 32 | RekeyTo | address | v2 | 32 byte Sender's new AuthAddr | +| 33 | ConfigAsset | uint64 | v2 | Asset ID in asset config transaction | +| 34 | ConfigAssetTotal | uint64 | v2 | Total number of units of this asset created | +| 35 | ConfigAssetDecimals | uint64 | v2 | Number of digits to display after the decimal place when displaying the asset | +| 36 | ConfigAssetDefaultFrozen | bool | v2 | Whether the asset's slots are frozen by default or not, 0 or 1 | +| 37 | ConfigAssetUnitName | []byte | v2 | Unit name of the asset | +| 38 | ConfigAssetName | []byte | v2 | The asset name | +| 39 | ConfigAssetURL | []byte | v2 | URL | +| 40 | ConfigAssetMetadataHash | [32]byte | v2 | 32 byte commitment to unspecified asset metadata | +| 41 | ConfigAssetManager | address | v2 | 32 byte address | +| 42 | ConfigAssetReserve | address | v2 | 32 byte address | +| 43 | ConfigAssetFreeze | address | v2 | 32 byte address | +| 44 | ConfigAssetClawback | address | v2 | 32 byte address | +| 45 | FreezeAsset | uint64 | v2 | Asset ID being frozen or un-frozen | +| 46 | FreezeAssetAccount | address | v2 | 32 byte address of the account whose asset slot is being frozen or un-frozen | +| 47 | FreezeAssetFrozen | bool | v2 | The new frozen value, 0 or 1 | +| 49 | NumAssets | uint64 | v3 | Number of Assets | +| 51 | NumApplications | uint64 | v3 | Number of Applications | +| 52 | GlobalNumUint | uint64 | v3 | Number of global state integers in ApplicationCall | +| 53 | GlobalNumByteSlice | uint64 | v3 | Number of global state byteslices in ApplicationCall | +| 54 | LocalNumUint | uint64 | v3 | Number of local state integers in ApplicationCall | +| 55 | LocalNumByteSlice | uint64 | v3 | Number of local state byteslices in ApplicationCall | +| 56 | ExtraProgramPages | uint64 | v4 | Number of additional pages for each of the application's approval and clear state programs. An ExtraProgramPages of 1 means 2048 more total bytes, or 1024 for each program. | +| 57 | Nonparticipation | bool | v5 | Marks an account nonparticipating for rewards | +| 59 | NumLogs | uint64 | v5 | Number of Logs (only with `itxn` in v5). Application mode only | +| 60 | CreatedAssetID | uint64 | v5 | Asset ID allocated by the creation of an ASA (only with `itxn` in v5). Application mode only | +| 61 | CreatedApplicationID | uint64 | v5 | ApplicationID allocated by the creation of an application (only with `itxn` in v5). Application mode only | +| 62 | LastLog | []byte | v6 | The last message emitted. Empty bytes if none were emitted. Application mode only | +| 63 | StateProofPK | []byte | v6 | 64 byte state proof public key | +| 65 | NumApprovalProgramPages | uint64 | v7 | Number of Approval Program pages | +| 67 | NumClearStateProgramPages | uint64 | v7 | Number of ClearState Program pages | + + +## global + +- Syntax: `global F` ∋ F: [global](#field-group-global) +- Bytecode: 0x32 {uint8} +- Stack: ... → ..., any +- global field F + +### global + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | MinTxnFee | uint64 | | microalgos | +| 1 | MinBalance | uint64 | | microalgos | +| 2 | MaxTxnLife | uint64 | | rounds | +| 3 | ZeroAddress | address | | 32 byte address of all zero bytes | +| 4 | GroupSize | uint64 | | Number of transactions in this atomic transaction group. At least 1 | +| 5 | LogicSigVersion | uint64 | v2 | Maximum supported version | +| 6 | Round | uint64 | v2 | Current round number. Application mode only. | +| 7 | LatestTimestamp | uint64 | v2 | Last confirmed block UNIX timestamp. Fails if negative. Application mode only. | +| 8 | CurrentApplicationID | uint64 | v2 | ID of current application executing. Application mode only. | +| 9 | CreatorAddress | address | v3 | Address of the creator of the current application. Application mode only. | +| 10 | CurrentApplicationAddress | address | v5 | Address that the current application controls. Application mode only. | +| 11 | GroupID | [32]byte | v5 | ID of the transaction group. 32 zero bytes if the transaction is not part of a group. | +| 12 | OpcodeBudget | uint64 | v6 | The remaining cost that can be spent by opcodes in this program. | +| 13 | CallerApplicationID | uint64 | v6 | The application ID of the application that called this application. 0 if this application is at the top-level. Application mode only. | +| 14 | CallerApplicationAddress | address | v6 | The application address of the application that called this application. ZeroAddress if this application is at the top-level. Application mode only. | + + +## gtxn + +- Syntax: `gtxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0x33 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the current group + +for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`. + +## load + +- Syntax: `load I` ∋ I: position in scratch space to load from +- Bytecode: 0x34 {uint8} +- Stack: ... → ..., any +- Ith scratch space value. All scratch spaces are 0 at program start. + +## store + +- Syntax: `store I` ∋ I: position in scratch space to store to +- Bytecode: 0x35 {uint8} +- Stack: ..., A → ... +- store A to the Ith scratch space + +## txna + +- Syntax: `txna F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x36 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the current transaction
`txna` can be called using `txn` with 2 immediates. +- Availability: v2 + +### txna + +Fields (see [transaction reference](https://developer.algorand.org/docs/reference/transactions/)) + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 26 | ApplicationArgs | []byte | v2 | Arguments passed to the application in the ApplicationCall transaction | +| 28 | Accounts | address | v2 | Accounts listed in the ApplicationCall transaction | +| 48 | Assets | uint64 | v3 | Foreign Assets listed in the ApplicationCall transaction | +| 50 | Applications | uint64 | v3 | Foreign Apps listed in the ApplicationCall transaction | +| 58 | Logs | []byte | v5 | Log messages emitted by an application call (only with `itxn` in v5). Application mode only | +| 64 | ApprovalProgramPages | []byte | v7 | Approval Program as an array of pages | +| 66 | ClearStateProgramPages | []byte | v7 | ClearState Program as an array of pages | + + +## gtxna + +- Syntax: `gtxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x37 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the current group
`gtxna` can be called using `gtxn` with 3 immediates. +- Availability: v2 + +## gtxns + +- Syntax: `gtxns F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0x38 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of the Ath transaction in the current group +- Availability: v3 + +for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction. + +## gtxnsa + +- Syntax: `gtxnsa F I` ∋ F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0x39 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith value of the array field F from the Ath transaction in the current group
`gtxnsa` can be called using `gtxns` with 2 immediates. +- Availability: v3 + +## gload + +- Syntax: `gload T I` ∋ T: transaction group index, I: position in scratch space to load from +- Bytecode: 0x3a {uint8}, {uint8} +- Stack: ... → ..., any +- Ith scratch space value of the Tth transaction in the current group +- Availability: v4 +- Mode: Application + +`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex. + +## gloads + +- Syntax: `gloads I` ∋ I: position in scratch space to load from +- Bytecode: 0x3b {uint8} +- Stack: ..., A: uint64 → ..., any +- Ith scratch space value of the Ath transaction in the current group +- Availability: v4 +- Mode: Application + +`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex. + +## gaid + +- Syntax: `gaid T` ∋ T: transaction group index +- Bytecode: 0x3c {uint8} +- Stack: ... → ..., uint64 +- ID of the asset or application created in the Tth transaction of the current group +- Availability: v4 +- Mode: Application + +`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex. + +## gaids + +- Bytecode: 0x3d +- Stack: ..., A: uint64 → ..., uint64 +- ID of the asset or application created in the Ath transaction of the current group +- Availability: v4 +- Mode: Application + +`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex. + +## loads + +- Bytecode: 0x3e +- Stack: ..., A: uint64 → ..., any +- Ath scratch space value. All scratch spaces are 0 at program start. +- Availability: v5 + +## stores + +- Bytecode: 0x3f +- Stack: ..., A: uint64, B → ... +- store B to the Ath scratch space +- Availability: v5 + +## bnz + +- Syntax: `bnz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x40 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is not zero + +The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff. + +At v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.) + +## bz + +- Syntax: `bz TARGET` ∋ TARGET: branch offset +- Bytecode: 0x41 {int16 (big-endian)} +- Stack: ..., A: uint64 → ... +- branch to TARGET if value A is zero +- Availability: v2 + +See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`. + +## b + +- Syntax: `b TARGET` ∋ TARGET: branch offset +- Bytecode: 0x42 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET +- Availability: v2 + +See `bnz` for details on how branches work. `b` always jumps to the offset. + +## return + +- Bytecode: 0x43 +- Stack: ..., A: uint64 → _exits_ +- use A as success value; end +- Availability: v2 + +## assert + +- Bytecode: 0x44 +- Stack: ..., A: uint64 → ... +- immediately fail unless A is a non-zero number +- Availability: v3 + +## bury + +- Syntax: `bury N` ∋ N: depth +- Bytecode: 0x45 {uint8} +- Stack: ..., A → ... +- replace the Nth value from the top of the stack with A. bury 0 fails. +- Availability: v8 + +## popn + +- Syntax: `popn N` ∋ N: stack depth +- Bytecode: 0x46 {uint8} +- Stack: ..., [N items] → ... +- remove N values from the top of the stack +- Availability: v8 + +## dupn + +- Syntax: `dupn N` ∋ N: copy count +- Bytecode: 0x47 {uint8} +- Stack: ..., A → ..., A, [N copies of A] +- duplicate A, N times +- Availability: v8 + +## pop + +- Bytecode: 0x48 +- Stack: ..., A → ... +- discard A + +## dup + +- Bytecode: 0x49 +- Stack: ..., A → ..., A, A +- duplicate A + +## dup2 + +- Bytecode: 0x4a +- Stack: ..., A, B → ..., A, B, A, B +- duplicate A and B +- Availability: v2 + +## dig + +- Syntax: `dig N` ∋ N: depth +- Bytecode: 0x4b {uint8} +- Stack: ..., A, [N items] → ..., A, [N items], A +- Nth value from the top of the stack. dig 0 is equivalent to dup +- Availability: v3 + +## swap + +- Bytecode: 0x4c +- Stack: ..., A, B → ..., B, A +- swaps A and B on stack +- Availability: v3 + +## select + +- Bytecode: 0x4d +- Stack: ..., A, B, C: uint64 → ..., A or B +- selects one of two values based on top-of-stack: B if C != 0, else A +- Availability: v3 + +## cover + +- Syntax: `cover N` ∋ N: depth +- Bytecode: 0x4e {uint8} +- Stack: ..., [N items], A → ..., A, [N items] +- remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N. +- Availability: v5 + +## uncover + +- Syntax: `uncover N` ∋ N: depth +- Bytecode: 0x4f {uint8} +- Stack: ..., A, [N items] → ..., [N items], A +- remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N. +- Availability: v5 + +## concat + +- Bytecode: 0x50 +- Stack: ..., A: []byte, B: []byte → ..., []byte +- join A and B +- Availability: v2 + +`concat` fails if the result would be greater than 4096 bytes. + +## substring + +- Syntax: `substring S E` ∋ S: start position, E: end position +- Bytecode: 0x51 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails +- Availability: v2 + +## substring3 + +- Bytecode: 0x52 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails +- Availability: v2 + +## getbit + +- Bytecode: 0x53 +- Stack: ..., A, B: uint64 → ..., uint64 +- Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +see explanation of bit ordering in setbit + +## setbit + +- Bytecode: 0x54 +- Stack: ..., A, B: uint64, C: uint64 → ..., any +- Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails +- Availability: v3 + +When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10. + +## getbyte + +- Bytecode: 0x55 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## setbyte + +- Bytecode: 0x56 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails +- Availability: v3 + +## extract + +- Syntax: `extract S L` ∋ S: start position, L: length +- Bytecode: 0x57 {uint8}, {uint8} +- Stack: ..., A: []byte → ..., []byte +- A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails +- Availability: v5 + +## extract3 + +- Bytecode: 0x58 +- Stack: ..., A: []byte, B: uint64, C: uint64 → ..., []byte +- A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails
`extract3` can be called using `extract` with no immediates. +- Availability: v5 + +## extract_uint16 + +- Bytecode: 0x59 +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint32 + +- Bytecode: 0x5a +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails +- Availability: v5 + +## extract_uint64 + +- Bytecode: 0x5b +- Stack: ..., A: []byte, B: uint64 → ..., uint64 +- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails +- Availability: v5 + +## replace2 + +- Syntax: `replace2 S` ∋ S: start position +- Bytecode: 0x5c {uint8} +- Stack: ..., A: []byte, B: []byte → ..., []byte +- Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)
`replace2` can be called using `replace` with 1 immediate. +- Availability: v7 + +## replace3 + +- Bytecode: 0x5d +- Stack: ..., A: []byte, B: uint64, C: []byte → ..., []byte +- Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)
`replace3` can be called using `replace` with no immediates. +- Availability: v7 + +## base64_decode + +- Syntax: `base64_decode E` ∋ E: [base64](#field-group-base64) +- Bytecode: 0x5e {uint8} +- Stack: ..., A: []byte → ..., []byte +- decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E +- **Cost**: 1 + 1 per 16 bytes of A +- Availability: v7 + +### base64 + +Encodings + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | URLEncoding | | +| 1 | StdEncoding | | + + +*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings. This opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings. + + Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`. + +## json_ref + +- Syntax: `json_ref R` ∋ R: [json_ref](#field-group-json_ref) +- Bytecode: 0x5f {uint8} +- Stack: ..., A: []byte, B: []byte → ..., any +- key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A +- **Cost**: 25 + 2 per 7 bytes of A +- Availability: v7 + +### json_ref + +Types + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | JSONString | []byte | | +| 1 | JSONUint64 | uint64 | | +| 2 | JSONObject | []byte | | + + +*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size. + +Almost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON. + +## balance + +- Bytecode: 0x60 +- Stack: ..., A → ..., uint64 +- balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit` +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## app_opted_in + +- Bytecode: 0x61 +- Stack: ..., A, B: uint64 → ..., bool +- 1 if account A is opted in to application B, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise. + +## app_local_get + +- Bytecode: 0x62 +- Stack: ..., A, B: []byte → ..., any +- local state of the key B in the current application in account A +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_local_get_ex + +- Bytecode: 0x63 +- Stack: ..., A, B: uint64, C: []byte → ..., X: any, Y: bool +- X is the local state of application B, key C in account A. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get + +- Bytecode: 0x64 +- Stack: ..., A: []byte → ..., any +- global state of the key A in the current application +- Availability: v2 +- Mode: Application + +params: state key. Return: value. The value is zero (of type uint64) if the key does not exist. + +## app_global_get_ex + +- Bytecode: 0x65 +- Stack: ..., A: uint64, B: []byte → ..., X: any, Y: bool +- X is the global state of application A, key B. Y is 1 if key existed, else 0 +- Availability: v2 +- Mode: Application + +params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist. + +## app_local_put + +- Bytecode: 0x66 +- Stack: ..., A, B: []byte, C → ... +- write C to key B in account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value. + +## app_global_put + +- Bytecode: 0x67 +- Stack: ..., A: []byte, B → ... +- write B to key A in the global state of the current application +- Availability: v2 +- Mode: Application + +## app_local_del + +- Bytecode: 0x68 +- Stack: ..., A, B: []byte → ... +- delete key B from account A's local state of the current application +- Availability: v2 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. + +Deleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.) + +## app_global_del + +- Bytecode: 0x69 +- Stack: ..., A: []byte → ... +- delete key A from the global state of the current application +- Availability: v2 +- Mode: Application + +params: state key. + +Deleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.) + +## asset_holding_get + +- Syntax: `asset_holding_get F` ∋ F: [asset_holding](#field-group-asset_holding) +- Bytecode: 0x70 {uint8} +- Stack: ..., A, B: uint64 → ..., X: any, Y: bool +- X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0 +- Availability: v2 +- Mode: Application + +### asset_holding + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AssetBalance | uint64 | Amount of the asset unit held by this account | +| 1 | AssetFrozen | bool | Is the asset frozen or not | + + +params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## asset_params_get + +- Syntax: `asset_params_get F` ∋ F: [asset_params](#field-group-asset_params) +- Bytecode: 0x71 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from asset A. Y is 1 if A exists, else 0 +- Availability: v2 +- Mode: Application + +### asset_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AssetTotal | uint64 | | Total number of units of this asset | +| 1 | AssetDecimals | uint64 | | See AssetParams.Decimals | +| 2 | AssetDefaultFrozen | bool | | Frozen by default or not | +| 3 | AssetUnitName | []byte | | Asset unit name | +| 4 | AssetName | []byte | | Asset name | +| 5 | AssetURL | []byte | | URL with additional info about the asset | +| 6 | AssetMetadataHash | [32]byte | | Arbitrary commitment | +| 7 | AssetManager | address | | Manager address | +| 8 | AssetReserve | address | | Reserve address | +| 9 | AssetFreeze | address | | Freeze address | +| 10 | AssetClawback | address | | Clawback address | +| 11 | AssetCreator | address | v5 | Creator address | + + +params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value. + +## app_params_get + +- Syntax: `app_params_get F` ∋ F: [app_params](#field-group-app_params) +- Bytecode: 0x72 {uint8} +- Stack: ..., A: uint64 → ..., X: any, Y: bool +- X is field F from app A. Y is 1 if A exists, else 0 +- Availability: v5 +- Mode: Application + +### app_params + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | AppApprovalProgram | []byte | Bytecode of Approval Program | +| 1 | AppClearStateProgram | []byte | Bytecode of Clear State Program | +| 2 | AppGlobalNumUint | uint64 | Number of uint64 values allowed in Global State | +| 3 | AppGlobalNumByteSlice | uint64 | Number of byte array values allowed in Global State | +| 4 | AppLocalNumUint | uint64 | Number of uint64 values allowed in Local State | +| 5 | AppLocalNumByteSlice | uint64 | Number of byte array values allowed in Local State | +| 6 | AppExtraProgramPages | uint64 | Number of Extra Program Pages of code space | +| 7 | AppCreator | address | Creator address | +| 8 | AppAddress | address | Address for which this application has authority | + + +params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value. + +## acct_params_get + +- Syntax: `acct_params_get F` ∋ F: [acct_params](#field-group-acct_params) +- Bytecode: 0x73 {uint8} +- Stack: ..., A → ..., X: any, Y: bool +- X is field F from account A. Y is 1 if A owns positive algos, else 0 +- Availability: v6 +- Mode: Application + +### acct_params + +Fields + +| Index | Name | Type | In | Notes | +| - | ------ | -- | - | --------- | +| 0 | AcctBalance | uint64 | | Account balance in microalgos | +| 1 | AcctMinBalance | uint64 | | Minimum required balance for account, in microalgos | +| 2 | AcctAuthAddr | address | | Address the account is rekeyed to. | +| 3 | AcctTotalNumUint | uint64 | v8 | The total number of uint64 values allocated by this account in Global and Local States. | +| 4 | AcctTotalNumByteSlice | uint64 | v8 | The total number of byte array values allocated by this account in Global and Local States. | +| 5 | AcctTotalExtraAppPages | uint64 | v8 | The number of extra app code pages used by this account. | +| 6 | AcctTotalAppsCreated | uint64 | v8 | The number of existing apps created by this account. | +| 7 | AcctTotalAppsOptedIn | uint64 | v8 | The number of apps this account is opted into. | +| 8 | AcctTotalAssetsCreated | uint64 | v8 | The number of existing ASAs created by this account. | +| 9 | AcctTotalAssets | uint64 | v8 | The numbers of ASAs held by this account (including ASAs this account created). | +| 10 | AcctTotalBoxes | uint64 | v8 | The number of existing boxes created by this account's app. | +| 11 | AcctTotalBoxBytes | uint64 | v8 | The total number of bytes used by this account's app's box keys and values. | + + +## min_balance + +- Bytecode: 0x78 +- Stack: ..., A → ..., uint64 +- minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change. +- Availability: v3 +- Mode: Application + +params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value. + +## pushbytes + +- Syntax: `pushbytes BYTES` ∋ BYTES: a byte constant +- Bytecode: 0x80 {varuint length, bytes} +- Stack: ... → ..., []byte +- immediate BYTES +- Availability: v3 + +pushbytes args are not added to the bytecblock during assembly processes + +## pushint + +- Syntax: `pushint UINT` ∋ UINT: an int constant +- Bytecode: 0x81 {varuint} +- Stack: ... → ..., uint64 +- immediate UINT +- Availability: v3 + +pushint args are not added to the intcblock during assembly processes + +## pushbytess + +- Syntax: `pushbytess BYTES ...` ∋ BYTES ...: a list of byte constants +- Bytecode: 0x82 {varuint count, [varuint length, bytes ...]} +- Stack: ... → ..., [N items] +- push sequences of immediate byte arrays to stack (first byte array being deepest) +- Availability: v8 + +pushbytess args are not added to the bytecblock during assembly processes + +## pushints + +- Syntax: `pushints UINT ...` ∋ UINT ...: a list of int constants +- Bytecode: 0x83 {varuint count, [varuint ...]} +- Stack: ... → ..., [N items] +- push sequence of immediate uints to stack in the order they appear (first uint being deepest) +- Availability: v8 + +pushints args are not added to the intcblock during assembly processes + +## ed25519verify_bare + +- Bytecode: 0x84 +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., bool +- for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1} +- **Cost**: 1900 +- Availability: v7 + +## callsub + +- Syntax: `callsub TARGET` ∋ TARGET: branch offset +- Bytecode: 0x88 {int16 (big-endian)} +- Stack: ... → ... +- branch unconditionally to TARGET, saving the next instruction on the call stack +- Availability: v4 + +The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it. + +## retsub + +- Bytecode: 0x89 +- Stack: ... → ... +- pop the top instruction from the call stack and branch to it +- Availability: v4 + +If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values. + +## proto + +- Syntax: `proto A R` ∋ A: number of arguments, R: number of return values +- Bytecode: 0x8a {uint8}, {uint8} +- Stack: ... → ... +- Prepare top call frame for a retsub that will assume A args and R return values. +- Availability: v8 + +Fails unless the last instruction executed was a `callsub`. + +## frame_dig + +- Syntax: `frame_dig I` ∋ I: frame slot +- Bytecode: 0x8b {int8} +- Stack: ... → ..., any +- Nth (signed) value from the frame pointer. +- Availability: v8 + +## frame_bury + +- Syntax: `frame_bury I` ∋ I: frame slot +- Bytecode: 0x8c {int8} +- Stack: ..., A → ... +- replace the Nth (signed) value from the frame pointer in the stack with A +- Availability: v8 + +## switch + +- Syntax: `switch TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8d {varuint count, [int16 (big-endian) ...]} +- Stack: ..., A: uint64 → ... +- branch to the Ath label. Continue at following instruction if index A exceeds the number of labels. +- Availability: v8 + +## match + +- Syntax: `match TARGET ...` ∋ TARGET ...: list of labels +- Bytecode: 0x8e {varuint count, [int16 (big-endian) ...]} +- Stack: ..., [A1, A2, ..., AN], B → ... +- given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found. +- Availability: v8 + +`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction. + +## shl + +- Bytecode: 0x90 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A times 2^B, modulo 2^64 +- Availability: v4 + +## shr + +- Bytecode: 0x91 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A divided by 2^B +- Availability: v4 + +## sqrt + +- Bytecode: 0x92 +- Stack: ..., A: uint64 → ..., uint64 +- The largest integer I such that I^2 <= A +- **Cost**: 4 +- Availability: v4 + +## bitlen + +- Bytecode: 0x93 +- Stack: ..., A → ..., uint64 +- The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4 +- Availability: v4 + +bitlen interprets arrays as big-endian integers, unlike setbit/getbit + +## exp + +- Bytecode: 0x94 +- Stack: ..., A: uint64, B: uint64 → ..., uint64 +- A raised to the Bth power. Fail if A == B == 0 and on overflow +- Availability: v4 + +## expw + +- Bytecode: 0x95 +- Stack: ..., A: uint64, B: uint64 → ..., X: uint64, Y: uint64 +- A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1 +- **Cost**: 10 +- Availability: v4 + +## bsqrt + +- Bytecode: 0x96 +- Stack: ..., A: []byte → ..., []byte +- The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers +- **Cost**: 40 +- Availability: v6 + +## divw + +- Bytecode: 0x97 +- Stack: ..., A: uint64, B: uint64, C: uint64 → ..., uint64 +- A,B / C. Fail if C == 0 or if result overflows. +- Availability: v6 + +The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low. + +## sha3_256 + +- Bytecode: 0x98 +- Stack: ..., A: []byte → ..., []byte +- SHA3_256 hash of value A, yields [32]byte +- **Cost**: 130 +- Availability: v7 + +## b+ + +- Bytecode: 0xa0 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A plus B. A and B are interpreted as big-endian unsigned integers +- **Cost**: 10 +- Availability: v4 + +## b- + +- Bytecode: 0xa1 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow. +- **Cost**: 10 +- Availability: v4 + +## b/ + +- Bytecode: 0xa2 +- Stack: ..., A: bigint, B: bigint → ..., bigint +- A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b* + +- Bytecode: 0xa3 +- Stack: ..., A: bigint, B: bigint → ..., []byte +- A times B. A and B are interpreted as big-endian unsigned integers. +- **Cost**: 20 +- Availability: v4 + +## b< + +- Bytecode: 0xa4 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b> + +- Bytecode: 0xa5 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b<= + +- Bytecode: 0xa6 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b>= + +- Bytecode: 0xa7 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b== + +- Bytecode: 0xa8 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b!= + +- Bytecode: 0xa9 +- Stack: ..., A: bigint, B: bigint → ..., bool +- 0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers +- Availability: v4 + +## b% + +- Bytecode: 0xaa +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero. +- **Cost**: 20 +- Availability: v4 + +## b| + +- Bytecode: 0xab +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-or B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b& + +- Bytecode: 0xac +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-and B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b^ + +- Bytecode: 0xad +- Stack: ..., A: []byte, B: []byte → ..., []byte +- A bitwise-xor B. A and B are zero-left extended to the greater of their lengths +- **Cost**: 6 +- Availability: v4 + +## b~ + +- Bytecode: 0xae +- Stack: ..., A: []byte → ..., []byte +- A with all bits inverted +- **Cost**: 4 +- Availability: v4 + +## bzero + +- Bytecode: 0xaf +- Stack: ..., A: uint64 → ..., []byte +- zero filled byte-array of length A +- Availability: v4 + +## log + +- Bytecode: 0xb0 +- Stack: ..., A: []byte → ... +- write A to log state of the current application +- Availability: v5 +- Mode: Application + +`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes. + +## itxn_begin + +- Bytecode: 0xb1 +- Stack: ... → ... +- begin preparation of a new inner transaction in a new transaction group +- Availability: v5 +- Mode: Application + +`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values. + +## itxn_field + +- Syntax: `itxn_field F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb2 {uint8} +- Stack: ..., A → ... +- set field F of the current inner transaction to A +- Availability: v5 +- Mode: Application + +`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.) + +## itxn_submit + +- Bytecode: 0xb3 +- Stack: ... → ... +- execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails. +- Availability: v5 +- Mode: Application + +`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction. + +## itxn + +- Syntax: `itxn F` ∋ F: [txn](#field-group-txn) +- Bytecode: 0xb4 {uint8} +- Stack: ... → ..., any +- field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxna + +- Syntax: `itxna F I` ∋ F: [txna](#field-group-txna), I: a transaction field array index +- Bytecode: 0xb5 {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F of the last inner transaction +- Availability: v5 +- Mode: Application + +## itxn_next + +- Bytecode: 0xb6 +- Stack: ... → ... +- begin preparation of a new inner transaction in the same transaction group +- Availability: v6 +- Mode: Application + +`itxn_next` initializes the transaction exactly as `itxn_begin` does + +## gitxn + +- Syntax: `gitxn T F` ∋ T: transaction group index, F: [txn](#field-group-txn) +- Bytecode: 0xb7 {uint8}, {uint8} +- Stack: ... → ..., any +- field F of the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## gitxna + +- Syntax: `gitxna T F I` ∋ T: transaction group index, F: [txna](#field-group-txna), I: transaction field array index +- Bytecode: 0xb8 {uint8}, {uint8}, {uint8} +- Stack: ... → ..., any +- Ith value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## box_create + +- Bytecode: 0xb9 +- Stack: ..., A: boxName, B: uint64 → ..., bool +- create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1 +- Availability: v8 +- Mode: Application + +Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`. + +## box_extract + +- Bytecode: 0xba +- Stack: ..., A: boxName, B: uint64, C: uint64 → ..., []byte +- read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. +- Availability: v8 +- Mode: Application + +## box_replace + +- Bytecode: 0xbb +- Stack: ..., A: boxName, B: uint64, C: []byte → ... +- write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size. +- Availability: v8 +- Mode: Application + +## box_del + +- Bytecode: 0xbc +- Stack: ..., A: boxName → ..., bool +- delete box named A if it exists. Return 1 if A existed, 0 otherwise +- Availability: v8 +- Mode: Application + +## box_len + +- Bytecode: 0xbd +- Stack: ..., A: boxName → ..., X: uint64, Y: bool +- X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0. +- Availability: v8 +- Mode: Application + +## box_get + +- Bytecode: 0xbe +- Stack: ..., A: boxName → ..., X: []byte, Y: bool +- X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0. +- Availability: v8 +- Mode: Application + +For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace` + +## box_put + +- Bytecode: 0xbf +- Stack: ..., A: boxName, B: []byte → ... +- replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist +- Availability: v8 +- Mode: Application + +For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace` + +## txnas + +- Syntax: `txnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc0 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the current transaction +- Availability: v5 + +## gtxnas + +- Syntax: `gtxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc1 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the current group +- Availability: v5 + +## gtxnsas + +- Syntax: `gtxnsas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc2 {uint8} +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth value of the array field F from the Ath transaction in the current group +- Availability: v5 + +## args + +- Bytecode: 0xc3 +- Stack: ..., A: uint64 → ..., []byte +- Ath LogicSig argument +- Availability: v5 +- Mode: Signature + +## gloadss + +- Bytecode: 0xc4 +- Stack: ..., A: uint64, B: uint64 → ..., any +- Bth scratch space value of the Ath transaction in the current group +- Availability: v6 +- Mode: Application + +## itxnas + +- Syntax: `itxnas F` ∋ F: [txna](#field-group-txna) +- Bytecode: 0xc5 {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F of the last inner transaction +- Availability: v6 +- Mode: Application + +## gitxnas + +- Syntax: `gitxnas T F` ∋ T: transaction group index, F: [txna](#field-group-txna) +- Bytecode: 0xc6 {uint8}, {uint8} +- Stack: ..., A: uint64 → ..., any +- Ath value of the array field F from the Tth transaction in the last inner group submitted +- Availability: v6 +- Mode: Application + +## vrf_verify + +- Syntax: `vrf_verify S` ∋ S: [vrf_verify](#field-group-vrf_verify) +- Bytecode: 0xd0 {uint8} +- Stack: ..., A: []byte, B: []byte, C: []byte → ..., X: []byte, Y: bool +- Verify the proof B of message A against pubkey C. Returns vrf output and verification flag. +- **Cost**: 5700 +- Availability: v7 + +### vrf_verify + +Standards + +| Index | Name | Notes | +| - | ------ | --------- | +| 0 | VrfAlgorand | | + + +`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/). + +## block + +- Syntax: `block F` ∋ F: [block](#field-group-block) +- Bytecode: 0xd1 {uint8} +- Stack: ..., A: uint64 → ..., any +- field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive) +- Availability: v7 + +### block + +Fields + +| Index | Name | Type | Notes | +| - | ------ | -- | --------- | +| 0 | BlkSeed | []byte | | +| 1 | BlkTimestamp | uint64 | | + diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go index 4939d9ded2..aa32c51eba 100644 --- a/data/transactions/logic/assembler.go +++ b/data/transactions/logic/assembler.go @@ -107,7 +107,7 @@ func (ref intReference) length(ops *OpStream, assembled []byte) (int, error) { case opIntc: return 2, nil default: - return 0, errorLinef(ops.OffsetToLine[ref.position], "unexpected op at intReference: %d", assembled[ref.position]) + return 0, sourceErrorf(ops.OffsetToSource[ref.position], "unexpected op at intReference: %d", assembled[ref.position]) } } @@ -176,7 +176,7 @@ func (ref byteReference) length(ops *OpStream, assembled []byte) (int, error) { case opBytec: return 2, nil default: - return 0, errorLinef(ops.OffsetToLine[ref.position], "unexpected op at byteReference: %d", assembled[ref.position]) + return 0, sourceErrorf(ops.OffsetToSource[ref.position], "unexpected op at byteReference: %d", assembled[ref.position]) } } @@ -214,7 +214,15 @@ func (ref byteReference) makeNewReference(ops *OpStream, singleton bool, newInde } } -// OpStream is destination for program and scratch space +// SourceLocation points to a specific location in a source file. +type SourceLocation struct { + // Line is the line number, starting at 0. + Line int + // Column is the column number, starting at 0. + Column int +} + +// OpStream accumulates state, including the final program, during assembly. type OpStream struct { Version uint64 Trace *strings.Builder @@ -249,8 +257,8 @@ type OpStream struct { // track references in order to patch in jump offsets labelReferences []labelReference - // map opcode offsets to source line - OffsetToLine map[int]int + // map opcode offsets to source location + OffsetToSource map[int]SourceLocation HasStatefulOps bool @@ -264,12 +272,12 @@ type OpStream struct { // OpStream must be used for each call to assemble(). func newOpStream(version uint64) OpStream { o := OpStream{ - labels: make(map[string]int), - OffsetToLine: make(map[int]int), - typeTracking: true, - Version: version, - macros: make(map[string][]token), - known: ProgramKnowledge{fp: -1}, + labels: make(map[string]int), + OffsetToSource: make(map[int]SourceLocation), + typeTracking: true, + Version: version, + macros: make(map[string][]token), + known: ProgramKnowledge{fp: -1}, } for i := range o.known.scratchSpace { @@ -292,7 +300,7 @@ type ProgramKnowledge struct { // fails. But when a label or callsub is encountered, `stack` is truncated // and `bottom` becomes StackAny, because we don't track program state // coming in from elsewhere. A `+` after a label succeeds, because the stack - // "vitually" contains an infinite list of StackAny. + // "virtually" contains an infinite list of StackAny. bottom StackType // deadcode indicates that the program is in deadcode, so no type checking @@ -365,9 +373,9 @@ func (ops *OpStream) createLabel(withColon token) { ops.known.label() } -// recordSourceLine adds an entry to pc to line mapping -func (ops *OpStream) recordSourceLine() { - ops.OffsetToLine[ops.pending.Len()] = ops.sourceLine - 1 +// recordSourceLocation adds an entry to pc to source location mapping +func (ops *OpStream) recordSourceLocation(line, column int) { + ops.OffsetToSource[ops.pending.Len()] = SourceLocation{line - 1, column} } // referToLabel records an opcode label reference to resolve later @@ -392,8 +400,8 @@ func (ops *OpStream) returns(spec *OpSpec, replacement StackType) { return } } - // returns was called on an OpSpec with no StackAny in its Returns - panic(fmt.Sprintf("%+v", spec)) + panic(fmt.Sprintf("returns was called on OpSpec '%s' without StackAny %+v in spec.Return", + spec.Name, spec.Return)) } // writeIntc writes opcodes for loading a uint64 constant onto the stack. @@ -990,6 +998,7 @@ func asmBranch(ops *OpStream, spec *OpSpec, mnemonic token, args []token) *sourc return nil } +// asmSwitch assembles switch and match opcodes func asmSwitch(ops *OpStream, spec *OpSpec, mnemonic token, args []token) *sourceError { numOffsets := len(args) if numOffsets > math.MaxUint8 { @@ -1104,7 +1113,7 @@ func (ops *OpStream) checkArgCount(name string, mnemonic token, args []token, ex return nil } -// Basic assembly. Any extra bytes of opcode are encoded as byte immediates. +// Basic assembly, used for most opcodes. It assembles based on the information in OpSpec. func asmDefault(ops *OpStream, spec *OpSpec, mnemonic token, args []token) *sourceError { if err := ops.checkArgCount(spec.Name, mnemonic, args, len(spec.OpDetails.Immediates)); err != nil { return err @@ -1727,12 +1736,12 @@ func addPseudoDocTags() { continue } msg := fmt.Sprintf("`%s` can be called using `%s` with %s.", spec.Name, name, joinIntsOnOr("immediate", i)) - desc, ok := opDocByName[spec.Name] - if ok { - opDocByName[spec.Name] = desc + "
" + msg - } else { - opDocByName[spec.Name] = msg + desc := opDescByName[spec.Name] + if desc.Short == "" { + panic(spec.Name) } + desc.Short = desc.Short + "
" + msg + opDescByName[spec.Name] = desc } } } @@ -1778,14 +1787,14 @@ func mergeProtos(specs map[int]OpSpec) (Proto, uint64, bool) { } } if debugExplainFuncPtr == nil { - debugExplainFuncPtr = spec.Explain + debugExplainFuncPtr = spec.StackExplain } i++ } return Proto{ - Arg: typedList{args, ""}, - Return: typedList{returns, ""}, - Explain: debugExplainFuncPtr, + Arg: typedList{args, ""}, + Return: typedList{returns, ""}, + StackExplain: debugExplainFuncPtr, }, minVersion, true } @@ -1826,8 +1835,8 @@ func (se sourceError) Unwrap() error { return se.Err } -func errorLinef(line int, format string, a ...interface{}) *sourceError { - return &sourceError{line, 0, fmt.Errorf(format, a...)} +func sourceErrorf(location SourceLocation, format string, a ...interface{}) *sourceError { + return &sourceError{location.Line, location.Column, fmt.Errorf(format, a...)} } type token struct { @@ -2087,8 +2096,9 @@ func (ops *OpStream) assemble(text string) error { } spec, expandedName, ok := getSpec(ops, current[0], len(current)-1) if ok { - ops.trace("%3d: %s\t", ops.sourceLine, opstring) - ops.recordSourceLine() + line, column := current[0].line, current[0].col + ops.trace("%3d:%d %s\t", line, column, opstring) + ops.recordSourceLocation(line, column) if spec.Modes == ModeApp { ops.HasStatefulOps = true } @@ -2516,14 +2526,14 @@ func (ops *OpStream) optimizeConstants(refs []constReference, constBlock []inter } } if !found { - err = errorLinef(ops.OffsetToLine[ref.getPosition()], "value not found in constant block: %v", ref.getValue()) + err = sourceErrorf(ops.OffsetToSource[ref.getPosition()], "value not found in constant block: %v", ref.getValue()) return } } for _, f := range freqs { if f.freq == 0 { - err = errorLinef(ops.sourceLine, "member of constant block is not used: %v", f.value) + err = sourceErrorf(SourceLocation{ops.sourceLine, 0}, "member of constant block is not used: %v", f.value) return } } @@ -2554,7 +2564,7 @@ func (ops *OpStream) optimizeConstants(refs []constReference, constBlock []inter } } if newIndex == -1 { - return nil, errorLinef(ops.OffsetToLine[ref.getPosition()], "value not found in constant block: %v", ref.getValue()) + return nil, sourceErrorf(ops.OffsetToSource[ref.getPosition()], "value not found in constant block: %v", ref.getValue()) } newBytes := ref.makeNewReference(ops, singleton, newIndex) @@ -2601,15 +2611,15 @@ func (ops *OpStream) optimizeConstants(refs []constReference, constBlock []inter } } - fixedOffsetsToLine := make(map[int]int, len(ops.OffsetToLine)) - for pos, sourceLine := range ops.OffsetToLine { + fixedOffsetsToSource := make(map[int]SourceLocation, len(ops.OffsetToSource)) + for pos, sourceLocation := range ops.OffsetToSource { if pos > position { - fixedOffsetsToLine[pos+positionDelta] = sourceLine + fixedOffsetsToSource[pos+positionDelta] = sourceLocation } else { - fixedOffsetsToLine[pos] = sourceLine + fixedOffsetsToSource[pos] = sourceLocation } } - ops.OffsetToLine = fixedOffsetsToLine + ops.OffsetToSource = fixedOffsetsToSource } ops.pending = *bytes.NewBuffer(raw) @@ -2666,11 +2676,11 @@ func (ops *OpStream) prependCBlocks() []byte { } // fixup offset to line mapping - newOffsetToLine := make(map[int]int, len(ops.OffsetToLine)) - for o, l := range ops.OffsetToLine { - newOffsetToLine[o+pbl] = l + newOffsetToSource := make(map[int]SourceLocation, len(ops.OffsetToSource)) + for o, l := range ops.OffsetToSource { + newOffsetToSource[o+pbl] = l } - ops.OffsetToLine = newOffsetToLine + ops.OffsetToSource = newOffsetToSource return out } @@ -2684,7 +2694,7 @@ func (ops *OpStream) record(se *sourceError) { } func (ops *OpStream) warn(t token, format string, a ...interface{}) { - warning := &sourceError{Line: t.line, Column: t.col, Err: fmt.Errorf(format, a...)} + warning := &sourceError{t.line, t.col, fmt.Errorf(format, a...)} ops.Warnings = append(ops.Warnings, warning) } diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 1769cfa9fb..d983492501 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -463,15 +463,16 @@ var nonsense = map[uint64]string{ } var compiled = map[uint64]string{ - 1: "012008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b1716154000032903494", - 2: "022008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f", - 3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e", - 4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f2310231123122313231418191a1b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164", - 5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f2310231123122313231418191a1b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b400b53a03", - 6: "06" + v6Compiled, - 7: "07" + v7Compiled, - 8: "08" + v8Compiled, - 9: "09" + v9Compiled, + 1: "012008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b1716154000032903494", + 2: "022008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f", + 3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f2310231123122313231418191a1b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e", + 4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f2310231123122313231418191a1b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164", + 5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f2310231123122313231418191a1b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b400b53a03", + 6: "06" + v6Compiled, + 7: "07" + v7Compiled, + 8: "08" + v8Compiled, + 9: "09" + v9Compiled, + 10: "0a" + v10Compiled, } @@ -2150,73 +2151,77 @@ func TestAssembleOffsets(t *testing.T) { source := "err" ops := testProg(t, source, AssemblerMaxVersion) require.Equal(t, 2, len(ops.Program)) - require.Equal(t, 1, len(ops.OffsetToLine)) + require.Equal(t, 1, len(ops.OffsetToSource)) // vlen - line, ok := ops.OffsetToLine[0] + location, ok := ops.OffsetToSource[0] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // err - line, ok = ops.OffsetToLine[1] + location, ok = ops.OffsetToSource[1] require.True(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) source = `err // comment -err +err; err ` ops = testProg(t, source, AssemblerMaxVersion) - require.Equal(t, 3, len(ops.Program)) - require.Equal(t, 2, len(ops.OffsetToLine)) + require.Equal(t, 4, len(ops.Program)) + require.Equal(t, 3, len(ops.OffsetToSource)) // vlen - line, ok = ops.OffsetToLine[0] + location, ok = ops.OffsetToSource[0] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // err 1 - line, ok = ops.OffsetToLine[1] + location, ok = ops.OffsetToSource[1] require.True(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // err 2 - line, ok = ops.OffsetToLine[2] + location, ok = ops.OffsetToSource[2] require.True(t, ok) - require.Equal(t, 2, line) + require.Equal(t, SourceLocation{Line: 2}, location) + // err 3 + location, ok = ops.OffsetToSource[3] + require.True(t, ok) + require.Equal(t, SourceLocation{Line: 2, Column: 5}, location) source = `err b label1 err label1: -err + err ` ops = testProg(t, source, AssemblerMaxVersion) require.Equal(t, 7, len(ops.Program)) - require.Equal(t, 4, len(ops.OffsetToLine)) + require.Equal(t, 4, len(ops.OffsetToSource)) // vlen - line, ok = ops.OffsetToLine[0] + location, ok = ops.OffsetToSource[0] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // err 1 - line, ok = ops.OffsetToLine[1] + location, ok = ops.OffsetToSource[1] require.True(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // b - line, ok = ops.OffsetToLine[2] + location, ok = ops.OffsetToSource[2] require.True(t, ok) - require.Equal(t, 1, line) + require.Equal(t, SourceLocation{Line: 1}, location) // b byte 1 - line, ok = ops.OffsetToLine[3] + location, ok = ops.OffsetToSource[3] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // b byte 2 - line, ok = ops.OffsetToLine[4] + location, ok = ops.OffsetToSource[4] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // err 2 - line, ok = ops.OffsetToLine[5] + location, ok = ops.OffsetToSource[5] require.True(t, ok) - require.Equal(t, 2, line) + require.Equal(t, SourceLocation{Line: 2}, location) // err 3 - line, ok = ops.OffsetToLine[6] + location, ok = ops.OffsetToSource[6] require.True(t, ok) - require.Equal(t, 4, line) + require.Equal(t, SourceLocation{Line: 4, Column: 2}, location) source = `pushint 0 // comment @@ -2224,23 +2229,23 @@ err ` ops = testProg(t, source, AssemblerMaxVersion) require.Equal(t, 4, len(ops.Program)) - require.Equal(t, 2, len(ops.OffsetToLine)) + require.Equal(t, 2, len(ops.OffsetToSource)) // vlen - line, ok = ops.OffsetToLine[0] + location, ok = ops.OffsetToSource[0] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // pushint - line, ok = ops.OffsetToLine[1] + location, ok = ops.OffsetToSource[1] require.True(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // pushint byte 1 - line, ok = ops.OffsetToLine[2] + location, ok = ops.OffsetToSource[2] require.False(t, ok) - require.Equal(t, 0, line) + require.Equal(t, SourceLocation{}, location) // ! - line, ok = ops.OffsetToLine[3] + location, ok = ops.OffsetToSource[3] require.True(t, ok) - require.Equal(t, 2, line) + require.Equal(t, SourceLocation{Line: 2}, location) } func TestHasStatefulOps(t *testing.T) { @@ -2977,26 +2982,13 @@ func TestGetSpec(t *testing.T) { require.Equal(t, "unknown opcode: nonsense", ops.Errors[1].Err.Error()) } -func TestAddPseudoDocTags(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies pseudoOps and opDocByName which are global maps - partitiontest.PartitionTest(t) - defer func() { - delete(pseudoOps, "tests") - delete(opDocByName, "multiple") - delete(opDocByName, "single") - delete(opDocByName, "none") - delete(opDocByName, "any") - }() - - pseudoOps["tests"] = map[int]OpSpec{2: {Name: "multiple"}, 1: {Name: "single"}, 0: {Name: "none"}, anyImmediates: {Name: "any"}} - addPseudoDocTags() - require.Equal(t, "`multiple` can be called using `tests` with 2 immediates.", opDocByName["multiple"]) - require.Equal(t, "`single` can be called using `tests` with 1 immediate.", opDocByName["single"]) - require.Equal(t, "`none` can be called using `tests` with no immediates.", opDocByName["none"]) - require.Equal(t, "", opDocByName["any"]) -} func TestReplacePseudo(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() + + require.Contains(t, opDescByName["replace3"].Short, "`replace3` can be called using `replace` with no immediates.") + require.Contains(t, opDescByName["replace2"].Short, "`replace2` can be called using `replace` with 1 immediate.") + replaceVersion := 7 for v := uint64(replaceVersion); v <= AssemblerMaxVersion; v++ { testProg(t, "byte 0x0000; byte 0x1234; replace 0", v) diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go index dc9dcf7b67..ad4f1674bd 100644 --- a/data/transactions/logic/doc.go +++ b/data/transactions/logic/doc.go @@ -22,278 +22,282 @@ import ( "github.com/algorand/go-algorand/protocol" ) -// short description of every op -var opDocByName = map[string]string{ - "err": "Fail immediately.", - "sha256": "SHA256 hash of value A, yields [32]byte", - "keccak256": "Keccak256 hash of value A, yields [32]byte", - "sha512_256": "SHA512_256 hash of value A, yields [32]byte", - "sha3_256": "SHA3_256 hash of value A, yields [32]byte", - "ed25519verify": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey => {0 or 1}", - "ed25519verify_bare": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1}", - "ecdsa_verify": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1}", - "ecdsa_pk_decompress": "decompress pubkey A into components X, Y", - "ecdsa_pk_recover": "for (data A, recovery id B, signature C, D) recover a public key", - "bn256_add": "for (curve points A and B) return the curve point A + B", - "bn256_scalar_mul": "for (curve point A, scalar K) return the curve point KA", - "bn256_pairing": "for (points in G1 group G1s, points in G2 group G2s), return whether they are paired => {0 or 1}", - - "+": "A plus B. Fail on overflow.", - "-": "A minus B. Fail if B > A.", - "/": "A divided by B (truncated division). Fail if B == 0.", - "*": "A times B. Fail on overflow.", - "<": "A less than B => {0 or 1}", - ">": "A greater than B => {0 or 1}", - "<=": "A less than or equal to B => {0 or 1}", - ">=": "A greater than or equal to B => {0 or 1}", - "&&": "A is not zero and B is not zero => {0 or 1}", - "||": "A is not zero or B is not zero => {0 or 1}", - "==": "A is equal to B => {0 or 1}", - "!=": "A is not equal to B => {0 or 1}", - "!": "A == 0 yields 1; else 0", - "len": "yields length of byte value A", - "itob": "converts uint64 A to big-endian byte array, always of length 8", - "btoi": "converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8.", - "%": "A modulo B. Fail if B == 0.", - "|": "A bitwise-or B", - "&": "A bitwise-and B", - "^": "A bitwise-xor B", - "~": "bitwise invert value A", - "shl": "A times 2^B, modulo 2^64", - "shr": "A divided by 2^B", - "sqrt": "The largest integer I such that I^2 <= A", - "bitlen": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", - "exp": "A raised to the Bth power. Fail if A == B == 0 and on overflow", - "expw": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", - "mulw": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", - "addw": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", - "divw": "A,B / C. Fail if C == 0 or if result overflows.", - "divmodw": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", - - "intcblock": "prepare block of uint64 constants for use by intc", - "intc": "Ith constant from intcblock", - "intc_0": "constant 0 from intcblock", - "intc_1": "constant 1 from intcblock", - "intc_2": "constant 2 from intcblock", - "intc_3": "constant 3 from intcblock", - "pushint": "immediate UINT", - "pushints": "push sequence of immediate uints to stack in the order they appear (first uint being deepest)", - "bytecblock": "prepare block of byte-array constants for use by bytec", - "bytec": "Ith constant from bytecblock", - "bytec_0": "constant 0 from bytecblock", - "bytec_1": "constant 1 from bytecblock", - "bytec_2": "constant 2 from bytecblock", - "bytec_3": "constant 3 from bytecblock", - "pushbytes": "immediate BYTES", - "pushbytess": "push sequences of immediate byte arrays to stack (first byte array being deepest)", - - "bzero": "zero filled byte-array of length A", - "arg": "Nth LogicSig argument", - "arg_0": "LogicSig argument 0", - "arg_1": "LogicSig argument 1", - "arg_2": "LogicSig argument 2", - "arg_3": "LogicSig argument 3", - "args": "Ath LogicSig argument", - "txn": "field F of current transaction", - "gtxn": "field F of the Tth transaction in the current group", - "gtxns": "field F of the Ath transaction in the current group", - "txna": "Ith value of the array field F of the current transaction", - "gtxna": "Ith value of the array field F from the Tth transaction in the current group", - "gtxnsa": "Ith value of the array field F from the Ath transaction in the current group", - "txnas": "Ath value of the array field F of the current transaction", - "gtxnas": "Ath value of the array field F from the Tth transaction in the current group", - "gtxnsas": "Bth value of the array field F from the Ath transaction in the current group", - "itxn": "field F of the last inner transaction", - "itxna": "Ith value of the array field F of the last inner transaction", - "itxnas": "Ath value of the array field F of the last inner transaction", - "gitxn": "field F of the Tth transaction in the last inner group submitted", - "gitxna": "Ith value of the array field F from the Tth transaction in the last inner group submitted", - "gitxnas": "Ath value of the array field F from the Tth transaction in the last inner group submitted", - - "global": "global field F", - "load": "Ith scratch space value. All scratch spaces are 0 at program start.", - "store": "store A to the Ith scratch space", - "loads": "Ath scratch space value. All scratch spaces are 0 at program start.", - "stores": "store B to the Ath scratch space", - "gload": "Ith scratch space value of the Tth transaction in the current group", - "gloads": "Ith scratch space value of the Ath transaction in the current group", - "gloadss": "Bth scratch space value of the Ath transaction in the current group", - "gaid": "ID of the asset or application created in the Tth transaction of the current group", - "gaids": "ID of the asset or application created in the Ath transaction of the current group", - - "json_ref": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", - - "bnz": "branch to TARGET if value A is not zero", - "bz": "branch to TARGET if value A is zero", - "b": "branch unconditionally to TARGET", - "return": "use A as success value; end", - "pop": "discard A", - "dup": "duplicate A", - "dup2": "duplicate A and B", - "dupn": "duplicate A, N times", - "dig": "Nth value from the top of the stack. dig 0 is equivalent to dup", - "bury": "replace the Nth value from the top of the stack with A. bury 0 fails.", - "cover": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N.", - "uncover": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N.", - "swap": "swaps A and B on stack", - "select": "selects one of two values based on top-of-stack: B if C != 0, else A", - - "concat": "join A and B", - "substring": "A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails", - "substring3": "A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails", - "getbit": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", - "setbit": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", - "getbyte": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", - "setbyte": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", - "extract": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", - "extract3": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails", - "extract_uint16": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", - "extract_uint32": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", - "extract_uint64": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", - "replace2": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)", - "replace3": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)", - "base64_decode": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", - "balance": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", - "min_balance": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", - "app_opted_in": "1 if account A is opted in to application B, else 0", - "app_local_get": "local state of the key B in the current application in account A", - "app_local_get_ex": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", - "app_global_get": "global state of the key A in the current application", - "app_global_get_ex": "X is the global state of application A, key B. Y is 1 if key existed, else 0", - "app_local_put": "write C to key B in account A's local state of the current application", - "app_global_put": "write B to key A in the global state of the current application", - "app_local_del": "delete key B from account A's local state of the current application", - "app_global_del": "delete key A from the global state of the current application", - "asset_holding_get": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", - "asset_params_get": "X is field F from asset A. Y is 1 if A exists, else 0", - "app_params_get": "X is field F from app A. Y is 1 if A exists, else 0", - "acct_params_get": "X is field F from account A. Y is 1 if A owns positive algos, else 0", - "assert": "immediately fail unless A is a non-zero number", - "callsub": "branch unconditionally to TARGET, saving the next instruction on the call stack", - "proto": "Prepare top call frame for a retsub that will assume A args and R return values.", - "retsub": "pop the top instruction from the call stack and branch to it", - - "b+": "A plus B. A and B are interpreted as big-endian unsigned integers", - "b-": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", - "b/": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", - "b*": "A times B. A and B are interpreted as big-endian unsigned integers.", - "b<": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", - "b>": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", - "b<=": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", - "b>=": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", - "b==": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", - "b!=": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", - "b%": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", - "b|": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", - "b&": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", - "b^": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", - "b~": "A with all bits inverted", - - "bsqrt": "The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers", - - "log": "write A to log state of the current application", - "itxn_begin": "begin preparation of a new inner transaction in a new transaction group", - "itxn_next": "begin preparation of a new inner transaction in the same transaction group", - "itxn_field": "set field F of the current inner transaction to A", - "itxn_submit": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", - - "vrf_verify": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", - "block": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", - - "switch": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", - "match": "given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", - - "frame_dig": "Nth (signed) value from the frame pointer.", - "frame_bury": "replace the Nth (signed) value from the frame pointer in the stack with A", - "popn": "remove N values from the top of the stack", - - "box_create": "create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", - "box_extract": "read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", - "box_replace": "write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", - "box_del": "delete box named A if it exists. Return 1 if A existed, 0 otherwise", - "box_len": "X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", - "box_get": "X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", - "box_put": "replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", +// OpDesc contains the human readable descriptions of opcodes and their +// immediate arguments. +type OpDesc struct { + Short string + Extra string + Immediates []string } -// OpDoc returns a description of the op -func OpDoc(opName string) string { - return opDocByName[opName] +var opDescByName = map[string]OpDesc{ + "err": {"Fail immediately.", "", nil}, + + "sha256": {"SHA256 hash of value A, yields [32]byte", "", nil}, + "keccak256": {"Keccak256 hash of value A, yields [32]byte", "", nil}, + "sha512_256": {"SHA512_256 hash of value A, yields [32]byte", "", nil}, + "sha3_256": {"SHA3_256 hash of value A, yields [32]byte", "", nil}, + + "ed25519verify": {"for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey => {0 or 1}", "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", nil}, + "ed25519verify_bare": {"for (data A, signature B, pubkey C) verify the signature of the data against the pubkey => {0 or 1}", "", nil}, + "ecdsa_verify": {"for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1}", "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", []string{"curve index"}}, + "ecdsa_pk_decompress": {"decompress pubkey A into components X, Y", "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", []string{"curve index"}}, + "ecdsa_pk_recover": {"for (data A, recovery id B, signature C, D) recover a public key", "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", []string{"curve index"}}, + + "ec_add": {"for curve points A and B, return the curve point A + B", "" + + "A and B are curve points in affine representation: field element X concatenated with field element Y. " + + "Field element `Z` is encoded as follows.\n" + + "For the base field elements (Fp), `Z` is encoded as a big-endian number and must be lower than the field modulus.\n" + + "For the quadratic field extension (Fp2), `Z` is encoded as the concatenation of the individual encoding of the coefficients. " + + "For an Fp2 element of the form `Z = Z0 + Z1 i`, where `i` is a formal quadratic non-residue, the encoding of Z is the concatenation of the encoding of `Z0` and `Z1` in this order. (`Z0` and `Z1` must be less than the field modulus).\n\n" + + "The point at infinity is encoded as `(X,Y) = (0,0)`.\n" + + "Groups G1 and G2 are denoted additively.\n\n" + + "Fails if A or B is not in G.\n" + + "A and/or B are allowed to be the point at infinity.\n" + + "Does _not_ check if A and B are in the main prime-order subgroup.", + []string{"curve index"}, + }, + "ec_scalar_mul": {"for curve point A and scalar B, return the curve point BA, the point A multiplied by the scalar B.", + "A is a curve point encoded and checked as described in `ec_add`. Scalar B is interpreted as a big-endian unsigned integer. Fails if B exceeds 32 bytes.", + []string{"curve index"}, + }, + "ec_pairing_check": {"1 if the product of the pairing of each point in A with its respective point in B is equal to the identity element of the target group Gt, else 0", + "A and B are concatenated points, encoded and checked as described in `ec_add`. A contains points of the group G, B contains points of the associated group (G2 if G is G1, and vice versa). Fails if A and B have a different number of points, or if any point is not in its described group or outside the main prime-order subgroup - a stronger condition than other opcodes. AVM values are limited to 4096 bytes, so `ec_pairing_check` is limited by the size of the points in the groups being operated upon.", + []string{"curve index"}, + }, + "ec_multi_scalar_mul": {"for curve points A and scalars B, return curve point B0A0 + B1A1 + B2A2 + ... + BnAn", + "A is a list of concatenated points, encoded and checked as described in `ec_add`. B is a list of concatenated scalars which, unlike ec_scalar_mul, must all be exactly 32 bytes long.\nThe name `ec_multi_scalar_mul` was chosen to reflect common usage, but a more consistent name would be `ec_multi_scalar_mul`. AVM values are limited to 4096 bytes, so `ec_multi_scalar_mul` is limited by the size of the points in the group being operated upon.", + []string{"curve index"}, + }, + "ec_subgroup_check": {"1 if A is in the main prime-order subgroup of G (including the point at infinity) else 0. Program fails if A is not in G at all.", "", []string{"curve index"}}, + "ec_map_to": {"maps field element A to group G", "" + + "BN254 points are mapped by the SVDW map. BLS12-381 points are mapped by the SSWU map.\n" + + "G1 element inputs are base field elements and G2 element inputs are quadratic field elements, with nearly the same encoding rules (for field elements) as defined in `ec_add`. There is one difference of encoding rule: G1 element inputs do not need to be 0-padded if they fit in less than 32 bytes for BN254 and less than 48 bytes for BLS12-381. (As usual, the empty byte array represents 0.) G2 elements inputs need to be always have the required size.", + []string{"curve index"}, + }, + + "+": {"A plus B. Fail on overflow.", "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", nil}, + "-": {"A minus B. Fail if B > A.", "", nil}, + "/": {"A divided by B (truncated division). Fail if B == 0.", "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", nil}, + "*": {"A times B. Fail on overflow.", "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", nil}, + "<": {"A less than B => {0 or 1}", "", nil}, + ">": {"A greater than B => {0 or 1}", "", nil}, + "<=": {"A less than or equal to B => {0 or 1}", "", nil}, + ">=": {"A greater than or equal to B => {0 or 1}", "", nil}, + "&&": {"A is not zero and B is not zero => {0 or 1}", "", nil}, + "||": {"A is not zero or B is not zero => {0 or 1}", "", nil}, + "==": {"A is equal to B => {0 or 1}", "", nil}, + "!=": {"A is not equal to B => {0 or 1}", "", nil}, + "!": {"A == 0 yields 1; else 0", "", nil}, + + "len": {"yields length of byte value A", "", nil}, + "itob": {"converts uint64 A to big-endian byte array, always of length 8", "", nil}, + "btoi": {"converts big-endian byte array A to uint64. Fails if len(A) > 8. Padded by leading 0s if len(A) < 8.", + "`btoi` fails if the input is longer than 8 bytes.", nil}, + + "%": {"A modulo B. Fail if B == 0.", "", nil}, + "|": {"A bitwise-or B", "", nil}, + "&": {"A bitwise-and B", "", nil}, + "^": {"A bitwise-xor B", "", nil}, + "~": {"bitwise invert value A", "", nil}, + "shl": {"A times 2^B, modulo 2^64", "", nil}, + "shr": {"A divided by 2^B", "", nil}, + "sqrt": {"The largest integer I such that I^2 <= A", "", nil}, + "bitlen": {"The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", nil}, + "exp": {"A raised to the Bth power. Fail if A == B == 0 and on overflow", "", nil}, + "expw": {"A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", "", nil}, + "mulw": {"A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", "", nil}, + "addw": {"A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", "", nil}, + "divw": {"A,B / C. Fail if C == 0 or if result overflows.", + "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", nil}, + "divmodw": {"W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", nil}, + + "intcblock": {"prepare block of uint64 constants for use by intc", "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", []string{"a block of int constant values"}}, + "intc": {"Ith constant from intcblock", "", []string{"an index in the intcblock"}}, + "intc_0": {"constant 0 from intcblock", "", nil}, + "intc_1": {"constant 1 from intcblock", "", nil}, + "intc_2": {"constant 2 from intcblock", "", nil}, + "intc_3": {"constant 3 from intcblock", "", nil}, + "pushint": {"immediate UINT", "pushint args are not added to the intcblock during assembly processes", []string{"an int constant"}}, + "pushints": {"push sequence of immediate uints to stack in the order they appear (first uint being deepest)", "pushints args are not added to the intcblock during assembly processes", []string{"a list of int constants"}}, + "bytecblock": {"prepare block of byte-array constants for use by bytec", "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", []string{"a block of byte constant values"}}, + "bytec": {"Ith constant from bytecblock", "", []string{"an index in the bytecblock"}}, + "bytec_0": {"constant 0 from bytecblock", "", nil}, + "bytec_1": {"constant 1 from bytecblock", "", nil}, + "bytec_2": {"constant 2 from bytecblock", "", nil}, + "bytec_3": {"constant 3 from bytecblock", "", nil}, + "pushbytes": {"immediate BYTES", "pushbytes args are not added to the bytecblock during assembly processes", []string{"a byte constant"}}, + "pushbytess": {"push sequences of immediate byte arrays to stack (first byte array being deepest)", + "pushbytess args are not added to the bytecblock during assembly processes", + []string{"a list of byte constants"}}, + + "bzero": {"zero filled byte-array of length A", "", nil}, + "arg": {"Nth LogicSig argument", "", []string{"an arg index"}}, + "arg_0": {"LogicSig argument 0", "", nil}, + "arg_1": {"LogicSig argument 1", "", nil}, + "arg_2": {"LogicSig argument 2", "", nil}, + "arg_3": {"LogicSig argument 3", "", nil}, + "args": {"Ath LogicSig argument", "", nil}, + + "txn": {"field F of current transaction", "", []string{"transaction field index"}}, + "gtxn": { + "field F of the Tth transaction in the current group", + "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + []string{"transaction group index", "transaction field index"}, + }, + "gtxns": {"field F of the Ath transaction in the current group", + "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + []string{"transaction field index"}, + }, + "txna": {"Ith value of the array field F of the current transaction", "", + []string{"transaction field index", "transaction field array index"}}, + "gtxna": {"Ith value of the array field F from the Tth transaction in the current group", "", + []string{"transaction group index", "transaction field index", "transaction field array index"}}, + "gtxnsa": {"Ith value of the array field F from the Ath transaction in the current group", "", + []string{"transaction field index", "transaction field array index"}}, + "txnas": {"Ath value of the array field F of the current transaction", "", + []string{"transaction field index"}}, + "gtxnas": {"Ath value of the array field F from the Tth transaction in the current group", "", + []string{"transaction group index", "transaction field index"}}, + "gtxnsas": {"Bth value of the array field F from the Ath transaction in the current group", "", + []string{"transaction field index"}}, + "itxn": {"field F of the last inner transaction", "", []string{"transaction field index"}}, + "itxna": {"Ith value of the array field F of the last inner transaction", "", + []string{"transaction field index", "a transaction field array index"}}, + "itxnas": {"Ath value of the array field F of the last inner transaction", "", + []string{"transaction field index"}}, + "gitxn": {"field F of the Tth transaction in the last inner group submitted", "", + []string{"transaction group index", "transaction field index"}}, + "gitxna": {"Ith value of the array field F from the Tth transaction in the last inner group submitted", "", + []string{"transaction group index", "transaction field index", "transaction field array index"}}, + "gitxnas": {"Ath value of the array field F from the Tth transaction in the last inner group submitted", "", + []string{"transaction group index", "transaction field index"}}, + + "global": {"global field F", "", []string{"a global field index"}}, + "load": {"Ith scratch space value. All scratch spaces are 0 at program start.", "", + []string{"position in scratch space to load from"}}, + "store": {"store A to the Ith scratch space", "", + []string{"position in scratch space to store to"}}, + "loads": {"Ath scratch space value. All scratch spaces are 0 at program start.", "", nil}, + "stores": {"store B to the Ath scratch space", "", nil}, + "gload": {"Ith scratch space value of the Tth transaction in the current group", + "`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex.", + []string{"transaction group index", "position in scratch space to load from"}}, + "gloads": {"Ith scratch space value of the Ath transaction in the current group", + "`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex.", + []string{"position in scratch space to load from"}, + }, + "gloadss": {"Bth scratch space value of the Ath transaction in the current group", "", nil}, + "gaid": {"ID of the asset or application created in the Tth transaction of the current group", + "`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex.", + []string{"transaction group index"}}, + "gaids": {"ID of the asset or application created in the Ath transaction of the current group", + "`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex.", nil}, + + "json_ref": {"key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", + "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", + []string{"return type index"}}, + + "bnz": {"branch to TARGET if value A is not zero", "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", []string{"branch offset"}}, + "bz": {"branch to TARGET if value A is zero", "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", []string{"branch offset"}}, + "b": {"branch unconditionally to TARGET", "See `bnz` for details on how branches work. `b` always jumps to the offset.", []string{"branch offset"}}, + "return": {"use A as success value; end", "", nil}, + + "pop": {"discard A", "", nil}, + "dup": {"duplicate A", "", nil}, + "dup2": {"duplicate A and B", "", nil}, + "dupn": {"duplicate A, N times", "", []string{"copy count"}}, + "dig": {"Nth value from the top of the stack. dig 0 is equivalent to dup", "", []string{"depth"}}, + "bury": {"replace the Nth value from the top of the stack with A. bury 0 fails.", "", []string{"depth"}}, + "cover": {"remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth <= N.", "", []string{"depth"}}, + "uncover": {"remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth <= N.", "", []string{"depth"}}, + "swap": {"swaps A and B on stack", "", nil}, + "select": {"selects one of two values based on top-of-stack: B if C != 0, else A", "", nil}, + + "concat": {"join A and B", "`concat` fails if the result would be greater than 4096 bytes.", nil}, + "substring": {"A range of bytes from A starting at S up to but not including E. If E < S, or either is larger than the array length, the program fails", "", []string{"start position", "end position"}}, + "substring3": {"A range of bytes from A starting at B up to but not including C. If C < B, or either is larger than the array length, the program fails", "", nil}, + "getbit": {"Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "see explanation of bit ordering in setbit", nil}, + "setbit": {"Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", nil}, + "getbyte": {"Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", "", nil}, + "setbyte": {"Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", "", nil}, + "extract": {"A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", "", []string{"start position", "length"}}, + "extract3": {"A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails", "", nil}, + "extract_uint16": {"A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", "", nil}, + "extract_uint32": {"A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", "", nil}, + "extract_uint64": {"A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", "", nil}, + "replace2": {"Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)", "", []string{"start position"}}, + "replace3": {"Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)", "", nil}, + + "base64_decode": {"decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings. This opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", []string{"encoding index"}}, + + "balance": {"balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", nil}, + "min_balance": {"minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", nil}, + "app_opted_in": {"1 if account A is opted in to application B, else 0", "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", nil}, + "app_local_get": {"local state of the key B in the current application in account A", "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", nil}, + "app_local_get_ex": {"X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", nil}, + "app_global_get": {"global state of the key A in the current application", "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", nil}, + "app_global_get_ex": {"X is the global state of application A, key B. Y is 1 if key existed, else 0", "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", nil}, + "app_local_put": {"write C to key B in account A's local state of the current application", "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", nil}, + "app_global_put": {"write B to key A in the global state of the current application", "", nil}, + "app_local_del": {"delete key B from account A's local state of the current application", "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", nil}, + "app_global_del": {"delete key A from the global state of the current application", "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", nil}, + "asset_holding_get": {"X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", []string{"asset holding field index"}}, + "asset_params_get": {"X is field F from asset A. Y is 1 if A exists, else 0", "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", []string{"asset params field index"}}, + "app_params_get": {"X is field F from app A. Y is 1 if A exists, else 0", "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", []string{"app params field index"}}, + "acct_params_get": {"X is field F from account A. Y is 1 if A owns positive algos, else 0", "", []string{"account params field index"}}, + "assert": {"immediately fail unless A is a non-zero number", "", nil}, + "callsub": {"branch unconditionally to TARGET, saving the next instruction on the call stack", "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", []string{"branch offset"}}, + "proto": {"Prepare top call frame for a retsub that will assume A args and R return values.", "Fails unless the last instruction executed was a `callsub`.", []string{"number of arguments", "number of return values"}}, + "retsub": {"pop the top instruction from the call stack and branch to it", "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", nil}, + + "b+": {"A plus B. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b-": {"A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", "", nil}, + "b/": {"A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil}, + "b*": {"A times B. A and B are interpreted as big-endian unsigned integers.", "", nil}, + "b<": {"1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b>": {"1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b<=": {"1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b>=": {"1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b==": {"1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b!=": {"0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", "", nil}, + "b%": {"A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "", nil}, + "b|": {"A bitwise-or B. A and B are zero-left extended to the greater of their lengths", "", nil}, + "b&": {"A bitwise-and B. A and B are zero-left extended to the greater of their lengths", "", nil}, + "b^": {"A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", "", nil}, + "b~": {"A with all bits inverted", "", nil}, + + "bsqrt": {"The largest integer I such that I^2 <= A. A and I are interpreted as big-endian unsigned integers", "", nil}, + + "log": {"write A to log state of the current application", "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", nil}, + "itxn_begin": {"begin preparation of a new inner transaction in a new transaction group", "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", nil}, + "itxn_next": {"begin preparation of a new inner transaction in the same transaction group", "`itxn_next` initializes the transaction exactly as `itxn_begin` does", nil}, + "itxn_field": {"set field F of the current inner transaction to A", "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", []string{"transaction field index"}}, + "itxn_submit": {"execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", nil}, + + "vrf_verify": {"Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", []string{" parameters index"}}, + "block": {"field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", "", []string{" block field index"}}, + + "switch": {"branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", "", []string{"list of labels"}}, + "match": {"given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", []string{"list of labels"}}, + + "frame_dig": {"Nth (signed) value from the frame pointer.", "", []string{"frame slot"}}, + "frame_bury": {"replace the Nth (signed) value from the frame pointer in the stack with A", "", []string{"frame slot"}}, + "popn": {"remove N values from the top of the stack", "", []string{"stack depth"}}, + + "box_create": {"create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", nil}, + "box_extract": {"read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "", nil}, + "box_replace": {"write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "", nil}, + "box_del": {"delete box named A if it exists. Return 1 if A existed, 0 otherwise", "", nil}, + "box_len": {"X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", "", nil}, + "box_get": {"X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", nil}, + "box_put": {"replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", nil}, } -var opcodeImmediateNotes = map[string][]string{ - "intcblock": {"a block of int constant values"}, - "intc": {"an index in the intcblock"}, - "pushint": {"an int constant"}, - "pushints": {"a list of int constants"}, - "bytecblock": {"a block of byte constant values"}, - "bytec": {"an index in the bytecblock"}, - "pushbytes": {"a byte constant"}, - "pushbytess": {"a list of byte constants"}, - - "arg": {"an arg index"}, - "global": {"a global field index"}, - - "txn": {"transaction field index"}, - "gtxn": {"transaction group index", "transaction field index"}, - "gtxns": {"transaction field index"}, - "txna": {"transaction field index", "transaction field array index"}, - "gtxna": {"transaction group index", "transaction field index", "transaction field array index"}, - "gtxnsa": {"transaction field index", "transaction field array index"}, - "txnas": {"transaction field index"}, - "gtxnas": {"transaction group index", "transaction field index"}, - "gtxnsas": {"transaction field index"}, - - "bnz": {"branch offset"}, - "bz": {"branch offset"}, - "b": {"branch offset"}, - "callsub": {"branch offset"}, - - "load": {"position in scratch space to load from"}, - "store": {"position in scratch space to store to"}, - "gload": {"transaction group index", "position in scratch space to load from"}, - "gloads": {"position in scratch space to load from"}, - "gaid": {"transaction group index"}, - - "substring": {"start position", "end position"}, - "extract": {"start position", "length"}, - "replace2": {"start position"}, - "dig": {"depth"}, - "bury": {"depth"}, - "cover": {"depth"}, - "uncover": {"depth"}, - - "asset_holding_get": {"asset holding field index"}, - "asset_params_get": {"asset params field index"}, - "app_params_get": {"app params field index"}, - "acct_params_get": {"account params field index"}, - - "itxn_field": {"transaction field index"}, - "itxn": {"transaction field index"}, - "itxna": {"transaction field index", "a transaction field array index"}, - "itxnas": {"transaction field index"}, - "gitxn": {"transaction group index", "transaction field index"}, - "gitxna": {"transaction group index", "transaction field index", "transaction field array index"}, - "gitxnas": {"transaction group index", "transaction field index"}, - - "ecdsa_verify": {"curve index"}, - "ecdsa_pk_decompress": {"curve index"}, - "ecdsa_pk_recover": {"curve index"}, - - "base64_decode": {"encoding index"}, - "json_ref": {"return type index"}, - - "vrf_verify": {" parameters index"}, - "block": {" block field index"}, - - "switch": {"list of labels"}, - "match": {"list of labels"}, - - "proto": {"number of arguments", "number of return values"}, - "frame_dig": {"frame slot"}, - "frame_bury": {"frame slot"}, - "popn": {"stack depth"}, - "dupn": {"copy count"}, +// OpDoc returns a description of the op +func OpDoc(opName string) string { + return opDescByName[opName].Short } // OpImmediateDetails contains information about the an immediate argument for @@ -309,7 +313,7 @@ type OpImmediateDetails struct { // OpImmediateDetailsFromSpec provides a slice of OpImmediateDetails // for a given OpSpec func OpImmediateDetailsFromSpec(spec OpSpec) []OpImmediateDetails { - argNotes := opcodeImmediateNotes[spec.Name] + argNotes := opDescByName[spec.Name].Immediates if len(argNotes) == 0 { return nil } @@ -330,83 +334,16 @@ func OpImmediateDetailsFromSpec(spec OpSpec) []OpImmediateDetails { return details } -// further documentation on the function of the opcode -var opDocExtras = map[string]string{ - "vrf_verify": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", - "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", - "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", - "ecdsa_pk_decompress": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", - "ecdsa_pk_recover": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", - "bn256_add": "A, B are curve points in G1 group. Each point consists of (X, Y) where X and Y are 256 bit integers, big-endian encoded. The encoded point is 64 bytes from concatenation of 32 byte X and 32 byte Y.", - "bn256_scalar_mul": "A is a curve point in G1 Group and encoded as described in `bn256_add`. Scalar K is a big-endian encoded big integer that has no padding zeros.", - "bn256_pairing": "G1s are encoded by the concatenation of encoded G1 points, as described in `bn256_add`. G2s are encoded by the concatenation of encoded G2 points. Each G2 is in form (XA0+i*XA1, YA0+i*YA1) and encoded by big-endian field element XA0, XA1, YA0 and YA1 in sequence.", - "bnz": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", - "bz": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", - "b": "See `bnz` for details on how branches work. `b` always jumps to the offset.", - "callsub": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", - "proto": "Fails unless the last instruction executed was a `callsub`.", - "retsub": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", - "intcblock": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", - "bytecblock": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", - "*": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", - "+": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", - "/": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", - "bitlen": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", - "divw": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", - "divmodw": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", - "gtxn": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", - "gtxns": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", - "gload": "`gload` fails unless the requested transaction is an ApplicationCall and T < GroupIndex.", - "gloads": "`gloads` fails unless the requested transaction is an ApplicationCall and A < GroupIndex.", - "gaid": "`gaid` fails unless the requested transaction created an asset or application and T < GroupIndex.", - "gaids": "`gaids` fails unless the requested transaction created an asset or application and A < GroupIndex.", - "btoi": "`btoi` fails if the input is longer than 8 bytes.", - "concat": "`concat` fails if the result would be greater than 4096 bytes.", - "pushbytes": "pushbytes args are not added to the bytecblock during assembly processes", - "pushbytess": "pushbytess args are not added to the bytecblock during assembly processes", - "pushint": "pushint args are not added to the intcblock during assembly processes", - "pushints": "pushints args are not added to the intcblock during assembly processes", - "getbit": "see explanation of bit ordering in setbit", - "setbit": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", - "balance": "params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value.", - "min_balance": "params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value.", - "app_opted_in": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", - "app_local_get": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", - "app_local_get_ex": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", - "app_global_get_ex": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", - "app_global_get": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", - "app_local_put": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", - "app_local_del": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", - "app_global_del": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", - "asset_holding_get": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", - "asset_params_get": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", - "app_params_get": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", - "log": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", - "itxn_begin": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", - "itxn_next": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", - "itxn_field": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", - "itxn_submit": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", - - "base64_decode": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings. This opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", - "json_ref": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", - - "match": "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", - - "box_create": "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", - "box_get": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", - "box_put": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", -} - // OpDocExtra returns extra documentation text about an op func OpDocExtra(opName string) string { - return opDocExtras[opName] + return opDescByName[opName].Extra } // OpGroups is groupings of ops for documentation purposes. The order // here is the order args opcodes are presented, so place related // opcodes consecutively, even if their opcode values are not. var OpGroups = map[string][]string{ - "Arithmetic": {"sha256", "keccak256", "sha512_256", "sha3_256", "ed25519verify", "ed25519verify_bare", "ecdsa_verify", "ecdsa_pk_recover", "ecdsa_pk_decompress", "vrf_verify", "bn256_add", "bn256_scalar_mul", "bn256_pairing", "+", "-", "/", "*", "<", ">", "<=", ">=", "&&", "||", "shl", "shr", "sqrt", "bitlen", "exp", "==", "!=", "!", "len", "itob", "btoi", "%", "|", "&", "^", "~", "mulw", "addw", "divw", "divmodw", "expw", "getbit", "setbit", "getbyte", "setbyte", "concat"}, + "Arithmetic": {"sha256", "keccak256", "sha512_256", "sha3_256", "ed25519verify", "ed25519verify_bare", "ecdsa_verify", "ecdsa_pk_recover", "ecdsa_pk_decompress", "vrf_verify", "ec_add", "ec_scalar_mul", "ec_pairing_check", "ec_multi_scalar_mul", "ec_subgroup_check", "ec_map_to", "+", "-", "/", "*", "<", ">", "<=", ">=", "&&", "||", "shl", "shr", "sqrt", "bitlen", "exp", "==", "!=", "!", "len", "itob", "btoi", "%", "|", "&", "^", "~", "mulw", "addw", "divw", "divmodw", "expw", "getbit", "setbit", "getbyte", "setbyte", "concat"}, "Byte Array Manipulation": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "replace2", "replace3", "base64_decode", "json_ref"}, "Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%", "bsqrt"}, "Byte Array Logic": {"b|", "b&", "b^", "b~"}, @@ -427,29 +364,6 @@ type VerCost struct { Cost string } -// OpAllCosts returns an array of the cost of an op by version. Each entry -// indicates the cost over a range of versions, so if the cost has remained -// constant, there is only one result, otherwise each entry shows the cost for a -// consecutive range of versions, inclusive. -func OpAllCosts(opName string) []VerCost { - var costs []VerCost - for v := 1; v <= LogicVersion; v++ { - spec, ok := OpsByName[v][opName] - if !ok { - continue - } - argLen := len(spec.Arg.Types) - cost := spec.OpDetails.docCost(argLen) - if costs == nil || cost != costs[len(costs)-1].Cost { - costs = append(costs, VerCost{v, v, cost}) - } else { - costs[len(costs)-1].To = v - } - } - - return costs -} - // TypeNameDescriptions contains extra description about a low level // protocol transaction Type string, and provide a friendlier type // constant name in assembler. diff --git a/data/transactions/logic/doc_test.go b/data/transactions/logic/doc_test.go index cf1c6f0295..300c0ffdb9 100644 --- a/data/transactions/logic/doc_test.go +++ b/data/transactions/logic/doc_test.go @@ -33,36 +33,20 @@ func TestOpDocs(t *testing.T) { for _, op := range OpSpecs { opsSeen[op.Name] = false } - for name := range opDocByName { + for name := range opDescByName { if _, ok := opsSeen[name]; !ok { // avoid assert.Contains: printing opsSeen is waste - assert.Fail(t, "opDocByName contains strange opcode", "%#v", name) + assert.Fail(t, "opDescByName contains strange opcode", "%#v", name) } opsSeen[name] = true } for op, seen := range opsSeen { - assert.True(t, seen, "opDocByName is missing doc for %#v", op) + assert.True(t, seen, "opDescByName is missing description for %#v", op) } require.Len(t, onCompletionDescriptions, len(OnCompletionNames)) require.Len(t, TypeNameDescriptions, len(TxnTypeNames)) } -// TestDocStragglers confirms that we don't have any docs laying -// around for non-existent opcodes, most likely from a rename. -func TestDocStragglers(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - - for op := range opDocExtras { - _, ok := opDocByName[op] - assert.True(t, ok, "%s is in opDocExtra, but not opDocByName", op) - } - for op := range opcodeImmediateNotes { - _, ok := opDocByName[op] - assert.True(t, ok, "%s is in opcodeImmediateNotes, but not opDocByName", op) - } -} - func TestOpGroupCoverage(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -129,21 +113,6 @@ func TestOpDocExtra(t *testing.T) { require.Empty(t, xd) } -func TestOpAllCosts(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - - a := OpAllCosts("+") - require.Len(t, a, 1) - require.Equal(t, "1", a[0].Cost) - - a = OpAllCosts("sha256") - require.Len(t, a, 2) - for _, cost := range a { - require.True(t, cost.Cost != "0") - } -} - func TestOnCompletionDescription(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 871aa0399a..ad4acd69dd 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -2509,13 +2509,20 @@ func branchTarget(cx *EvalContext) (int, error) { } func switchTarget(cx *EvalContext, branchIdx uint64) (int, error) { + if cx.pc+1 >= len(cx.program) { + opcode := cx.program[cx.pc] + spec := &opsByOpcode[cx.version][opcode] + return 0, fmt.Errorf("bare %s opcode at end of program", spec.Name) + } numOffsets := int(cx.program[cx.pc+1]) end := cx.pc + 2 // end of opcode + number of offsets, beginning of offset list eoi := end + 2*numOffsets // end of instruction if eoi > len(cx.program) { // eoi will equal len(p) if switch is last instruction - return 0, fmt.Errorf("switch claims to extend beyond program") + opcode := cx.program[cx.pc] + spec := &opsByOpcode[cx.version][opcode] + return 0, fmt.Errorf("%s opcode claims to extend beyond program", spec.Name) } offset := 0 @@ -2550,8 +2557,13 @@ func checkBranch(cx *EvalContext) error { return nil } -// checks switch is encoded properly (and calculates nextpc) +// checks switch or match is encoded properly (and calculates nextpc) func checkSwitch(cx *EvalContext) error { + if cx.pc+1 >= len(cx.program) { + opcode := cx.program[cx.pc] + spec := &opsByOpcode[cx.version][opcode] + return fmt.Errorf("bare %s opcode at end of program", spec.Name) + } numOffsets := int(cx.program[cx.pc+1]) eoi := cx.pc + 2 + 2*numOffsets @@ -2628,6 +2640,9 @@ func opSwitch(cx *EvalContext) error { } func opMatch(cx *EvalContext) error { + if cx.pc+1 >= len(cx.program) { + return fmt.Errorf("bare match opcode at end of program") + } n := int(cx.program[cx.pc+1]) // stack contains the n sized match list and the single match value if n+1 > len(cx.Stack) { diff --git a/data/transactions/logic/evalCrypto_test.go b/data/transactions/logic/evalCrypto_test.go index e4b22e4975..946b649562 100644 --- a/data/transactions/logic/evalCrypto_test.go +++ b/data/transactions/logic/evalCrypto_test.go @@ -25,11 +25,9 @@ import ( "encoding/hex" "fmt" "math/big" - mrand "math/rand" "strconv" "testing" - "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/stretchr/testify/require" "golang.org/x/exp/slices" @@ -621,7 +619,7 @@ ecdsa_verify Secp256k1 ! assert global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-1700-8) + ` +int ` + fmt.Sprintf("%d", testLogicBudget-1700-8) + ` == ` testAccepts(t, source, 6) // Secp256k1 was 5, but OpcodeBudget is 6 @@ -636,7 +634,7 @@ ecdsa_verify Secp256r1 ! assert global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-2500-8) + ` +int ` + fmt.Sprintf("%d", testLogicBudget-2500-8) + ` == ` testAccepts(t, source, fidoVersion) @@ -870,132 +868,3 @@ int 1` benchmarkEcdsa(b, source, Secp256k1) }) } - -type benchmarkBn256Data struct { - a []byte - k []byte - g1 []byte - g2 []byte - programs []byte -} - -func benchmarkBn256DataGenData(b *testing.B) (data []benchmarkBn256Data) { - data = make([]benchmarkBn256Data, b.N) - var g1Gen bn254.G1Jac - var g1GenAff bn254.G1Affine - g1Gen.X.SetString("1") - g1Gen.Y.SetString("2") - g1Gen.Z.SetString("1") - g1GenAff.FromJacobian(&g1Gen) - var a bn254.G1Affine - a.ScalarMultiplication(&g1GenAff, new(big.Int).SetUint64(mrand.Uint64())) - - for i := 0; i < b.N; i++ { - var a bn254.G1Affine - a.ScalarMultiplication(&g1GenAff, new(big.Int).SetUint64(mrand.Uint64())) - - data[i].a = bN254G1ToBytes(&a) - data[i].k = new(big.Int).SetUint64(mrand.Uint64()).Bytes() - - // Pair one g1 and one g2 - data[i].g1, _ = hex.DecodeString("0ebc9fc712b13340c800793386a88385e40912a21bacad2cc7db17d36e54c802238449426931975cced7200f08681ab9a86a2e5c2336cf625451cf2413318e32") - data[i].g2, _ = hex.DecodeString("217fbd9a9db5719cfbe3580e3d8750cada058fdfffe95c440a0528ffc608f36e05d6a67604658d40b3e4cac3c46150f2702d87739b7774d79a8147f7271773b420f9429ee13c1843404bfd70e75efa886c173e57dde32970274d8bc53dfd562403f6276318990d053785b4ca342ebc4581a23a39285804bb74e079aa2ef3ba66") - } - return data -} - -func benchmarkBn256(b *testing.B, source string) { - data := benchmarkBn256DataGenData(b) - ops, err := AssembleStringWithVersion(source, pairingVersion) - require.NoError(b, err) - for i := 0; i < b.N; i++ { - data[i].programs = ops.Program - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - var txn transactions.SignedTxn - txn.Lsig.Logic = data[i].programs - txn.Lsig.Args = [][]byte{data[i].a, data[i].k, data[i].g1, data[i].g2} - ep := defaultSigParams(txn) - pass, err := EvalSignature(0, ep) - if !pass { - b.Log(hex.EncodeToString(data[i].programs)) - b.Log(ep.Trace.String()) - } - if err != nil { - require.NoError(b, err) - } - if !pass { - require.True(b, pass) - } - } -} - -func BenchmarkBn256AddRaw(b *testing.B) { - data := benchmarkBn256DataGenData(b) - a1 := bytesToBN254G1(data[0].g1) - a2 := bytesToBN254G1(data[0].g1) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = new(bn254.G1Affine).Add(&a1, &a2) - } -} - -func BenchmarkBn256AddWithMarshal(b *testing.B) { - b.ResetTimer() - var v [][]byte - v = make([][]byte, b.N) - g1, _ := hex.DecodeString("0ebc9fc712b13340c800793386a88385e40912a21bacad2cc7db17d36e54c802238449426931975cced7200f08681ab9a86a2e5c2336cf625451cf2413318e32") - - for i := 0; i < b.N; i++ { - a1 := bytesToBN254G1(g1) - a2 := bytesToBN254G1(g1) - r := new(bn254.G1Affine).Add(&a1, &a2) - v[i] = r.Marshal() - } -} - -func BenchmarkBn256PairingRaw(b *testing.B) { - data := benchmarkBn256DataGenData(b) - g1s := bytesToBN254G1s(data[0].g1) - g2s := bytesToBN254G2s(data[0].g2) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ok, _ := bn254.PairingCheck(g1s, g2s) - require.False(b, ok) - } -} - -func BenchmarkBn256(b *testing.B) { - if pairingVersion > LogicVersion { - b.Skip() - } - b.Run("bn256 add", func(b *testing.B) { - benchmarkOperation(b, "byte 0x0ebc9fc712b13340c800793386a88385e40912a21bacad2cc7db17d36e54c802238449426931975cced7200f08681ab9a86a2e5c2336cf625451cf2413318e32", "dup; bn256_add", "pop; int 1") - }) - - b.Run("bn256 scalar mul", func(b *testing.B) { - source := ` -arg 0 -arg 1 -bn256_scalar_mul -pop -int 1 -` - benchmarkBn256(b, source) - }) - - b.Run("bn256 pairing", func(b *testing.B) { - source := ` -arg 2 -arg 3 -bn256_pairing -pop -int 1 -` - benchmarkBn256(b, source) - }) -} diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index ab7167c7c8..5f2a5970b7 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -3108,9 +3108,13 @@ func TestReturnTypes(t *testing.T) { "frame_dig": true, // would need a "proto" subroutine "frame_bury": true, // would need a "proto" subroutine - "bn256_add": true, - "bn256_scalar_mul": true, - "bn256_pairing": true, + // These should not remain here, we should be able to construct example + "ec_add": true, + "ec_scalar_mul": true, + "ec_pairing_check": true, + "ec_multi_scalar_mul": true, + "ec_subgroup_check": true, + "ec_map_to": true, } byName := OpsByName[LogicVersion] diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 20479c6905..230ad8c640 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -52,6 +52,8 @@ func protoVer(version uint64) protoOpt { } } +var testLogicBudget = 25_000 // In a var so that we can temporarily change it + func makeTestProtoV(version uint64) *config.ConsensusParams { return makeTestProto(protoVer(version)) } @@ -59,8 +61,8 @@ func makeTestProtoV(version uint64) *config.ConsensusParams { func makeTestProto(opts ...protoOpt) *config.ConsensusParams { p := config.ConsensusParams{ LogicSigVersion: LogicVersion, - LogicSigMaxCost: 20000, Application: true, + LogicSigMaxCost: uint64(testLogicBudget), MaxAppProgramCost: 700, MaxAppKeyLen: 64, @@ -1753,7 +1755,6 @@ const testTxnProgramTextV9 = testTxnProgramTextV8 + ` assert int 1 ` - const testTxnProgramTextV10 = testTxnProgramTextV9 + ` assert int 1 @@ -1862,15 +1863,16 @@ func TestTxn(t *testing.T) { t.Parallel() tests := map[uint64]string{ - 1: testTxnProgramTextV1, - 2: testTxnProgramTextV2, - 3: testTxnProgramTextV3, - 4: testTxnProgramTextV4, - 5: testTxnProgramTextV5, - 6: testTxnProgramTextV6, - 7: testTxnProgramTextV7, - 8: testTxnProgramTextV8, - 9: testTxnProgramTextV9, + 1: testTxnProgramTextV1, + 2: testTxnProgramTextV2, + 3: testTxnProgramTextV3, + 4: testTxnProgramTextV4, + 5: testTxnProgramTextV5, + 6: testTxnProgramTextV6, + 7: testTxnProgramTextV7, + 8: testTxnProgramTextV8, + 9: testTxnProgramTextV9, + 10: testTxnProgramTextV10, } @@ -2996,9 +2998,9 @@ func TestSlowLogic(t *testing.T) { testAccepts(t, source, 1) // in v1, each repeat costs 30 - v1overspend := fragment + strings.Repeat(fragment+"&&; ", 20000/30) + v1overspend := fragment + strings.Repeat(fragment+"&&; ", testLogicBudget/30) // in v2,v3 each repeat costs 134 - v2overspend := fragment + strings.Repeat(fragment+"&&; ", 20000/134) + v2overspend := fragment + strings.Repeat(fragment+"&&; ", testLogicBudget/134) // v1overspend fails (on v1) ops := testProg(t, v1overspend, 1) @@ -3038,13 +3040,14 @@ int %d == `, budget-1, budget-5) } - testLogic(t, source(20000), LogicVersion, nil) + b := testLogicBudget + testLogic(t, source(b), LogicVersion, nil) - testLogics(t, []string{source(40000), source(39993)}, nil, nil) + testLogics(t, []string{source(2 * b), source(2*b - 7)}, nil, nil) - testLogics(t, []string{source(60000), source(59993), ""}, nil, nil) + testLogics(t, []string{source(3 * b), source(3*b - 7), ""}, nil, nil) - testLogics(t, []string{source(20000), source(20000)}, nil, + testLogics(t, []string{source(b), source(b)}, nil, func(p *config.ConsensusParams) { p.EnableLogicSigCostPooling = false }) } @@ -3153,8 +3156,7 @@ done: int 1 `, v) // cut two last bytes - intc_1 and last byte of bnz - ops.Program = ops.Program[:len(ops.Program)-2] - testLogicBytes(t, ops.Program, nil, + testLogicBytes(t, ops.Program[:len(ops.Program)-2], nil, "bnz program ends short", "bnz program ends short") }) } @@ -3214,57 +3216,62 @@ func TestShortBytecblock2(t *testing.T) { const panicString = "out of memory, buffer overrun, stack overflow, divide by zero, halt and catch fire" -func opPanic(cx *EvalContext) error { - panic(panicString) -} -func checkPanic(cx *EvalContext) error { - panic(panicString) -} - -// withPanicOpcode temporarily modifies the opsByOpcode array to include an additional panic opcode. -// This opcode will be named "panic". +// withOpcode temporarily modifies the opsByOpcode array to include an +// additional opcode, specieid by op. // // WARNING: do not call this in a parallel test, since it's not safe for concurrent use. -func withPanicOpcode(t *testing.T, version uint64, panicDuringCheck bool, f func(opcode byte)) { +func withOpcode(t *testing.T, version uint64, op OpSpec, f func(opcode byte)) { t.Helper() - const name = "panic" var foundEmptySpace bool var hackedOpcode byte var oldSpec OpSpec - // Find an unused opcode to temporarily convert to a panicing opcode, - // and append it to program. + // Find an unused opcode to temporarily convert to op for opcode, spec := range opsByOpcode[version] { if spec.op == nil { foundEmptySpace = true require.LessOrEqual(t, opcode, math.MaxUint8) hackedOpcode = byte(opcode) oldSpec = spec - - details := detDefault() - if panicDuringCheck { - details.check = checkPanic - } - panicSpec := OpSpec{ - Opcode: hackedOpcode, - Name: name, - op: opPanic, - OpDetails: details, - } - - opsByOpcode[version][opcode] = panicSpec - OpsByName[version][name] = panicSpec + copy := op + copy.Opcode = hackedOpcode + opsByOpcode[version][opcode] = copy + OpsByName[version][op.Name] = copy break } } - require.True(t, foundEmptySpace, "could not find an empty space for the panic opcode") + require.True(t, foundEmptySpace, "could not find an empty space for the opcode") defer func() { opsByOpcode[version][hackedOpcode] = oldSpec - delete(OpsByName[version], name) + delete(OpsByName[version], op.Name) }() f(hackedOpcode) } +// withPanicOpcode temporarily modifies the opsByOpcode array to include an additional panic opcode. +// This opcode will be named "panic". +// +// WARNING: do not call this in a parallel test, since it's not safe for concurrent use. +func withPanicOpcode(t *testing.T, version uint64, panicDuringCheck bool, f func(opcode byte)) { + t.Helper() + + opPanic := func(cx *EvalContext) error { + panic(panicString) + } + details := detDefault() + if panicDuringCheck { + details.check = opPanic + } + + panicSpec := OpSpec{ + Name: "panic", + op: opPanic, + OpDetails: details, + } + + withOpcode(t, version, panicSpec, f) +} + func TestPanic(t *testing.T) { //nolint:paralleltest // Uses withPanicOpcode partitiontest.PartitionTest(t) @@ -3729,12 +3736,17 @@ int 142791994204213819 + ` -func evalLoop(b *testing.B, runs int, program []byte) { +func evalLoop(b *testing.B, runs int, programs ...[]byte) { + program := programs[0] + final := programs[len(programs)-1] b.Helper() b.ResetTimer() for i := 0; i < runs; i++ { var txn transactions.SignedTxn txn.Lsig.Logic = program + if i == runs-1 { + txn.Lsig.Logic = final + } pass, err := EvalSignature(0, benchmarkSigParams(txn)) if !pass { // rerun to trace it. tracing messes up timing too much @@ -3766,11 +3778,18 @@ func benchmarkBasicProgram(b *testing.B, source string) { // the idea is that you can subtract that out from the reported speed func benchmarkOperation(b *testing.B, prefix string, operation string, suffix string) { b.Helper() - runs := 1 + b.N/2000 + runs := b.N / 2000 inst := strings.Count(operation, ";") + strings.Count(operation, "\n") source := prefix + ";" + strings.Repeat(operation+"\n", 2000) + ";" + suffix ops := testProg(b, source, AssemblerMaxVersion) - evalLoop(b, runs, ops.Program) + finalOps := ops + + if b.N%2000 != 0 { + runs++ + finalSource := prefix + ";" + strings.Repeat(operation+"\n", b.N%2000) + ";" + suffix + finalOps = testProg(b, finalSource, AssemblerMaxVersion) + } + evalLoop(b, runs, ops.Program, finalOps.Program) b.ReportMetric(float64(inst), "extra/op") } @@ -5259,7 +5278,7 @@ byte "" base64_decode URLEncoding pop global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-3-1) + ` // base64_decode cost = 1 +int ` + fmt.Sprintf("%d", testLogicBudget-3-1) + ` // base64_decode cost = 1 == ` testAccepts(t, source, fidoVersion) @@ -5269,7 +5288,7 @@ byte "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" base64_decode URLEncoding pop global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-3-5) + ` // base64_decode cost = 5 (64 bytes -> 1 + 64/16) +int ` + fmt.Sprintf("%d", testLogicBudget-3-5) + ` // base64_decode cost = 5 (64 bytes -> 1 + 64/16) == ` testAccepts(t, source, fidoVersion) @@ -5279,7 +5298,7 @@ byte "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567" base64_decode URLEncoding pop global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-3-5) + ` // base64_decode cost = 5 (60 bytes -> 1 + ceil(60/16)) +int ` + fmt.Sprintf("%d", testLogicBudget-3-5) + ` // base64_decode cost = 5 (60 bytes -> 1 + ceil(60/16)) == ` testAccepts(t, source, fidoVersion) @@ -5289,7 +5308,7 @@ byte "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_AA==" base64_decode URLEncoding pop global OpcodeBudget -int ` + fmt.Sprintf("%d", 20_000-3-6) + ` // base64_decode cost = 6 (68 bytes -> 1 + ceil(68/16)) +int ` + fmt.Sprintf("%d", testLogicBudget-3-6) + ` // base64_decode cost = 6 (68 bytes -> 1 + ceil(68/16)) == ` testAccepts(t, source, fidoVersion) @@ -5815,6 +5834,40 @@ switch done1 done2; done1: ; done2: ; `, 8) } +// TestShortSwitch ensures a clean error, in Check and Eval, when a switch ends early +func TestShortSwitch(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + source := ` + int 1 + int 1 + switch label1 label2 + label1: + label2: + ` + ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) + require.NoError(t, err) + + // fine as is + testLogicBytes(t, ops.Program, nil) + + beyond := "switch opcode claims to extend beyond program" + + // bad if a label is gone + testLogicBytes(t, ops.Program[:len(ops.Program)-2], nil, beyond, beyond) + + // chop off all the labels, but keep the label count + testLogicBytes(t, ops.Program[:len(ops.Program)-4], nil, beyond, beyond) + + // chop off before the label count + testLogicBytes(t, ops.Program[:len(ops.Program)-5], nil, + "bare switch opcode at end of program", "bare switch opcode at end of program") + + // chop off half of a label + testLogicBytes(t, ops.Program[:len(ops.Program)-1], nil, beyond, beyond) +} + func TestMatch(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -5967,6 +6020,41 @@ one: int 0; `, 8) } +// TestShortMatch ensures a clean error when a match ends early +func TestShortMatch(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + source := `int 1 + int 40 + int 45 + int 40 + match label1 label2 + label1: + label2: + ` + ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion) + require.NoError(t, err) + + // fine as is + testLogicBytes(t, ops.Program, nil) + + beyond := "match opcode claims to extend beyond program" + + // bad if a label is gone + testLogicBytes(t, ops.Program[:len(ops.Program)-2], nil, beyond, beyond) + + // chop off all the labels, but keep the label count + testLogicBytes(t, ops.Program[:len(ops.Program)-4], nil, beyond, beyond) + + // chop off before the label count + testLogicBytes(t, ops.Program[:len(ops.Program)-5], nil, + "bare match opcode at end of program", "bare match opcode at end of program") + + // chop off half of a label + testLogicBytes(t, ops.Program[:len(ops.Program)-1], nil, beyond, beyond) +} + func TestPushConsts(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index 10bd93cb20..bb5c2179e9 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -23,7 +23,7 @@ import ( "github.com/algorand/go-algorand/protocol" ) -//go:generate stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AcctParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Encoding,JSONRefType,VrfStandard,BlockField -output=fields_string.go +//go:generate stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AcctParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,EcGroup,Base64Encoding,JSONRefType,VrfStandard,BlockField -output=fields_string.go // FieldSpec unifies the various specs for assembly, disassembly, and doc generation. type FieldSpec interface { @@ -687,6 +687,74 @@ var EcdsaCurves = FieldGroup{ ecdsaCurveSpecByName, } +// EcGroup is an enum for `ec_` opcodes +type EcGroup int + +const ( + // BN254g1 is the G1 group of BN254 + BN254g1 EcGroup = iota + // BN254g2 is the G2 group of BN254 + BN254g2 + // BLS12_381g1 specifies the G1 group of BLS 12-381 + BLS12_381g1 + // BLS12_381g2 specifies the G2 group of BLS 12-381 + BLS12_381g2 + invalidEcGroup // compile-time constant for number of fields +) + +var ecGroupNames [invalidEcGroup]string + +type ecGroupSpec struct { + field EcGroup + doc string +} + +func (fs ecGroupSpec) Field() byte { + return byte(fs.field) +} +func (fs ecGroupSpec) Type() StackType { + return StackNone // Will not show, since all are untyped +} +func (fs ecGroupSpec) OpVersion() uint64 { + return pairingVersion +} +func (fs ecGroupSpec) Version() uint64 { + return pairingVersion +} +func (fs ecGroupSpec) Note() string { + return fs.doc +} + +var ecGroupSpecs = [...]ecGroupSpec{ + {BN254g1, "G1 of the BN254 curve. Points encoded as 32 byte X following by 32 byte Y"}, + {BN254g2, "G2 of the BN254 curve. Points encoded as 64 byte X following by 64 byte Y"}, + {BLS12_381g1, "G1 of the BLS 12-381 curve. Points encoded as 48 byte X following by 48 byte Y"}, + {BLS12_381g2, "G2 of the BLS 12-381 curve. Points encoded as 96 byte X following by 96 byte Y"}, +} + +func ecGroupSpecByField(c EcGroup) (ecGroupSpec, bool) { + if int(c) >= len(ecGroupSpecs) { + return ecGroupSpec{}, false + } + return ecGroupSpecs[c], true +} + +var ecGroupSpecByName = make(ecGroupNameSpecMap, len(ecGroupNames)) + +type ecGroupNameSpecMap map[string]ecGroupSpec + +func (s ecGroupNameSpecMap) get(name string) (FieldSpec, bool) { + fs, ok := s[name] + return fs, ok +} + +// EcGroups collects details about the constants used to describe EcGroups +var EcGroups = FieldGroup{ + "EC", "Groups", + ecGroupNames[:], + ecGroupSpecByName, +} + // Base64Encoding is an enum for the `base64decode` opcode type Base64Encoding int @@ -1334,6 +1402,13 @@ func init() { ecdsaCurveSpecByName[s.field.String()] = s } + equal(len(ecGroupSpecs), len(ecGroupNames)) + for i, s := range ecGroupSpecs { + equal(int(s.field), i) + ecGroupNames[s.field] = s.field.String() + ecGroupSpecByName[s.field.String()] = s + } + equal(len(base64EncodingSpecs), len(base64EncodingNames)) for i, s := range base64EncodingSpecs { equal(int(s.field), i) diff --git a/data/transactions/logic/fields_string.go b/data/transactions/logic/fields_string.go index 5ff79cf396..7d3c2b42f2 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AcctParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,Base64Encoding,JSONRefType,VrfStandard,BlockField -output=fields_string.go"; DO NOT EDIT. +// Code generated by "stringer -type=TxnField,GlobalField,AssetParamsField,AppParamsField,AcctParamsField,AssetHoldingField,OnCompletionConstType,EcdsaCurve,EcGroup,Base64Encoding,JSONRefType,VrfStandard,BlockField -output=fields_string.go"; DO NOT EDIT. package logic @@ -267,6 +267,27 @@ func (i EcdsaCurve) String() string { } return _EcdsaCurve_name[_EcdsaCurve_index[i]:_EcdsaCurve_index[i+1]] } +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BN254g1-0] + _ = x[BN254g2-1] + _ = x[BLS12_381g1-2] + _ = x[BLS12_381g2-3] + _ = x[invalidEcGroup-4] +} + +const _EcGroup_name = "BN254g1BN254g2BLS12_381g1BLS12_381g2invalidEcGroup" + +var _EcGroup_index = [...]uint8{0, 7, 14, 25, 36, 50} + +func (i EcGroup) String() string { + if i < 0 || i >= EcGroup(len(_EcGroup_index)-1) { + return "EcGroup(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _EcGroup_name[_EcGroup_index[i]:_EcGroup_index[i+1]] +} func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. diff --git a/data/transactions/logic/langspec_v1.json b/data/transactions/logic/langspec_v1.json new file mode 100644 index 0000000000..e88de1429d --- /dev/null +++ b/data/transactions/logic/langspec_v1.json @@ -0,0 +1,1136 @@ +{ + "Version": 1, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "7", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "26", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "9", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v10.json b/data/transactions/logic/langspec_v10.json new file mode 100644 index 0000000000..de938ffb72 --- /dev/null +++ b/data/transactions/logic/langspec_v10.json @@ -0,0 +1,4789 @@ +{ + "Version": 10, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 5, + "Name": "ecdsa_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=1700; Secp256r1=2500", + "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 6, + "Name": "ecdsa_pk_decompress", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=650; Secp256r1=2400", + "Doc": "decompress pubkey A into components X, Y", + "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 7, + "Name": "ecdsa_pk_recover", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "2000", + "Doc": "for (data A, recovery id B, signature C, D) recover a public key", + "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress", + "CurrentApplicationAddress", + "GroupID", + "OpcodeBudget", + "CallerApplicationID", + "CallerApplicationAddress", + "AssetCreateMinBalance", + "AssetOptInMinBalance" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address", + "[32]byte", + "uint64", + "uint64", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 62, + "Name": "loads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 63, + "Name": "stores", + "Args": [ + "uint64", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "store B to the Ath scratch space", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 69, + "Name": "bury", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "replace the Nth value from the top of the stack with A. bury 0 fails.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 70, + "Name": "popn", + "Size": 2, + "DocCost": "1", + "Doc": "remove N values from the top of the stack", + "ImmediateNote": [ + { + "Comment": "stack depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 71, + "Name": "dupn", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "duplicate A, N times", + "ImmediateNote": [ + { + "Comment": "copy count", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 78, + "Name": "cover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 79, + "Name": "uncover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 87, + "Name": "extract", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 88, + "Name": "extract3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 89, + "Name": "extract_uint16", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 90, + "Name": "extract_uint32", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 91, + "Name": "extract_uint64", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 92, + "Name": "replace2", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 93, + "Name": "replace3", + "Args": [ + "[]byte", + "uint64", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)\n`replace3` can be called using `replace` with no immediates.", + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 94, + "Name": "base64_decode", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "URLEncoding", + "StdEncoding" + ], + "ArgEnumTypes": [ + "any", + "any" + ], + "DocCost": "1 + 1 per 16 bytes of A", + "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", + "ImmediateNote": [ + { + "Comment": "encoding index", + "Encoding": "uint8", + "Name": "E", + "Reference": "base64" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 95, + "Name": "json_ref", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "JSONString", + "JSONUint64", + "JSONObject" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64", + "[]byte" + ], + "DocCost": "25 + 2 per 7 bytes of A", + "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", + "ImmediateNote": [ + { + "Comment": "return type index", + "Encoding": "uint8", + "Name": "R", + "Reference": "json_ref" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback", + "AssetCreator" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 114, + "Name": "app_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AppApprovalProgram", + "AppClearStateProgram", + "AppGlobalNumUint", + "AppGlobalNumByteSlice", + "AppLocalNumUint", + "AppLocalNumByteSlice", + "AppExtraProgramPages", + "AppCreator", + "AppAddress" + ], + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from app A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 115, + "Name": "acct_params_get", + "Args": [ + "any" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AcctBalance", + "AcctMinBalance", + "AcctAuthAddr", + "AcctTotalNumUint", + "AcctTotalNumByteSlice", + "AcctTotalExtraAppPages", + "AcctTotalAppsCreated", + "AcctTotalAppsOptedIn", + "AcctTotalAssetsCreated", + "AcctTotalAssets", + "AcctTotalBoxes", + "AcctTotalBoxBytes" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", + "ImmediateNote": [ + { + "Comment": "account params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "acct_params" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 130, + "Name": "pushbytess", + "Size": 0, + "DocCost": "1", + "Doc": "push sequences of immediate byte arrays to stack (first byte array being deepest)", + "DocExtra": "pushbytess args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a list of byte constants", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 131, + "Name": "pushints", + "Size": 0, + "DocCost": "1", + "Doc": "push sequence of immediate uints to stack in the order they appear (first uint being deepest)", + "DocExtra": "pushints args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a list of int constants", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 132, + "Name": "ed25519verify_bare", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 138, + "Name": "proto", + "Size": 3, + "DocCost": "1", + "Doc": "Prepare top call frame for a retsub that will assume A args and R return values.", + "DocExtra": "Fails unless the last instruction executed was a `callsub`.", + "ImmediateNote": [ + { + "Comment": "number of arguments", + "Encoding": "uint8", + "Name": "A" + }, + { + "Comment": "number of return values", + "Encoding": "uint8", + "Name": "R" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 139, + "Name": "frame_dig", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth (signed) value from the frame pointer.", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 140, + "Name": "frame_bury", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "replace the Nth (signed) value from the frame pointer in the stack with A", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 141, + "Name": "switch", + "Args": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 142, + "Name": "match", + "Size": 0, + "DocCost": "1", + "Doc": "given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", + "DocExtra": "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 150, + "Name": "bsqrt", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "40", + "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", + "IntroducedVersion": 6, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 151, + "Name": "divw", + "Args": [ + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A,B / C. Fail if C == 0 or if result overflows.", + "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", + "IntroducedVersion": 6, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 152, + "Name": "sha3_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "SHA3_256 hash of value A, yields [32]byte", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 176, + "Name": "log", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write A to log state of the current application", + "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 177, + "Name": "itxn_begin", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in a new transaction group", + "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 178, + "Name": "itxn_field", + "Args": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "Note", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "Accounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "Applications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "StateProofPK", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "[]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "uint64", + "[]byte", + "address", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "set field F of the current inner transaction to A", + "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 179, + "Name": "itxn_submit", + "Size": 1, + "DocCost": "1", + "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", + "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 180, + "Name": "itxn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 181, + "Name": "itxna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 182, + "Name": "itxn_next", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in the same transaction group", + "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 183, + "Name": "gitxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 184, + "Name": "gitxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 185, + "Name": "box_create", + "Args": [ + "boxName", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", + "DocExtra": "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 186, + "Name": "box_extract", + "Args": [ + "boxName", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 187, + "Name": "box_replace", + "Args": [ + "boxName", + "uint64", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 188, + "Name": "box_del", + "Args": [ + "boxName" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete box named A if it exists. Return 1 if A existed, 0 otherwise", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 189, + "Name": "box_len", + "Args": [ + "boxName" + ], + "Returns": [ + "uint64", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 190, + "Name": "box_get", + "Args": [ + "boxName" + ], + "Returns": [ + "[]byte", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", + "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 191, + "Name": "box_put", + "Args": [ + "boxName", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", + "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 192, + "Name": "txnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F of the current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 193, + "Name": "gtxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 194, + "Name": "gtxnsas", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Bth value of the array field F from the Ath transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 195, + "Name": "args", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath LogicSig argument", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 196, + "Name": "gloadss", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth scratch space value of the Ath transaction in the current group", + "IntroducedVersion": 6, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 197, + "Name": "itxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ath value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 198, + "Name": "gitxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 208, + "Name": "vrf_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "VrfAlgorand" + ], + "DocCost": "5700", + "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", + "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", + "ImmediateNote": [ + { + "Comment": " parameters index", + "Encoding": "uint8", + "Name": "S", + "Reference": "vrf_verify" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 209, + "Name": "block", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "BlkSeed", + "BlkTimestamp" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", + "ImmediateNote": [ + { + "Comment": " block field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "block" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 224, + "Name": "ec_add", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "BN254g1=125; BN254g2=170; BLS12_381g1=205; BLS12_381g2=290", + "Doc": "for curve points A and B, return the curve point A + B", + "DocExtra": "A and B are curve points in affine representation: field element X concatenated with field element Y. Field element `Z` is encoded as follows.\nFor the base field elements (Fp), `Z` is encoded as a big-endian number and must be lower than the field modulus.\nFor the quadratic field extension (Fp2), `Z` is encoded as the concatenation of the individual encoding of the coefficients. For an Fp2 element of the form `Z = Z0 + Z1 i`, where `i` is a formal quadratic non-residue, the encoding of Z is the concatenation of the encoding of `Z0` and `Z1` in this order. (`Z0` and `Z1` must be less than the field modulus).\n\nThe point at infinity is encoded as `(X,Y) = (0,0)`.\nGroups G1 and G2 are denoted additively.\n\nFails if A or B is not in G.\nA and/or B are allowed to be the point at infinity.\nDoes _not_ check if A and B are in the main prime-order subgroup.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 225, + "Name": "ec_scalar_mul", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "BN254g1=1810; BN254g2=3430; BLS12_381g1=2950; BLS12_381g2=6530", + "Doc": "for curve point A and scalar B, return the curve point BA, the point A multiplied by the scalar B.", + "DocExtra": "A is a curve point encoded and checked as described in `ec_add`. Scalar B is interpreted as a big-endian unsigned integer. Fails if B exceeds 32 bytes.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 226, + "Name": "ec_pairing_check", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "DocCost": "BN254g1=8000 + 7400 per 64 bytes of B; BN254g2=8000 + 7400 per 128 bytes of B; BLS12_381g1=13000 + 10000 per 96 bytes of B; BLS12_381g2=13000 + 10000 per 192 bytes of B", + "Doc": "1 if the product of the pairing of each point in A with its respective point in B is equal to the identity element of the target group Gt, else 0", + "DocExtra": "A and B are concatenated points, encoded and checked as described in `ec_add`. A contains points of the group G, B contains points of the associated group (G2 if G is G1, and vice versa). Fails if A and B have a different number of points, or if any point is not in its described group or outside the main prime-order subgroup - a stronger condition than other opcodes. AVM values are limited to 4096 bytes, so `ec_pairing_check` is limited by the size of the points in the groups being operated upon.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 227, + "Name": "ec_multi_scalar_mul", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "BN254g1=3600 + 90 per 32 bytes of B; BN254g2=7200 + 270 per 32 bytes of B; BLS12_381g1=6500 + 95 per 32 bytes of B; BLS12_381g2=14850 + 485 per 32 bytes of B", + "Doc": "for curve points A and scalars B, return curve point B0A0 + B1A1 + B2A2 + ... + BnAn", + "DocExtra": "A is a list of concatenated points, encoded and checked as described in `ec_add`. B is a list of concatenated scalars which, unlike ec_scalar_mul, must all be exactly 32 bytes long.\nThe name `ec_multi_scalar_mul` was chosen to reflect common usage, but a more consistent name would be `ec_multi_scalar_mul`. AVM values are limited to 4096 bytes, so `ec_multi_scalar_mul` is limited by the size of the points in the group being operated upon.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 228, + "Name": "ec_subgroup_check", + "Args": [ + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "DocCost": "BN254g1=20; BN254g2=3100; BLS12_381g1=1850; BLS12_381g2=2340", + "Doc": "1 if A is in the main prime-order subgroup of G (including the point at infinity) else 0. Program fails if A is not in G at all.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 229, + "Name": "ec_map_to", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "BN254g1=630; BN254g2=3300; BLS12_381g1=1950; BLS12_381g2=8150", + "Doc": "maps field element A to group G", + "DocExtra": "BN254 points are mapped by the SVDW map. BLS12-381 points are mapped by the SSWU map.\nG1 element inputs are base field elements and G2 element inputs are quadratic field elements, with nearly the same encoding rules (for field elements) as defined in `ec_add`. There is one difference of encoding rule: G1 element inputs do not need to be 0-padded if they fit in less than 32 bytes for BN254 and less than 48 bytes for BLS12-381. (As usual, the empty byte array represents 0.) G2 elements inputs need to be always have the required size.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "G", + "Reference": "EC" + } + ], + "IntroducedVersion": 10, + "Groups": [ + "Arithmetic" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v2.json b/data/transactions/logic/langspec_v2.json new file mode 100644 index 0000000000..b0319cf998 --- /dev/null +++ b/data/transactions/logic/langspec_v2.json @@ -0,0 +1,1744 @@ +{ + "Version": 2, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts" + ], + "ArgEnumTypes": [ + "[]byte", + "address" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts" + ], + "ArgEnumTypes": [ + "[]byte", + "address" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "uint64", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "uint64", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "uint64", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v3.json b/data/transactions/logic/langspec_v3.json new file mode 100644 index 0000000000..e3fe6e4938 --- /dev/null +++ b/data/transactions/logic/langspec_v3.json @@ -0,0 +1,2183 @@ +{ + "Version": 3, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "uint64", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "uint64", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "uint64", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v4.json b/data/transactions/logic/langspec_v4.json new file mode 100644 index 0000000000..9ffb5f7419 --- /dev/null +++ b/data/transactions/logic/langspec_v4.json @@ -0,0 +1,2730 @@ +{ + "Version": 4, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v5.json b/data/transactions/logic/langspec_v5.json new file mode 100644 index 0000000000..9c336c7d86 --- /dev/null +++ b/data/transactions/logic/langspec_v5.json @@ -0,0 +1,3590 @@ +{ + "Version": 5, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 5, + "Name": "ecdsa_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "Secp256k1=1700", + "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 6, + "Name": "ecdsa_pk_decompress", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "Secp256k1=650", + "Doc": "decompress pubkey A into components X, Y", + "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 7, + "Name": "ecdsa_pk_recover", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "2000", + "Doc": "for (data A, recovery id B, signature C, D) recover a public key", + "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress", + "CurrentApplicationAddress", + "GroupID" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address", + "[32]byte" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 62, + "Name": "loads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 63, + "Name": "stores", + "Args": [ + "uint64", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "store B to the Ath scratch space", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 78, + "Name": "cover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 79, + "Name": "uncover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 87, + "Name": "extract", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 88, + "Name": "extract3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 89, + "Name": "extract_uint16", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 90, + "Name": "extract_uint32", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 91, + "Name": "extract_uint64", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback", + "AssetCreator" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 114, + "Name": "app_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AppApprovalProgram", + "AppClearStateProgram", + "AppGlobalNumUint", + "AppGlobalNumByteSlice", + "AppLocalNumUint", + "AppLocalNumByteSlice", + "AppExtraProgramPages", + "AppCreator", + "AppAddress" + ], + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from app A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 176, + "Name": "log", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write A to log state of the current application", + "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 177, + "Name": "itxn_begin", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in a new transaction group", + "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 178, + "Name": "itxn_field", + "Args": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "Note", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "Accounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "Applications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "[]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "uint64", + "[]byte", + "address", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "set field F of the current inner transaction to A", + "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 179, + "Name": "itxn_submit", + "Size": 1, + "DocCost": "1", + "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", + "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 180, + "Name": "itxn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 181, + "Name": "itxna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 192, + "Name": "txnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F of the current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 193, + "Name": "gtxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 194, + "Name": "gtxnsas", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Bth value of the array field F from the Ath transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 195, + "Name": "args", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath LogicSig argument", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v6.json b/data/transactions/logic/langspec_v6.json new file mode 100644 index 0000000000..613d389191 --- /dev/null +++ b/data/transactions/logic/langspec_v6.json @@ -0,0 +1,3975 @@ +{ + "Version": 6, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 5, + "Name": "ecdsa_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "Secp256k1=1700", + "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 6, + "Name": "ecdsa_pk_decompress", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "Secp256k1=650", + "Doc": "decompress pubkey A into components X, Y", + "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 7, + "Name": "ecdsa_pk_recover", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1" + ], + "DocCost": "2000", + "Doc": "for (data A, recovery id B, signature C, D) recover a public key", + "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress", + "CurrentApplicationAddress", + "GroupID", + "OpcodeBudget", + "CallerApplicationID", + "CallerApplicationAddress" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address", + "[32]byte", + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 62, + "Name": "loads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 63, + "Name": "stores", + "Args": [ + "uint64", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "store B to the Ath scratch space", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 78, + "Name": "cover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 79, + "Name": "uncover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 87, + "Name": "extract", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 88, + "Name": "extract3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 89, + "Name": "extract_uint16", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 90, + "Name": "extract_uint32", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 91, + "Name": "extract_uint64", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback", + "AssetCreator" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 114, + "Name": "app_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AppApprovalProgram", + "AppClearStateProgram", + "AppGlobalNumUint", + "AppGlobalNumByteSlice", + "AppLocalNumUint", + "AppLocalNumByteSlice", + "AppExtraProgramPages", + "AppCreator", + "AppAddress" + ], + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from app A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 115, + "Name": "acct_params_get", + "Args": [ + "any" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AcctBalance", + "AcctMinBalance", + "AcctAuthAddr" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", + "ImmediateNote": [ + { + "Comment": "account params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "acct_params" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 150, + "Name": "bsqrt", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "40", + "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", + "IntroducedVersion": 6, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 151, + "Name": "divw", + "Args": [ + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A,B / C. Fail if C == 0 or if result overflows.", + "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", + "IntroducedVersion": 6, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 176, + "Name": "log", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write A to log state of the current application", + "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 177, + "Name": "itxn_begin", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in a new transaction group", + "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 178, + "Name": "itxn_field", + "Args": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "Note", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "Accounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "Applications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "[]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "uint64", + "[]byte", + "address", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte" + ], + "DocCost": "1", + "Doc": "set field F of the current inner transaction to A", + "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 179, + "Name": "itxn_submit", + "Size": 1, + "DocCost": "1", + "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", + "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 180, + "Name": "itxn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 181, + "Name": "itxna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 182, + "Name": "itxn_next", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in the same transaction group", + "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 183, + "Name": "gitxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 184, + "Name": "gitxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 192, + "Name": "txnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F of the current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 193, + "Name": "gtxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 194, + "Name": "gtxnsas", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte" + ], + "DocCost": "1", + "Doc": "Bth value of the array field F from the Ath transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 195, + "Name": "args", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath LogicSig argument", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 196, + "Name": "gloadss", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth scratch space value of the Ath transaction in the current group", + "IntroducedVersion": 6, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 197, + "Name": "itxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ath value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 198, + "Name": "gitxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + } + ] +} diff --git a/data/transactions/logic/langspec_v7.json b/data/transactions/logic/langspec_v7.json new file mode 100644 index 0000000000..cb3664eb3a --- /dev/null +++ b/data/transactions/logic/langspec_v7.json @@ -0,0 +1,4280 @@ +{ + "Version": 7, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 5, + "Name": "ecdsa_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=1700; Secp256r1=2500", + "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 6, + "Name": "ecdsa_pk_decompress", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=650; Secp256r1=2400", + "Doc": "decompress pubkey A into components X, Y", + "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 7, + "Name": "ecdsa_pk_recover", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "2000", + "Doc": "for (data A, recovery id B, signature C, D) recover a public key", + "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress", + "CurrentApplicationAddress", + "GroupID", + "OpcodeBudget", + "CallerApplicationID", + "CallerApplicationAddress" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address", + "[32]byte", + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 62, + "Name": "loads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 63, + "Name": "stores", + "Args": [ + "uint64", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "store B to the Ath scratch space", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 78, + "Name": "cover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 79, + "Name": "uncover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 87, + "Name": "extract", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 88, + "Name": "extract3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 89, + "Name": "extract_uint16", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 90, + "Name": "extract_uint32", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 91, + "Name": "extract_uint64", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 92, + "Name": "replace2", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 93, + "Name": "replace3", + "Args": [ + "[]byte", + "uint64", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)\n`replace3` can be called using `replace` with no immediates.", + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 94, + "Name": "base64_decode", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "URLEncoding", + "StdEncoding" + ], + "ArgEnumTypes": [ + "any", + "any" + ], + "DocCost": "1 + 1 per 16 bytes of A", + "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", + "ImmediateNote": [ + { + "Comment": "encoding index", + "Encoding": "uint8", + "Name": "E", + "Reference": "base64" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 95, + "Name": "json_ref", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "JSONString", + "JSONUint64", + "JSONObject" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64", + "[]byte" + ], + "DocCost": "25 + 2 per 7 bytes of A", + "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", + "ImmediateNote": [ + { + "Comment": "return type index", + "Encoding": "uint8", + "Name": "R", + "Reference": "json_ref" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback", + "AssetCreator" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 114, + "Name": "app_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AppApprovalProgram", + "AppClearStateProgram", + "AppGlobalNumUint", + "AppGlobalNumByteSlice", + "AppLocalNumUint", + "AppLocalNumByteSlice", + "AppExtraProgramPages", + "AppCreator", + "AppAddress" + ], + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from app A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 115, + "Name": "acct_params_get", + "Args": [ + "any" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AcctBalance", + "AcctMinBalance", + "AcctAuthAddr" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", + "ImmediateNote": [ + { + "Comment": "account params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "acct_params" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 132, + "Name": "ed25519verify_bare", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 150, + "Name": "bsqrt", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "40", + "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", + "IntroducedVersion": 6, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 151, + "Name": "divw", + "Args": [ + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A,B / C. Fail if C == 0 or if result overflows.", + "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", + "IntroducedVersion": 6, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 152, + "Name": "sha3_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "SHA3_256 hash of value A, yields [32]byte", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 176, + "Name": "log", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write A to log state of the current application", + "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 177, + "Name": "itxn_begin", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in a new transaction group", + "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 178, + "Name": "itxn_field", + "Args": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "Note", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "Accounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "Applications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "StateProofPK", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "[]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "uint64", + "[]byte", + "address", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "set field F of the current inner transaction to A", + "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 179, + "Name": "itxn_submit", + "Size": 1, + "DocCost": "1", + "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", + "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 180, + "Name": "itxn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 181, + "Name": "itxna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 182, + "Name": "itxn_next", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in the same transaction group", + "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 183, + "Name": "gitxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 184, + "Name": "gitxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 192, + "Name": "txnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F of the current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 193, + "Name": "gtxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 194, + "Name": "gtxnsas", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Bth value of the array field F from the Ath transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 195, + "Name": "args", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath LogicSig argument", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 196, + "Name": "gloadss", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth scratch space value of the Ath transaction in the current group", + "IntroducedVersion": 6, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 197, + "Name": "itxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ath value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 198, + "Name": "gitxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 208, + "Name": "vrf_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "VrfAlgorand" + ], + "DocCost": "5700", + "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", + "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", + "ImmediateNote": [ + { + "Comment": " parameters index", + "Encoding": "uint8", + "Name": "S", + "Reference": "vrf_verify" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 209, + "Name": "block", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "BlkSeed", + "BlkTimestamp" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", + "ImmediateNote": [ + { + "Comment": " block field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "block" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "State Access" + ] + } + ] +} diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec_v8.json similarity index 96% rename from data/transactions/logic/langspec.json rename to data/transactions/logic/langspec_v8.json index c8fdb8ea3f..1cce84389a 100644 --- a/data/transactions/logic/langspec.json +++ b/data/transactions/logic/langspec_v8.json @@ -1,5 +1,5 @@ { - "EvalMaxVersion": 9, + "Version": 8, "LogicSigVersion": 9, "NamedTypes": [ { @@ -107,6 +107,7 @@ "Opcode": 0, "Name": "err", "Size": 1, + "DocCost": "1", "Doc": "Fail immediately.", "IntroducedVersion": 1, "Groups": [ @@ -123,6 +124,7 @@ "[32]byte" ], "Size": 1, + "DocCost": "35", "Doc": "SHA256 hash of value A, yields [32]byte", "IntroducedVersion": 1, "Groups": [ @@ -139,6 +141,7 @@ "[32]byte" ], "Size": 1, + "DocCost": "130", "Doc": "Keccak256 hash of value A, yields [32]byte", "IntroducedVersion": 1, "Groups": [ @@ -155,6 +158,7 @@ "[32]byte" ], "Size": 1, + "DocCost": "45", "Doc": "SHA512_256 hash of value A, yields [32]byte", "IntroducedVersion": 1, "Groups": [ @@ -173,6 +177,7 @@ "bool" ], "Size": 1, + "DocCost": "1900", "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", "IntroducedVersion": 1, @@ -198,6 +203,7 @@ "Secp256k1", "Secp256r1" ], + "DocCost": "Secp256k1=1700; Secp256r1=2500", "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", "ImmediateNote": [ @@ -228,6 +234,7 @@ "Secp256k1", "Secp256r1" ], + "DocCost": "Secp256k1=650; Secp256r1=2400", "Doc": "decompress pubkey A into components X, Y", "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", "ImmediateNote": [ @@ -261,6 +268,7 @@ "Secp256k1", "Secp256r1" ], + "DocCost": "2000", "Doc": "for (data A, recovery id B, signature C, D) recover a public key", "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", "ImmediateNote": [ @@ -287,6 +295,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A plus B. Fail on overflow.", "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", "IntroducedVersion": 1, @@ -305,6 +314,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A minus B. Fail if B \u003e A.", "IntroducedVersion": 1, "Groups": [ @@ -322,6 +332,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A divided by B (truncated division). Fail if B == 0.", "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", "IntroducedVersion": 1, @@ -340,6 +351,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A times B. Fail on overflow.", "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", "IntroducedVersion": 1, @@ -358,6 +370,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A less than B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -375,6 +388,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A greater than B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -392,6 +406,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A less than or equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -409,6 +424,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A greater than or equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -426,6 +442,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -443,6 +460,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -460,6 +478,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A is equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -477,6 +496,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "A is not equal to B =\u003e {0 or 1}", "IntroducedVersion": 1, "Groups": [ @@ -493,6 +513,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A == 0 yields 1; else 0", "IntroducedVersion": 1, "Groups": [ @@ -509,6 +530,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "yields length of byte value A", "IntroducedVersion": 1, "Groups": [ @@ -525,6 +547,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "converts uint64 A to big-endian byte array, always of length 8", "IntroducedVersion": 1, "Groups": [ @@ -541,6 +564,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", "IntroducedVersion": 1, @@ -559,6 +583,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A modulo B. Fail if B == 0.", "IntroducedVersion": 1, "Groups": [ @@ -576,6 +601,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A bitwise-or B", "IntroducedVersion": 1, "Groups": [ @@ -593,6 +619,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A bitwise-and B", "IntroducedVersion": 1, "Groups": [ @@ -610,6 +637,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A bitwise-xor B", "IntroducedVersion": 1, "Groups": [ @@ -626,6 +654,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "bitwise invert value A", "IntroducedVersion": 1, "Groups": [ @@ -644,6 +673,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", "IntroducedVersion": 1, "Groups": [ @@ -662,6 +692,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", "IntroducedVersion": 2, "Groups": [ @@ -684,6 +715,7 @@ "uint64" ], "Size": 1, + "DocCost": "20", "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", "IntroducedVersion": 4, @@ -695,6 +727,7 @@ "Opcode": 32, "Name": "intcblock", "Size": 0, + "DocCost": "1", "Doc": "prepare block of uint64 constants for use by intc", "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", "ImmediateNote": [ @@ -716,6 +749,7 @@ "uint64" ], "Size": 2, + "DocCost": "1", "Doc": "Ith constant from intcblock", "ImmediateNote": [ { @@ -736,6 +770,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "constant 0 from intcblock", "IntroducedVersion": 1, "Groups": [ @@ -749,6 +784,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "constant 1 from intcblock", "IntroducedVersion": 1, "Groups": [ @@ -762,6 +798,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "constant 2 from intcblock", "IntroducedVersion": 1, "Groups": [ @@ -775,6 +812,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "constant 3 from intcblock", "IntroducedVersion": 1, "Groups": [ @@ -785,6 +823,7 @@ "Opcode": 38, "Name": "bytecblock", "Size": 0, + "DocCost": "1", "Doc": "prepare block of byte-array constants for use by bytec", "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", "ImmediateNote": [ @@ -806,6 +845,7 @@ "[]byte" ], "Size": 2, + "DocCost": "1", "Doc": "Ith constant from bytecblock", "ImmediateNote": [ { @@ -826,6 +866,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "constant 0 from bytecblock", "IntroducedVersion": 1, "Groups": [ @@ -839,6 +880,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "constant 1 from bytecblock", "IntroducedVersion": 1, "Groups": [ @@ -852,6 +894,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "constant 2 from bytecblock", "IntroducedVersion": 1, "Groups": [ @@ -865,6 +908,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "constant 3 from bytecblock", "IntroducedVersion": 1, "Groups": [ @@ -878,6 +922,7 @@ "[]byte" ], "Size": 2, + "DocCost": "1", "Doc": "Nth LogicSig argument", "ImmediateNote": [ { @@ -898,6 +943,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "LogicSig argument 0", "IntroducedVersion": 1, "Groups": [ @@ -911,6 +957,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "LogicSig argument 1", "IntroducedVersion": 1, "Groups": [ @@ -924,6 +971,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "LogicSig argument 2", "IntroducedVersion": 1, "Groups": [ @@ -937,6 +985,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "LogicSig argument 3", "IntroducedVersion": 1, "Groups": [ @@ -1090,6 +1139,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of current transaction", "ImmediateNote": [ { @@ -1126,9 +1176,7 @@ "GroupID", "OpcodeBudget", "CallerApplicationID", - "CallerApplicationAddress", - "AssetCreateMinBalance", - "AssetOptInMinBalance" + "CallerApplicationAddress" ], "ArgEnumTypes": [ "uint64", @@ -1145,10 +1193,9 @@ "[32]byte", "uint64", "uint64", - "address", - "uint64", - "uint64" + "address" ], + "DocCost": "1", "Doc": "global field F", "ImmediateNote": [ { @@ -1310,6 +1357,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of the Tth transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", "ImmediateNote": [ @@ -1337,6 +1385,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", "ImmediateNote": [ { @@ -1357,6 +1406,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "store A to the Ith scratch space", "ImmediateNote": [ { @@ -1395,6 +1445,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", "ImmediateNote": [ { @@ -1439,6 +1490,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", "ImmediateNote": [ { @@ -1613,6 +1665,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of the Ath transaction in the current group", "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", "ImmediateNote": [ @@ -1656,6 +1709,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", "ImmediateNote": [ { @@ -1682,6 +1736,7 @@ "any" ], "Size": 3, + "DocCost": "1", "Doc": "Ith scratch space value of the Tth transaction in the current group", "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", "ImmediateNote": [ @@ -1711,6 +1766,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "Ith scratch space value of the Ath transaction in the current group", "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", "ImmediateNote": [ @@ -1732,6 +1788,7 @@ "uint64" ], "Size": 2, + "DocCost": "1", "Doc": "ID of the asset or application created in the Tth transaction of the current group", "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", "ImmediateNote": [ @@ -1756,6 +1813,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "ID of the asset or application created in the Ath transaction of the current group", "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", "IntroducedVersion": 4, @@ -1773,6 +1831,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", "IntroducedVersion": 5, "Groups": [ @@ -1787,6 +1846,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "store B to the Ath scratch space", "IntroducedVersion": 5, "Groups": [ @@ -1800,6 +1860,7 @@ "uint64" ], "Size": 3, + "DocCost": "1", "Doc": "branch to TARGET if value A is not zero", "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", "ImmediateNote": [ @@ -1821,6 +1882,7 @@ "uint64" ], "Size": 3, + "DocCost": "1", "Doc": "branch to TARGET if value A is zero", "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", "ImmediateNote": [ @@ -1839,6 +1901,7 @@ "Opcode": 66, "Name": "b", "Size": 3, + "DocCost": "1", "Doc": "branch unconditionally to TARGET", "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", "ImmediateNote": [ @@ -1860,6 +1923,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "use A as success value; end", "IntroducedVersion": 2, "Groups": [ @@ -1873,6 +1937,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "immediately fail unless A is a non-zero number", "IntroducedVersion": 3, "Groups": [ @@ -1886,6 +1951,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "replace the Nth value from the top of the stack with A. bury 0 fails.", "ImmediateNote": [ { @@ -1903,6 +1969,7 @@ "Opcode": 70, "Name": "popn", "Size": 2, + "DocCost": "1", "Doc": "remove N values from the top of the stack", "ImmediateNote": [ { @@ -1923,6 +1990,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "duplicate A, N times", "ImmediateNote": [ { @@ -1943,6 +2011,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "discard A", "IntroducedVersion": 1, "Groups": [ @@ -1960,6 +2029,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "duplicate A", "IntroducedVersion": 1, "Groups": [ @@ -1980,6 +2050,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "duplicate A and B", "IntroducedVersion": 2, "Groups": [ @@ -1997,6 +2068,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", "ImmediateNote": [ { @@ -2022,6 +2094,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "swaps A and B on stack", "IntroducedVersion": 3, "Groups": [ @@ -2040,6 +2113,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", "IntroducedVersion": 3, "Groups": [ @@ -2056,6 +2130,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", "ImmediateNote": [ { @@ -2079,6 +2154,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", "ImmediateNote": [ { @@ -2103,6 +2179,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "join A and B", "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", "IntroducedVersion": 2, @@ -2120,6 +2197,7 @@ "[]byte" ], "Size": 3, + "DocCost": "1", "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", "ImmediateNote": [ { @@ -2150,6 +2228,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", "IntroducedVersion": 2, "Groups": [ @@ -2167,6 +2246,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "DocExtra": "see explanation of bit ordering in setbit", "IntroducedVersion": 3, @@ -2186,6 +2266,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", "IntroducedVersion": 3, @@ -2204,6 +2285,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", "IntroducedVersion": 3, "Groups": [ @@ -2222,6 +2304,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", "IntroducedVersion": 3, "Groups": [ @@ -2238,6 +2321,7 @@ "[]byte" ], "Size": 3, + "DocCost": "1", "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", "ImmediateNote": [ { @@ -2268,6 +2352,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", "IntroducedVersion": 5, "Groups": [ @@ -2285,6 +2370,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", "IntroducedVersion": 5, "Groups": [ @@ -2302,6 +2388,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", "IntroducedVersion": 5, "Groups": [ @@ -2319,6 +2406,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", "IntroducedVersion": 5, "Groups": [ @@ -2336,6 +2424,7 @@ "[]byte" ], "Size": 2, + "DocCost": "1", "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", "ImmediateNote": [ { @@ -2361,6 +2450,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)\n`replace3` can be called using `replace` with no immediates.", "IntroducedVersion": 7, "Groups": [ @@ -2385,6 +2475,7 @@ "any", "any" ], + "DocCost": "1 + 1 per 16 bytes of A", "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", "ImmediateNote": [ @@ -2421,6 +2512,7 @@ "uint64", "[]byte" ], + "DocCost": "25 + 2 per 7 bytes of A", "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", "ImmediateNote": [ @@ -2446,8 +2538,9 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", - "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", "IntroducedVersion": 2, "Groups": [ "State Access" @@ -2464,6 +2557,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if account A is opted in to application B, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", "IntroducedVersion": 2, @@ -2482,6 +2576,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "local state of the key B in the current application in account A", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", "IntroducedVersion": 2, @@ -2502,6 +2597,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", "IntroducedVersion": 2, @@ -2519,6 +2615,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "global state of the key A in the current application", "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", "IntroducedVersion": 2, @@ -2538,6 +2635,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", "IntroducedVersion": 2, @@ -2554,6 +2652,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "write C to key B in account A's local state of the current application", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", "IntroducedVersion": 2, @@ -2569,6 +2668,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "write B to key A in the global state of the current application", "IntroducedVersion": 2, "Groups": [ @@ -2583,6 +2683,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "delete key B from account A's local state of the current application", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", "IntroducedVersion": 2, @@ -2597,6 +2698,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "delete key A from the global state of the current application", "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", "IntroducedVersion": 2, @@ -2624,6 +2726,7 @@ "uint64", "bool" ], + "DocCost": "1", "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", "ImmediateNote": [ @@ -2678,6 +2781,7 @@ "address", "address" ], + "DocCost": "1", "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", "ImmediateNote": [ @@ -2726,6 +2830,7 @@ "address", "address" ], + "DocCost": "1", "Doc": "X is field F from app A. Y is 1 if A exists, else 0", "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", "ImmediateNote": [ @@ -2780,6 +2885,7 @@ "uint64", "uint64" ], + "DocCost": "1", "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", "ImmediateNote": [ { @@ -2804,8 +2910,9 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", - "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address). Return: value.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", "IntroducedVersion": 3, "Groups": [ "State Access" @@ -2818,6 +2925,7 @@ "[]byte" ], "Size": 0, + "DocCost": "1", "Doc": "immediate BYTES", "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", "ImmediateNote": [ @@ -2839,6 +2947,7 @@ "uint64" ], "Size": 0, + "DocCost": "1", "Doc": "immediate UINT", "DocExtra": "pushint args are not added to the intcblock during assembly processes", "ImmediateNote": [ @@ -2857,6 +2966,7 @@ "Opcode": 130, "Name": "pushbytess", "Size": 0, + "DocCost": "1", "Doc": "push sequences of immediate byte arrays to stack (first byte array being deepest)", "DocExtra": "pushbytess args are not added to the bytecblock during assembly processes", "ImmediateNote": [ @@ -2875,6 +2985,7 @@ "Opcode": 131, "Name": "pushints", "Size": 0, + "DocCost": "1", "Doc": "push sequence of immediate uints to stack in the order they appear (first uint being deepest)", "DocExtra": "pushints args are not added to the intcblock during assembly processes", "ImmediateNote": [ @@ -2901,6 +3012,7 @@ "bool" ], "Size": 1, + "DocCost": "1900", "Doc": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey =\u003e {0 or 1}", "IntroducedVersion": 7, "Groups": [ @@ -2911,6 +3023,7 @@ "Opcode": 136, "Name": "callsub", "Size": 3, + "DocCost": "1", "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", "ImmediateNote": [ @@ -2929,6 +3042,7 @@ "Opcode": 137, "Name": "retsub", "Size": 1, + "DocCost": "1", "Doc": "pop the top instruction from the call stack and branch to it", "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", "IntroducedVersion": 4, @@ -2940,6 +3054,7 @@ "Opcode": 138, "Name": "proto", "Size": 3, + "DocCost": "1", "Doc": "Prepare top call frame for a retsub that will assume A args and R return values.", "DocExtra": "Fails unless the last instruction executed was a `callsub`.", "ImmediateNote": [ @@ -2966,6 +3081,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "Nth (signed) value from the frame pointer.", "ImmediateNote": [ { @@ -2986,6 +3102,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "replace the Nth (signed) value from the frame pointer in the stack with A", "ImmediateNote": [ { @@ -3006,6 +3123,7 @@ "uint64" ], "Size": 0, + "DocCost": "1", "Doc": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", "ImmediateNote": [ { @@ -3023,6 +3141,7 @@ "Opcode": 142, "Name": "match", "Size": 0, + "DocCost": "1", "Doc": "given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", "DocExtra": "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", "ImmediateNote": [ @@ -3048,6 +3167,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A times 2^B, modulo 2^64", "IntroducedVersion": 4, "Groups": [ @@ -3065,6 +3185,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A divided by 2^B", "IntroducedVersion": 4, "Groups": [ @@ -3081,6 +3202,7 @@ "uint64" ], "Size": 1, + "DocCost": "4", "Doc": "The largest integer I such that I^2 \u003c= A", "IntroducedVersion": 4, "Groups": [ @@ -3097,6 +3219,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", "IntroducedVersion": 4, @@ -3115,6 +3238,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", "IntroducedVersion": 4, "Groups": [ @@ -3133,6 +3257,7 @@ "uint64" ], "Size": 1, + "DocCost": "10", "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", "IntroducedVersion": 4, "Groups": [ @@ -3149,6 +3274,7 @@ "[]byte" ], "Size": 1, + "DocCost": "40", "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", "IntroducedVersion": 6, "Groups": [ @@ -3167,6 +3293,7 @@ "uint64" ], "Size": 1, + "DocCost": "1", "Doc": "A,B / C. Fail if C == 0 or if result overflows.", "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", "IntroducedVersion": 6, @@ -3184,6 +3311,7 @@ "[]byte" ], "Size": 1, + "DocCost": "130", "Doc": "SHA3_256 hash of value A, yields [32]byte", "IntroducedVersion": 7, "Groups": [ @@ -3201,6 +3329,7 @@ "[]byte" ], "Size": 1, + "DocCost": "10", "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3218,6 +3347,7 @@ "bigint" ], "Size": 1, + "DocCost": "10", "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", "IntroducedVersion": 4, "Groups": [ @@ -3235,6 +3365,7 @@ "bigint" ], "Size": 1, + "DocCost": "20", "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "IntroducedVersion": 4, "Groups": [ @@ -3252,6 +3383,7 @@ "[]byte" ], "Size": 1, + "DocCost": "20", "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", "IntroducedVersion": 4, "Groups": [ @@ -3269,6 +3401,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3286,6 +3419,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3303,6 +3437,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3320,6 +3455,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3337,6 +3473,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3354,6 +3491,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", "IntroducedVersion": 4, "Groups": [ @@ -3371,6 +3509,7 @@ "[]byte" ], "Size": 1, + "DocCost": "20", "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", "IntroducedVersion": 4, "Groups": [ @@ -3388,6 +3527,7 @@ "[]byte" ], "Size": 1, + "DocCost": "6", "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, "Groups": [ @@ -3405,6 +3545,7 @@ "[]byte" ], "Size": 1, + "DocCost": "6", "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, "Groups": [ @@ -3422,6 +3563,7 @@ "[]byte" ], "Size": 1, + "DocCost": "6", "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", "IntroducedVersion": 4, "Groups": [ @@ -3438,6 +3580,7 @@ "[]byte" ], "Size": 1, + "DocCost": "4", "Doc": "A with all bits inverted", "IntroducedVersion": 4, "Groups": [ @@ -3454,6 +3597,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "zero filled byte-array of length A", "IntroducedVersion": 4, "Groups": [ @@ -3467,6 +3611,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "write A to log state of the current application", "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", "IntroducedVersion": 5, @@ -3478,6 +3623,7 @@ "Opcode": 177, "Name": "itxn_begin", "Size": 1, + "DocCost": "1", "Doc": "begin preparation of a new inner transaction in a new transaction group", "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", "IntroducedVersion": 5, @@ -3598,6 +3744,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "set field F of the current inner transaction to A", "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", "ImmediateNote": [ @@ -3617,6 +3764,7 @@ "Opcode": 179, "Name": "itxn_submit", "Size": 1, + "DocCost": "1", "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", "IntroducedVersion": 5, @@ -3771,6 +3919,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of the last inner transaction", "ImmediateNote": [ { @@ -3810,6 +3959,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ith value of the array field F of the last inner transaction", "ImmediateNote": [ { @@ -3833,6 +3983,7 @@ "Opcode": 182, "Name": "itxn_next", "Size": 1, + "DocCost": "1", "Doc": "begin preparation of a new inner transaction in the same transaction group", "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", "IntroducedVersion": 6, @@ -3987,6 +4138,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of the Tth transaction in the last inner group submitted", "ImmediateNote": [ { @@ -4031,6 +4183,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", "ImmediateNote": [ { @@ -4066,6 +4219,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", "DocExtra": "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", "IntroducedVersion": 8, @@ -4085,6 +4239,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "IntroducedVersion": 8, "Groups": [ @@ -4100,6 +4255,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", "IntroducedVersion": 8, "Groups": [ @@ -4116,6 +4272,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "delete box named A if it exists. Return 1 if A existed, 0 otherwise", "IntroducedVersion": 8, "Groups": [ @@ -4133,6 +4290,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", "IntroducedVersion": 8, "Groups": [ @@ -4150,6 +4308,7 @@ "bool" ], "Size": 1, + "DocCost": "1", "Doc": "X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", "IntroducedVersion": 8, @@ -4165,6 +4324,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", "IntroducedVersion": 8, @@ -4200,6 +4360,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ath value of the array field F of the current transaction", "ImmediateNote": [ { @@ -4242,6 +4403,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Ath value of the array field F from the Tth transaction in the current group", "ImmediateNote": [ { @@ -4290,6 +4452,7 @@ "[]byte", "[]byte" ], + "DocCost": "1", "Doc": "Bth value of the array field F from the Ath transaction in the current group", "ImmediateNote": [ { @@ -4314,6 +4477,7 @@ "[]byte" ], "Size": 1, + "DocCost": "1", "Doc": "Ath LogicSig argument", "IntroducedVersion": 5, "Groups": [ @@ -4331,6 +4495,7 @@ "any" ], "Size": 1, + "DocCost": "1", "Doc": "Bth scratch space value of the Ath transaction in the current group", "IntroducedVersion": 6, "Groups": [ @@ -4347,6 +4512,7 @@ "any" ], "Size": 2, + "DocCost": "1", "Doc": "Ath value of the array field F of the last inner transaction", "ImmediateNote": [ { @@ -4371,6 +4537,7 @@ "any" ], "Size": 3, + "DocCost": "1", "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", "ImmediateNote": [ { @@ -4406,6 +4573,7 @@ "ArgEnum": [ "VrfAlgorand" ], + "DocCost": "5700", "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", "ImmediateNote": [ @@ -4439,6 +4607,7 @@ "[]byte", "uint64" ], + "DocCost": "1", "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", "ImmediateNote": [ { diff --git a/data/transactions/logic/langspec_v9.json b/data/transactions/logic/langspec_v9.json new file mode 100644 index 0000000000..9c4a41c780 --- /dev/null +++ b/data/transactions/logic/langspec_v9.json @@ -0,0 +1,4626 @@ +{ + "Version": 9, + "LogicSigVersion": 9, + "NamedTypes": [ + { + "Name": "uint64", + "Abbreviation": "i", + "Bound": [ + 0, + 18446744073709551615 + ], + "AVMType": "uint64" + }, + { + "Name": "stateKey", + "Abbreviation": "K", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "none", + "Abbreviation": "x", + "Bound": [ + 0, + 0 + ], + "AVMType": "none" + }, + { + "Name": "method", + "Abbreviation": "M", + "Bound": [ + 4, + 4 + ], + "AVMType": "[]byte" + }, + { + "Name": "boxName", + "Abbreviation": "N", + "Bound": [ + 1, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "bool", + "Abbreviation": "T", + "Bound": [ + 0, + 1 + ], + "AVMType": "uint64" + }, + { + "Name": "bigint", + "Abbreviation": "I", + "Bound": [ + 0, + 64 + ], + "AVMType": "[]byte" + }, + { + "Name": "any", + "Abbreviation": "a", + "Bound": [ + 0, + 0 + ], + "AVMType": "any" + }, + { + "Name": "address", + "Abbreviation": "A", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + }, + { + "Name": "[]byte", + "Abbreviation": "b", + "Bound": [ + 0, + 4096 + ], + "AVMType": "[]byte" + }, + { + "Name": "[32]byte", + "Abbreviation": "H", + "Bound": [ + 32, + 32 + ], + "AVMType": "[]byte" + } + ], + "Ops": [ + { + "Opcode": 0, + "Name": "err", + "Size": 1, + "DocCost": "1", + "Doc": "Fail immediately.", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 1, + "Name": "sha256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "35", + "Doc": "SHA256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 2, + "Name": "keccak256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "Keccak256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 3, + "Name": "sha512_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[32]byte" + ], + "Size": 1, + "DocCost": "45", + "Doc": "SHA512_256 hash of value A, yields [32]byte", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 4, + "Name": "ed25519verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 5, + "Name": "ecdsa_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=1700; Secp256r1=2500", + "Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 6, + "Name": "ecdsa_pk_decompress", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "Secp256k1=650; Secp256r1=2400", + "Doc": "decompress pubkey A into components X, Y", + "DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 7, + "Name": "ecdsa_pk_recover", + "Args": [ + "[]byte", + "uint64", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "Secp256k1", + "Secp256r1" + ], + "DocCost": "2000", + "Doc": "for (data A, recovery id B, signature C, D) recover a public key", + "DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.", + "ImmediateNote": [ + { + "Comment": "curve index", + "Encoding": "uint8", + "Name": "V", + "Reference": "ECDSA" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 8, + "Name": "+", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 9, + "Name": "-", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A minus B. Fail if B \u003e A.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 10, + "Name": "/", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by B (truncated division). Fail if B == 0.", + "DocExtra": "`divmodw` is available to divide the two-element values produced by `mulw` and `addw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 11, + "Name": "*", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B. Fail on overflow.", + "DocExtra": "Overflow is an error condition which halts execution and fails the transaction. Full precision is available from `mulw`.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 12, + "Name": "\u003c", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 13, + "Name": "\u003e", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 14, + "Name": "\u003c=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A less than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 15, + "Name": "\u003e=", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A greater than or equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 16, + "Name": "\u0026\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero and B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 17, + "Name": "||", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not zero or B is not zero =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 18, + "Name": "==", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 19, + "Name": "!=", + "Args": [ + "any", + "any" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A is not equal to B =\u003e {0 or 1}", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 20, + "Name": "!", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A == 0 yields 1; else 0", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 21, + "Name": "len", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "yields length of byte value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 22, + "Name": "itob", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts uint64 A to big-endian byte array, always of length 8", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 23, + "Name": "btoi", + "Args": [ + "[]byte" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "converts big-endian byte array A to uint64. Fails if len(A) \u003e 8. Padded by leading 0s if len(A) \u003c 8.", + "DocExtra": "`btoi` fails if the input is longer than 8 bytes.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 24, + "Name": "%", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A modulo B. Fail if B == 0.", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 25, + "Name": "|", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-or B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 26, + "Name": "\u0026", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-and B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 27, + "Name": "^", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A bitwise-xor B", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 28, + "Name": "~", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "bitwise invert value A", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 29, + "Name": "mulw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times B as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low", + "IntroducedVersion": 1, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 30, + "Name": "addw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A plus B as a 128-bit result. X is the carry-bit, Y is the low-order 64 bits.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 31, + "Name": "divmodw", + "Args": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64", + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "20", + "Doc": "W,X = (A,B / C,D); Y,Z = (A,B modulo C,D)", + "DocExtra": "The notation J,K indicates that two uint64 values J and K are interpreted as a uint128 value, with J as the high uint64 and K the low.", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 32, + "Name": "intcblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of uint64 constants for use by intc", + "DocExtra": "`intcblock` loads following program bytes into an array of integer constants in the evaluator. These integer constants can be referred to by `intc` and `intc_*` which will push the value onto the stack. Subsequent calls to `intcblock` reset and replace the integer constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of int constant values", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 33, + "Name": "intc", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from intcblock", + "ImmediateNote": [ + { + "Comment": "an index in the intcblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 34, + "Name": "intc_0", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 35, + "Name": "intc_1", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 36, + "Name": "intc_2", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 37, + "Name": "intc_3", + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from intcblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 38, + "Name": "bytecblock", + "Size": 0, + "DocCost": "1", + "Doc": "prepare block of byte-array constants for use by bytec", + "DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.", + "ImmediateNote": [ + { + "Comment": "a block of byte constant values", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 39, + "Name": "bytec", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith constant from bytecblock", + "ImmediateNote": [ + { + "Comment": "an index in the bytecblock", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 40, + "Name": "bytec_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 0 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 41, + "Name": "bytec_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 1 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 42, + "Name": "bytec_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 2 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 43, + "Name": "bytec_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "constant 3 from bytecblock", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 44, + "Name": "arg", + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth LogicSig argument", + "ImmediateNote": [ + { + "Comment": "an arg index", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 45, + "Name": "arg_0", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 0", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 46, + "Name": "arg_1", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 1", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 47, + "Name": "arg_2", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 2", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 48, + "Name": "arg_3", + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "LogicSig argument 3", + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 49, + "Name": "txn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 50, + "Name": "global", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "MinTxnFee", + "MinBalance", + "MaxTxnLife", + "ZeroAddress", + "GroupSize", + "LogicSigVersion", + "Round", + "LatestTimestamp", + "CurrentApplicationID", + "CreatorAddress", + "CurrentApplicationAddress", + "GroupID", + "OpcodeBudget", + "CallerApplicationID", + "CallerApplicationAddress" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address", + "[32]byte", + "uint64", + "uint64", + "address" + ], + "DocCost": "1", + "Doc": "global field F", + "ImmediateNote": [ + { + "Comment": "a global field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "global" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 51, + "Name": "gtxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If this transaction is _i_ in the group, `gtxn i field` is equivalent to `txn field`.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 52, + "Name": "load", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value. All scratch spaces are 0 at program start.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 53, + "Name": "store", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "store A to the Ith scratch space", + "ImmediateNote": [ + { + "Comment": "position in scratch space to store to", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 54, + "Name": "txna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the current transaction\n`txna` can be called using `txn` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 55, + "Name": "gtxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the current group\n`gtxna` can be called using `gtxn` with 3 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 56, + "Name": "gtxns", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Ath transaction in the current group", + "DocExtra": "for notes on transaction fields available, see `txn`. If top of stack is _i_, `gtxns field` is equivalent to `gtxn _i_ field`. gtxns exists so that _i_ can be calculated, often based on the index of the current transaction.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 57, + "Name": "gtxnsa", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Ath transaction in the current group\n`gtxnsa` can be called using `gtxns` with 2 immediates.", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 58, + "Name": "gload", + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ith scratch space value of the Tth transaction in the current group", + "DocExtra": "`gload` fails unless the requested transaction is an ApplicationCall and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 59, + "Name": "gloads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ith scratch space value of the Ath transaction in the current group", + "DocExtra": "`gloads` fails unless the requested transaction is an ApplicationCall and A \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "position in scratch space to load from", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 60, + "Name": "gaid", + "Returns": [ + "uint64" + ], + "Size": 2, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Tth transaction of the current group", + "DocExtra": "`gaid` fails unless the requested transaction created an asset or application and T \u003c GroupIndex.", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 61, + "Name": "gaids", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "ID of the asset or application created in the Ath transaction of the current group", + "DocExtra": "`gaids` fails unless the requested transaction created an asset or application and A \u003c GroupIndex.", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 62, + "Name": "loads", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath scratch space value. All scratch spaces are 0 at program start.", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 63, + "Name": "stores", + "Args": [ + "uint64", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "store B to the Ath scratch space", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 64, + "Name": "bnz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is not zero", + "DocExtra": "The `bnz` instruction opcode 0x40 is followed by two immediate data bytes which are a high byte first and low byte second which together form a 16 bit offset which the instruction may branch to. For a bnz instruction at `pc`, if the last element of the stack is not zero then branch to instruction at `pc + 3 + N`, else proceed to next instruction at `pc + 3`. Branch targets must be aligned instructions. (e.g. Branching to the second byte of a 2 byte op will be rejected.) Starting at v4, the offset is treated as a signed 16 bit integer allowing for backward branches and looping. In prior version (v1 to v3), branch offsets are limited to forward branches only, 0-0x7fff.\n\nAt v2 it became allowed to branch to the end of the program exactly after the last instruction: bnz to byte N (with 0-indexing) was illegal for a TEAL program with N bytes before v2, and is legal after it. This change eliminates the need for a last instruction of no-op as a branch target at the end. (Branching beyond the end--in other words, to a byte larger than N--is still illegal and will cause the program to fail.)", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 65, + "Name": "bz", + "Args": [ + "uint64" + ], + "Size": 3, + "DocCost": "1", + "Doc": "branch to TARGET if value A is zero", + "DocExtra": "See `bnz` for details on how branches work. `bz` inverts the behavior of `bnz`.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 66, + "Name": "b", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET", + "DocExtra": "See `bnz` for details on how branches work. `b` always jumps to the offset.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 67, + "Name": "return", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "use A as success value; end", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 68, + "Name": "assert", + "Args": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "immediately fail unless A is a non-zero number", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 69, + "Name": "bury", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "replace the Nth value from the top of the stack with A. bury 0 fails.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 70, + "Name": "popn", + "Size": 2, + "DocCost": "1", + "Doc": "remove N values from the top of the stack", + "ImmediateNote": [ + { + "Comment": "stack depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 71, + "Name": "dupn", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "duplicate A, N times", + "ImmediateNote": [ + { + "Comment": "copy count", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 72, + "Name": "pop", + "Args": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "discard A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 73, + "Name": "dup", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A", + "IntroducedVersion": 1, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 74, + "Name": "dup2", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any", + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "duplicate A and B", + "IntroducedVersion": 2, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 75, + "Name": "dig", + "Args": [ + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth value from the top of the stack. dig 0 is equivalent to dup", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 76, + "Name": "swap", + "Args": [ + "any", + "any" + ], + "Returns": [ + "any", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "swaps A and B on stack", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 77, + "Name": "select", + "Args": [ + "any", + "any", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "selects one of two values based on top-of-stack: B if C != 0, else A", + "IntroducedVersion": 3, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 78, + "Name": "cover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove top of stack, and place it deeper in the stack such that N elements are above it. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 79, + "Name": "uncover", + "Args": [ + "any" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "remove the value at depth N in the stack and shift above items down so the Nth deep value is on top of the stack. Fails if stack depth \u003c= N.", + "ImmediateNote": [ + { + "Comment": "depth", + "Encoding": "uint8", + "Name": "N" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 80, + "Name": "concat", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "join A and B", + "DocExtra": "`concat` fails if the result would be greater than 4096 bytes.", + "IntroducedVersion": 2, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 81, + "Name": "substring", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including E. If E \u003c S, or either is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "end position", + "Encoding": "uint8", + "Name": "E" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 82, + "Name": "substring3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including C. If C \u003c B, or either is larger than the array length, the program fails", + "IntroducedVersion": 2, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 83, + "Name": "getbit", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth bit of (byte-array or integer) A. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "see explanation of bit ordering in setbit", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 84, + "Name": "setbit", + "Args": [ + "any", + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of (byte-array or integer) A, with the Bth bit set to (0 or 1) C. If B is greater than or equal to the bit length of the value (8*byte length), the program fails", + "DocExtra": "When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on the integer 0 yields 8, or 2^3. When A is a byte array, index 0 is the leftmost bit of the leftmost byte. Setting bits 0 through 11 to 1 in a 4-byte-array of 0s yields the byte array 0xfff00000. Setting bit 3 to 1 on the 1-byte-array 0x00 yields the byte array 0x10.", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 85, + "Name": "getbyte", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth byte of A, as an integer. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 86, + "Name": "setbyte", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the Bth byte set to small integer (between 0..255) C. If B is greater than or equal to the array length, the program fails", + "IntroducedVersion": 3, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 87, + "Name": "extract", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 3, + "DocCost": "1", + "Doc": "A range of bytes from A starting at S up to but not including S+L. If L is 0, then extract to the end of the string. If S or S+L is larger than the array length, the program fails", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + }, + { + "Comment": "length", + "Encoding": "uint8", + "Name": "L" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 88, + "Name": "extract3", + "Args": [ + "[]byte", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A range of bytes from A starting at B up to but not including B+C. If B+C is larger than the array length, the program fails\n`extract3` can be called using `extract` with no immediates.", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 89, + "Name": "extract_uint16", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 90, + "Name": "extract_uint32", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 91, + "Name": "extract_uint64", + "Args": [ + "[]byte", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails", + "IntroducedVersion": 5, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 92, + "Name": "replace2", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at S replaced by the bytes of B. Fails if S+len(B) exceeds len(A)\n`replace2` can be called using `replace` with 1 immediate.", + "ImmediateNote": [ + { + "Comment": "start position", + "Encoding": "uint8", + "Name": "S" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 93, + "Name": "replace3", + "Args": [ + "[]byte", + "uint64", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Copy of A with the bytes starting at B replaced by the bytes of C. Fails if B+len(C) exceeds len(A)\n`replace3` can be called using `replace` with no immediates.", + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 94, + "Name": "base64_decode", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 2, + "ArgEnum": [ + "URLEncoding", + "StdEncoding" + ], + "ArgEnumTypes": [ + "any", + "any" + ], + "DocCost": "1 + 1 per 16 bytes of A", + "Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.", + "ImmediateNote": [ + { + "Comment": "encoding index", + "Encoding": "uint8", + "Name": "E", + "Reference": "base64" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 95, + "Name": "json_ref", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "JSONString", + "JSONUint64", + "JSONObject" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64", + "[]byte" + ], + "DocCost": "25 + 2 per 7 bytes of A", + "Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A", + "DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.", + "ImmediateNote": [ + { + "Comment": "return type index", + "Encoding": "uint8", + "Name": "R", + "Reference": "json_ref" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Byte Array Manipulation" + ] + }, + { + "Opcode": 96, + "Name": "balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "balance for account A, in microalgos. The balance is observed after the effects of previous transactions in the group, and after the fee for the current transaction is deducted. Changes caused by inner transactions are observable immediately following `itxn_submit`", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 97, + "Name": "app_opted_in", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if account A is opted in to application B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: 1 if opted in and 0 otherwise.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 98, + "Name": "app_local_get", + "Args": [ + "any", + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "local state of the key B in the current application in account A", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 99, + "Name": "app_local_get_ex", + "Args": [ + "any", + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the local state of application B, key C in account A. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 100, + "Name": "app_global_get", + "Args": [ + "[]byte" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "global state of the key A in the current application", + "DocExtra": "params: state key. Return: value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 101, + "Name": "app_global_get_ex", + "Args": [ + "uint64", + "[]byte" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the global state of application A, key B. Y is 1 if key existed, else 0", + "DocExtra": "params: Txn.ForeignApps offset (or, since v4, an _available_ application id), state key. Return: did_exist flag (top of the stack, 1 if the application and key existed and 0 otherwise), value. The value is zero (of type uint64) if the key does not exist.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 102, + "Name": "app_local_put", + "Args": [ + "any", + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write C to key B in account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key, value.", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 103, + "Name": "app_global_put", + "Args": [ + "[]byte", + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write B to key A in the global state of the current application", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 104, + "Name": "app_local_del", + "Args": [ + "any", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key B from account A's local state of the current application", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), state key.\n\nDeleting a key which is already absent has no effect on the application local state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 105, + "Name": "app_global_del", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete key A from the global state of the current application", + "DocExtra": "params: state key.\n\nDeleting a key which is already absent has no effect on the application global state. (In particular, it does _not_ cause the program to fail.)", + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 112, + "Name": "asset_holding_get", + "Args": [ + "any", + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetBalance", + "AssetFrozen" + ], + "ArgEnumTypes": [ + "uint64", + "bool" + ], + "DocCost": "1", + "Doc": "X is field F from account A's holding of asset B. Y is 1 if A is opted into B, else 0", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ address), asset id (or, since v4, a Txn.ForeignAssets offset). Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset holding field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_holding" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 113, + "Name": "asset_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AssetTotal", + "AssetDecimals", + "AssetDefaultFrozen", + "AssetUnitName", + "AssetName", + "AssetURL", + "AssetMetadataHash", + "AssetManager", + "AssetReserve", + "AssetFreeze", + "AssetClawback", + "AssetCreator" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from asset A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignAssets offset (or, since v4, an _available_ asset id. Return: did_exist flag (1 if the asset existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "asset params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "asset_params" + } + ], + "IntroducedVersion": 2, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 114, + "Name": "app_params_get", + "Args": [ + "uint64" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AppApprovalProgram", + "AppClearStateProgram", + "AppGlobalNumUint", + "AppGlobalNumByteSlice", + "AppLocalNumUint", + "AppLocalNumByteSlice", + "AppExtraProgramPages", + "AppCreator", + "AppAddress" + ], + "ArgEnumTypes": [ + "[]byte", + "[]byte", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "address", + "address" + ], + "DocCost": "1", + "Doc": "X is field F from app A. Y is 1 if A exists, else 0", + "DocExtra": "params: Txn.ForeignApps offset or an _available_ app id. Return: did_exist flag (1 if the application existed and 0 otherwise), value.", + "ImmediateNote": [ + { + "Comment": "app params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "app_params" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 115, + "Name": "acct_params_get", + "Args": [ + "any" + ], + "Returns": [ + "any", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "AcctBalance", + "AcctMinBalance", + "AcctAuthAddr", + "AcctTotalNumUint", + "AcctTotalNumByteSlice", + "AcctTotalExtraAppPages", + "AcctTotalAppsCreated", + "AcctTotalAppsOptedIn", + "AcctTotalAssetsCreated", + "AcctTotalAssets", + "AcctTotalBoxes", + "AcctTotalBoxBytes" + ], + "ArgEnumTypes": [ + "uint64", + "uint64", + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64" + ], + "DocCost": "1", + "Doc": "X is field F from account A. Y is 1 if A owns positive algos, else 0", + "ImmediateNote": [ + { + "Comment": "account params field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "acct_params" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 120, + "Name": "min_balance", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "minimum required balance for account A, in microalgos. Required balance is affected by ASA, App, and Box usage. When creating or opting into an app, the minimum balance grows before the app code runs, therefore the increase is visible there. When deleting or closing out, the minimum balance decreases after the app executes. Changes caused by inner transactions or box usage are observable immediately following the opcode effecting the change.", + "DocExtra": "params: Txn.Accounts offset (or, since v4, an _available_ account address), _available_ application id (or, since v4, a Txn.ForeignApps offset). Return: value.", + "IntroducedVersion": 3, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 128, + "Name": "pushbytes", + "Returns": [ + "[]byte" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate BYTES", + "DocExtra": "pushbytes args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a byte constant", + "Encoding": "varuint length, bytes", + "Name": "BYTES" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 129, + "Name": "pushint", + "Returns": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "immediate UINT", + "DocExtra": "pushint args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "an int constant", + "Encoding": "varuint", + "Name": "UINT" + } + ], + "IntroducedVersion": 3, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 130, + "Name": "pushbytess", + "Size": 0, + "DocCost": "1", + "Doc": "push sequences of immediate byte arrays to stack (first byte array being deepest)", + "DocExtra": "pushbytess args are not added to the bytecblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a list of byte constants", + "Encoding": "varuint count, [varuint length, bytes ...]", + "Name": "BYTES ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 131, + "Name": "pushints", + "Size": 0, + "DocCost": "1", + "Doc": "push sequence of immediate uints to stack in the order they appear (first uint being deepest)", + "DocExtra": "pushints args are not added to the intcblock during assembly processes", + "ImmediateNote": [ + { + "Comment": "a list of int constants", + "Encoding": "varuint count, [varuint ...]", + "Name": "UINT ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 132, + "Name": "ed25519verify_bare", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1900", + "Doc": "for (data A, signature B, pubkey C) verify the signature of the data against the pubkey =\u003e {0 or 1}", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 136, + "Name": "callsub", + "Size": 3, + "DocCost": "1", + "Doc": "branch unconditionally to TARGET, saving the next instruction on the call stack", + "DocExtra": "The call stack is separate from the data stack. Only `callsub`, `retsub`, and `proto` manipulate it.", + "ImmediateNote": [ + { + "Comment": "branch offset", + "Encoding": "int16 (big-endian)", + "Name": "TARGET" + } + ], + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 137, + "Name": "retsub", + "Size": 1, + "DocCost": "1", + "Doc": "pop the top instruction from the call stack and branch to it", + "DocExtra": "If the current frame was prepared by `proto A R`, `retsub` will remove the 'A' arguments from the stack, move the `R` return values down, and pop any stack locations above the relocated return values.", + "IntroducedVersion": 4, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 138, + "Name": "proto", + "Size": 3, + "DocCost": "1", + "Doc": "Prepare top call frame for a retsub that will assume A args and R return values.", + "DocExtra": "Fails unless the last instruction executed was a `callsub`.", + "ImmediateNote": [ + { + "Comment": "number of arguments", + "Encoding": "uint8", + "Name": "A" + }, + { + "Comment": "number of return values", + "Encoding": "uint8", + "Name": "R" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 139, + "Name": "frame_dig", + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Nth (signed) value from the frame pointer.", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 140, + "Name": "frame_bury", + "Args": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "replace the Nth (signed) value from the frame pointer in the stack with A", + "ImmediateNote": [ + { + "Comment": "frame slot", + "Encoding": "int8", + "Name": "I" + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 141, + "Name": "switch", + "Args": [ + "uint64" + ], + "Size": 0, + "DocCost": "1", + "Doc": "branch to the Ath label. Continue at following instruction if index A exceeds the number of labels.", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 142, + "Name": "match", + "Size": 0, + "DocCost": "1", + "Doc": "given match cases from A[1] to A[N], branch to the Ith label where A[I] = B. Continue to the following instruction if no matches are found.", + "DocExtra": "`match` consumes N+1 values from the stack. Let the top stack value be B. The following N values represent an ordered list of match cases/constants (A), where the first value (A[0]) is the deepest in the stack. The immediate arguments are an ordered list of N labels (T). `match` will branch to target T[I], where A[I] = B. If there are no matches then execution continues on to the next instruction.", + "ImmediateNote": [ + { + "Comment": "list of labels", + "Encoding": "varuint count, [int16 (big-endian) ...]", + "Name": "TARGET ..." + } + ], + "IntroducedVersion": 8, + "Groups": [ + "Flow Control" + ] + }, + { + "Opcode": 144, + "Name": "shl", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A times 2^B, modulo 2^64", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 145, + "Name": "shr", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A divided by 2^B", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 146, + "Name": "sqrt", + "Args": [ + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "4", + "Doc": "The largest integer I such that I^2 \u003c= A", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 147, + "Name": "bitlen", + "Args": [ + "any" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "The highest set bit in A. If A is a byte-array, it is interpreted as a big-endian unsigned integer. bitlen of 0 is 0, bitlen of 8 is 4", + "DocExtra": "bitlen interprets arrays as big-endian integers, unlike setbit/getbit", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 148, + "Name": "exp", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A raised to the Bth power. Fail if A == B == 0 and on overflow", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 149, + "Name": "expw", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "uint64", + "uint64" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A raised to the Bth power as a 128-bit result in two uint64s. X is the high 64 bits, Y is the low. Fail if A == B == 0 or if the results exceeds 2^128-1", + "IntroducedVersion": 4, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 150, + "Name": "bsqrt", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "40", + "Doc": "The largest integer I such that I^2 \u003c= A. A and I are interpreted as big-endian unsigned integers", + "IntroducedVersion": 6, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 151, + "Name": "divw", + "Args": [ + "uint64", + "uint64", + "uint64" + ], + "Returns": [ + "uint64" + ], + "Size": 1, + "DocCost": "1", + "Doc": "A,B / C. Fail if C == 0 or if result overflows.", + "DocExtra": "The notation A,B indicates that A and B are interpreted as a uint128 value, with A as the high uint64 and B the low.", + "IntroducedVersion": 6, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 152, + "Name": "sha3_256", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "130", + "Doc": "SHA3_256 hash of value A, yields [32]byte", + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 160, + "Name": "b+", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A plus B. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 161, + "Name": "b-", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "10", + "Doc": "A minus B. A and B are interpreted as big-endian unsigned integers. Fail on underflow.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 162, + "Name": "b/", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bigint" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A divided by B (truncated division). A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 163, + "Name": "b*", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A times B. A and B are interpreted as big-endian unsigned integers.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 164, + "Name": "b\u003c", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 165, + "Name": "b\u003e", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 166, + "Name": "b\u003c=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is less than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 167, + "Name": "b\u003e=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is greater than or equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 168, + "Name": "b==", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "1 if A is equal to B, else 0. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 169, + "Name": "b!=", + "Args": [ + "bigint", + "bigint" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "0 if A is equal to B, else 1. A and B are interpreted as big-endian unsigned integers", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 170, + "Name": "b%", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "20", + "Doc": "A modulo B. A and B are interpreted as big-endian unsigned integers. Fail if B is zero.", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Arithmetic" + ] + }, + { + "Opcode": 171, + "Name": "b|", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-or B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 172, + "Name": "b\u0026", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-and B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 173, + "Name": "b^", + "Args": [ + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "6", + "Doc": "A bitwise-xor B. A and B are zero-left extended to the greater of their lengths", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 174, + "Name": "b~", + "Args": [ + "[]byte" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "4", + "Doc": "A with all bits inverted", + "IntroducedVersion": 4, + "Groups": [ + "Byte Array Logic" + ] + }, + { + "Opcode": 175, + "Name": "bzero", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "zero filled byte-array of length A", + "IntroducedVersion": 4, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 176, + "Name": "log", + "Args": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write A to log state of the current application", + "DocExtra": "`log` fails if called more than MaxLogCalls times in a program, or if the sum of logged bytes exceeds 1024 bytes.", + "IntroducedVersion": 5, + "Groups": [ + "State Access" + ] + }, + { + "Opcode": 177, + "Name": "itxn_begin", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in a new transaction group", + "DocExtra": "`itxn_begin` initializes Sender to the application address; Fee to the minimum allowable, taking into account MinTxnFee and credit from overpaying in earlier transactions; FirstValid/LastValid to the values in the invoking transaction, and all other fields to zero or empty values.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 178, + "Name": "itxn_field", + "Args": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "Note", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "Accounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "Applications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "StateProofPK", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "[]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "uint64", + "[]byte", + "address", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "set field F of the current inner transaction to A", + "DocExtra": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 179, + "Name": "itxn_submit", + "Size": 1, + "DocCost": "1", + "Doc": "execute the current inner transaction group. Fail if executing this group would exceed the inner transaction limit, or if any transaction in the group fails.", + "DocExtra": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.", + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 180, + "Name": "itxn", + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 181, + "Name": "itxna", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "a transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 182, + "Name": "itxn_next", + "Size": 1, + "DocCost": "1", + "Doc": "begin preparation of a new inner transaction in the same transaction group", + "DocExtra": "`itxn_next` initializes the transaction exactly as `itxn_begin` does", + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 183, + "Name": "gitxn", + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "Sender", + "Fee", + "FirstValid", + "FirstValidTime", + "LastValid", + "Note", + "Lease", + "Receiver", + "Amount", + "CloseRemainderTo", + "VotePK", + "SelectionPK", + "VoteFirst", + "VoteLast", + "VoteKeyDilution", + "Type", + "TypeEnum", + "XferAsset", + "AssetAmount", + "AssetSender", + "AssetReceiver", + "AssetCloseTo", + "GroupIndex", + "TxID", + "ApplicationID", + "OnCompletion", + "ApplicationArgs", + "NumAppArgs", + "Accounts", + "NumAccounts", + "ApprovalProgram", + "ClearStateProgram", + "RekeyTo", + "ConfigAsset", + "ConfigAssetTotal", + "ConfigAssetDecimals", + "ConfigAssetDefaultFrozen", + "ConfigAssetUnitName", + "ConfigAssetName", + "ConfigAssetURL", + "ConfigAssetMetadataHash", + "ConfigAssetManager", + "ConfigAssetReserve", + "ConfigAssetFreeze", + "ConfigAssetClawback", + "FreezeAsset", + "FreezeAssetAccount", + "FreezeAssetFrozen", + "Assets", + "NumAssets", + "Applications", + "NumApplications", + "GlobalNumUint", + "GlobalNumByteSlice", + "LocalNumUint", + "LocalNumByteSlice", + "ExtraProgramPages", + "Nonparticipation", + "Logs", + "NumLogs", + "CreatedAssetID", + "CreatedApplicationID", + "LastLog", + "StateProofPK", + "ApprovalProgramPages", + "NumApprovalProgramPages", + "ClearStateProgramPages", + "NumClearStateProgramPages" + ], + "ArgEnumTypes": [ + "address", + "uint64", + "uint64", + "uint64", + "uint64", + "[]byte", + "[32]byte", + "address", + "uint64", + "address", + "[32]byte", + "[32]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "uint64", + "uint64", + "uint64", + "address", + "address", + "address", + "uint64", + "[32]byte", + "uint64", + "uint64", + "[]byte", + "uint64", + "address", + "uint64", + "[]byte", + "[]byte", + "address", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "[]byte", + "[]byte", + "[32]byte", + "address", + "address", + "address", + "address", + "uint64", + "address", + "bool", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "uint64", + "bool", + "[]byte", + "uint64", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte", + "uint64", + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txn" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 184, + "Name": "gitxna", + "Returns": [ + "any" + ], + "Size": 4, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ith value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + }, + { + "Comment": "transaction field array index", + "Encoding": "uint8", + "Name": "I" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 185, + "Name": "box_create", + "Args": [ + "boxName", + "uint64" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "create a box named A, of length B. Fail if A is empty or B exceeds 32,768. Returns 0 if A already existed, else 1", + "DocExtra": "Newly created boxes are filled with 0 bytes. `box_create` will fail if the referenced box already exists with a different size. Otherwise, existing boxes are unchanged by `box_create`.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 186, + "Name": "box_extract", + "Args": [ + "boxName", + "uint64", + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "read C bytes from box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 187, + "Name": "box_replace", + "Args": [ + "boxName", + "uint64", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "write byte-array C into box A, starting at offset B. Fail if A does not exist, or the byte range is outside A's size.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 188, + "Name": "box_del", + "Args": [ + "boxName" + ], + "Returns": [ + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "delete box named A if it exists. Return 1 if A existed, 0 otherwise", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 189, + "Name": "box_len", + "Args": [ + "boxName" + ], + "Returns": [ + "uint64", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the length of box A if A exists, else 0. Y is 1 if A exists, else 0.", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 190, + "Name": "box_get", + "Args": [ + "boxName" + ], + "Returns": [ + "[]byte", + "bool" + ], + "Size": 1, + "DocCost": "1", + "Doc": "X is the contents of box A if A exists, else ''. Y is 1 if A exists, else 0.", + "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 191, + "Name": "box_put", + "Args": [ + "boxName", + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "replaces the contents of box A with byte-array B. Fails if A exists and len(B) != len(box A). Creates A if it does not exist", + "DocExtra": "For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `box_replace`", + "IntroducedVersion": 8, + "Groups": [ + "Box Access" + ] + }, + { + "Opcode": 192, + "Name": "txnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F of the current transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 193, + "Name": "gtxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 194, + "Name": "gtxnsas", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "ApplicationArgs", + "Accounts", + "Assets", + "Applications", + "Logs", + "ApprovalProgramPages", + "ClearStateProgramPages" + ], + "ArgEnumTypes": [ + "[]byte", + "address", + "uint64", + "uint64", + "[]byte", + "[]byte", + "[]byte" + ], + "DocCost": "1", + "Doc": "Bth value of the array field F from the Ath transaction in the current group", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 195, + "Name": "args", + "Args": [ + "uint64" + ], + "Returns": [ + "[]byte" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Ath LogicSig argument", + "IntroducedVersion": 5, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 196, + "Name": "gloadss", + "Args": [ + "uint64", + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 1, + "DocCost": "1", + "Doc": "Bth scratch space value of the Ath transaction in the current group", + "IntroducedVersion": 6, + "Groups": [ + "Loading Values" + ] + }, + { + "Opcode": 197, + "Name": "itxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "DocCost": "1", + "Doc": "Ath value of the array field F of the last inner transaction", + "ImmediateNote": [ + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 198, + "Name": "gitxnas", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 3, + "DocCost": "1", + "Doc": "Ath value of the array field F from the Tth transaction in the last inner group submitted", + "ImmediateNote": [ + { + "Comment": "transaction group index", + "Encoding": "uint8", + "Name": "T" + }, + { + "Comment": "transaction field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "txna" + } + ], + "IntroducedVersion": 6, + "Groups": [ + "Inner Transactions" + ] + }, + { + "Opcode": 208, + "Name": "vrf_verify", + "Args": [ + "[]byte", + "[]byte", + "[]byte" + ], + "Returns": [ + "[]byte", + "bool" + ], + "Size": 2, + "ArgEnum": [ + "VrfAlgorand" + ], + "DocCost": "5700", + "Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.", + "DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).", + "ImmediateNote": [ + { + "Comment": " parameters index", + "Encoding": "uint8", + "Name": "S", + "Reference": "vrf_verify" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "Arithmetic" + ] + }, + { + "Opcode": 209, + "Name": "block", + "Args": [ + "uint64" + ], + "Returns": [ + "any" + ], + "Size": 2, + "ArgEnum": [ + "BlkSeed", + "BlkTimestamp" + ], + "ArgEnumTypes": [ + "[]byte", + "uint64" + ], + "DocCost": "1", + "Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)", + "ImmediateNote": [ + { + "Comment": " block field index", + "Encoding": "uint8", + "Name": "F", + "Reference": "block" + } + ], + "IntroducedVersion": 7, + "Groups": [ + "State Access" + ] + } + ] +} diff --git a/data/transactions/logic/opcodeExplain.go b/data/transactions/logic/opcodeExplain.go index ec95d97d1b..4b4f965a65 100644 --- a/data/transactions/logic/opcodeExplain.go +++ b/data/transactions/logic/opcodeExplain.go @@ -32,6 +32,9 @@ const ( // AppStateDelete stands for deleting an app state. AppStateDelete + + // AppStateRead stands for reading from an app state. + AppStateRead ) // AppStateEnum stands for the enum of app state type, should be one of global/local/box. @@ -165,6 +168,20 @@ func opMatchStackChange(cx *EvalContext) (deletions, additions int) { return } +func opBoxExtractStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // length + prev := last - 1 // start + pprev := prev - 1 // name + + return BoxState, AppStateRead, cx.appID, basics.Address{}, string(cx.Stack[pprev].Bytes) +} + +func opBoxGetStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // name + + return BoxState, AppStateRead, cx.appID, basics.Address{}, string(cx.Stack[last].Bytes) +} + func opBoxCreateStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { last := len(cx.Stack) - 1 // size prev := last - 1 // name @@ -187,9 +204,51 @@ func opBoxDelStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics. } func opBoxPutStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { - last := len(cx.Stack) - 1 // name + last := len(cx.Stack) - 1 // value + prev := last - 1 // name + + return BoxState, AppStateWrite, cx.appID, basics.Address{}, string(cx.Stack[prev].Bytes) +} + +func opAppLocalGetStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // state key + prev := last - 1 // account + + // NOTE: we swallow the error of finding account ref, for eventually it would error in execution time, + // and we don't have to complain here. + var addr basics.Address + addr, _, _, _ = cx.localsReference(cx.Stack[prev], 0) + + return LocalState, AppStateRead, cx.appID, addr, string(cx.Stack[last].Bytes) +} + +func opAppLocalGetExStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // state key + prev := last - 1 // app id + pprev := prev - 1 // account + + // NOTE: we swallow the error of finding account ref, for eventually it would error in execution time, + // and we don't have to complain here. + addr, appID, _, _ := cx.localsReference(cx.Stack[pprev], cx.Stack[prev].Uint) + + return LocalState, AppStateRead, appID, addr, string(cx.Stack[last].Bytes) +} + +func opAppGlobalGetStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // state key + + return GlobalState, AppStateRead, cx.appID, basics.Address{}, string(cx.Stack[last].Bytes) +} + +func opAppGlobalGetExStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { + last := len(cx.Stack) - 1 // state key + prev := last - 1 // app id - return BoxState, AppStateWrite, cx.appID, basics.Address{}, string(cx.Stack[last].Bytes) + // NOTE: we swallow the error of finding application ID, for eventually it would error in execution time, + // and we don't have to complain here. + appID, _ := cx.appReference(cx.Stack[prev].Uint, true) + + return GlobalState, AppStateRead, appID, basics.Address{}, string(cx.Stack[last].Bytes) } func opAppLocalPutStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, basics.AppIndex, basics.Address, string) { @@ -230,18 +289,15 @@ func opAppGlobalDelStateChange(cx *EvalContext) (AppStateEnum, AppStateOpEnum, b return GlobalState, AppStateDelete, cx.appID, basics.Address{}, string(cx.Stack[last].Bytes) } -// AppNewStateQuerying is used only for simulation endpoint exec trace export: +// AppStateQuerying is used for simulation endpoint exec trace export: // it reads *new* app state after opcode that writes to app-state. // Since it is collecting new/updated app state, we don't have to error again here, // and thus we omit the error or non-existence case, just returning empty TealValue. // Otherwise, we find the updated new state value, and wrap up with new TealValue. -func AppNewStateQuerying( +func AppStateQuerying( cx *EvalContext, appState AppStateEnum, stateOp AppStateOpEnum, appID basics.AppIndex, account basics.Address, key string) basics.TealValue { - if stateOp != AppStateWrite { - return basics.TealValue{} - } switch appState { case BoxState: boxBytes, exists, err := cx.Ledger.GetBox(appID, key) @@ -259,7 +315,17 @@ func AppNewStateQuerying( } return globalValue case LocalState: - addr, acctID, err := cx.mutableAccountReference(stackValue{Bytes: account[:]}) + var ( + addr basics.Address + acctID uint64 + err error + ) + switch stateOp { + case AppStateWrite, AppStateDelete: + addr, acctID, err = cx.mutableAccountReference(stackValue{Bytes: account[:]}) + default: + addr, _, acctID, err = cx.localsReference(stackValue{Bytes: account[:]}, uint64(appID)) + } if err != nil { return basics.TealValue{} } diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 295b8ed512..e005d57cf3 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -18,12 +18,12 @@ package logic import ( "fmt" - "sort" "strconv" "strings" "github.com/algorand/go-algorand/data/basics" "golang.org/x/exp/maps" + "golang.org/x/exp/slices" ) // LogicVersion defines default assembler and max eval versions @@ -86,6 +86,19 @@ type linearCost struct { depth int } +func (lc linearCost) check() linearCost { + if lc.baseCost < 1 || lc.chunkCost < 0 || lc.chunkSize < 0 || lc.chunkSize > maxStringSize || lc.depth < 0 { + panic(fmt.Sprintf("bad cost configuration %+v", lc)) + } + if lc.chunkCost > 0 && lc.chunkSize == 0 { + panic(fmt.Sprintf("chunk cost when chunk size is zero %+v", lc)) + } + if lc.chunkCost == 0 && lc.chunkSize > 0 { + panic(fmt.Sprintf("no chunk cost with positive chunk size %+v", lc)) + } + return lc +} + func (lc *linearCost) compute(stack []stackValue) int { cost := lc.baseCost if lc.chunkCost != 0 && lc.chunkSize != 0 { @@ -129,7 +142,7 @@ type OpDetails struct { trusted bool // if `trusted`, don't check stack effects. they are more complicated than simply checking the opcode prototype. } -func (d *OpDetails) docCost(argLen int) string { +func (d *OpDetails) docCost(argLen int, version uint64) string { cost := d.FullCost.docCost(argLen) if cost != "" { return cost @@ -142,13 +155,15 @@ func (d *OpDetails) docCost(argLen int) string { } found = true group := imm.Group + var fieldCostStrings []string for _, name := range group.Names { fs, ok := group.SpecByName(name) - if !ok { + if !ok || fs.Version() > version { continue } - cost += fmt.Sprintf(" %s=%d", name, imm.fieldCosts[fs.Field()]) + fieldCostStrings = append(fieldCostStrings, fmt.Sprintf("%s=%s", name, imm.fieldCosts[fs.Field()].docCost(argLen))) } + cost = strings.Join(fieldCostStrings, "; ") } } return cost @@ -166,7 +181,8 @@ func (d *OpDetails) Cost(program []byte, pc int, stack []stackValue) int { } for i := range d.Immediates { if d.Immediates[i].fieldCosts != nil { - cost += d.Immediates[i].fieldCosts[program[pc+1+i]] + lc := d.Immediates[i].fieldCosts[program[pc+1+i]] + cost += lc.compute(stack) } } return cost @@ -210,13 +226,11 @@ func (d OpDetails) assembler(asm asmFunc) OpDetails { } func costly(cost int) OpDetails { - d := detDefault() - d.FullCost.baseCost = cost - return d + return detDefault().costs(cost) } func (d OpDetails) costs(cost int) OpDetails { - d.FullCost = linearCost{baseCost: cost} + d.FullCost = linearCost{baseCost: cost}.check() return d } @@ -285,20 +299,36 @@ func (d OpDetails) field(name string, group *FieldGroup) OpDetails { } func costByField(immediate string, group *FieldGroup, costs []int) OpDetails { - opd := immediates(immediate).costs(0) + if len(costs) != len(group.Names) { + panic(fmt.Sprintf("While defining costs for %s in group %s: %d costs != %d names", + immediate, group.Name, len(costs), len(group.Names))) + } + fieldCosts := make([]linearCost, len(costs)) + for i, cost := range costs { + fieldCosts[i] = linearCost{baseCost: cost} + } + return costByFieldAndLength(immediate, group, fieldCosts) +} + +func costByFieldAndLength(immediate string, group *FieldGroup, costs []linearCost) OpDetails { + if len(costs) != len(group.Names) { + panic(fmt.Sprintf("While defining costs for %s in group %s: %d costs != %d names", + immediate, group.Name, len(costs), len(group.Names))) + } + opd := immediates(immediate) + opd.FullCost = linearCost{} // zero FullCost is what causes eval to look deeper opd.Immediates[0].Group = group - fieldCosts := make([]int, 256) - copy(fieldCosts, costs) - opd.Immediates[0].fieldCosts = fieldCosts + full := make([]linearCost, 256) // ensure we have 256 entries for easy lookup + for i := range costs { + full[i] = costs[i].check() + } + opd.Immediates[0].fieldCosts = full return opd } func costByLength(initial, perChunk, chunkSize, depth int) OpDetails { - if initial < 1 || perChunk <= 0 || chunkSize < 1 || chunkSize > maxStringSize { - panic("bad cost configuration") - } d := detDefault() - d.FullCost = linearCost{initial, perChunk, chunkSize, depth} + d.FullCost = linearCost{initial, perChunk, chunkSize, depth}.check() return d } @@ -344,7 +374,7 @@ type immediate struct { Group *FieldGroup // If non-nil, always 256 long, so cost can be checked before eval - fieldCosts []int + fieldCosts []linearCost } func imm(name string, kind immKind) immediate { @@ -362,24 +392,24 @@ type Proto struct { Arg typedList // what gets popped from the stack Return typedList // what gets pushed to the stack - // Explain is the pointer to the function used in debugging process during simulation: - // - on default construction, Explain relies on Arg and Return count. + // StackExplain is the pointer to the function used in debugging process during simulation: + // - on default construction, StackExplain relies on Arg and Return count. // - otherwise, we need to explicitly infer from EvalContext, by registering through explain function - Explain debugStackExplain + StackExplain debugStackExplain - // StateExplain is the pointer to the function used for debugging in simulation: + // AppStateExplain is the pointer to the function used for debugging in simulation: // - for an opcode not touching app's local/global/box state, this pointer is nil. // - otherwise, we call this method and check the operation of an opcode on app's state. - StateExplain stateChangeExplain + AppStateExplain stateChangeExplain } func (p Proto) stackExplain(e debugStackExplain) Proto { - p.Explain = e + p.StackExplain = e return p } -func (p Proto) stateExplain(s stateChangeExplain) Proto { - p.StateExplain = s +func (p Proto) appStateExplain(s stateChangeExplain) Proto { + p.AppStateExplain = s return p } @@ -412,9 +442,9 @@ func proto(signature string, effects ...string) Proto { retTypes := parseStackTypes(parts[1]) debugExplainFunc := defaultDebugExplain(len(filterNoneTypes(argTypes)), len(filterNoneTypes(retTypes))) return Proto{ - Arg: typedList{argTypes, argEffect}, - Return: typedList{retTypes, retEffect}, - Explain: debugExplainFunc, + Arg: typedList{argTypes, argEffect}, + Return: typedList{retTypes, retEffect}, + StackExplain: debugExplainFunc, } } @@ -433,6 +463,11 @@ func (spec *OpSpec) AlwaysExits() bool { return len(spec.Return.Types) == 1 && spec.Return.Types[0].AVMType == avmNone } +// DocCost returns the cost of the opcode in human-readable form. +func (spec *OpSpec) DocCost(version uint64) string { + return spec.OpDetails.docCost(len(spec.Arg.Types), version) +} + func (spec *OpSpec) deadens() bool { switch spec.Name { case "b", "callsub", "retsub", "err", "return": @@ -583,18 +618,18 @@ var OpSpecs = []OpSpec{ {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)}, {0x61, "app_opted_in", opAppOptedIn, proto("ii:T"), 2, only(ModeApp)}, {0x61, "app_opted_in", opAppOptedIn, proto("ai:T"), directRefEnabledVersion, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(ModeApp)}, - {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:aT"), 2, only(ModeApp)}, - {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:aT"), directRefEnabledVersion, only(ModeApp)}, - {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(ModeApp)}, - {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:aT"), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("iba:").stateExplain(opAppLocalPutStateChange), 2, only(ModeApp)}, - {0x66, "app_local_put", opAppLocalPut, proto("aba:").stateExplain(opAppLocalPutStateChange), directRefEnabledVersion, only(ModeApp)}, - {0x67, "app_global_put", opAppGlobalPut, proto("ba:").stateExplain(opAppGlobalPutStateChange), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ib:").stateExplain(opAppLocalDelStateChange), 2, only(ModeApp)}, - {0x68, "app_local_del", opAppLocalDel, proto("ab:").stateExplain(opAppLocalDelStateChange), directRefEnabledVersion, only(ModeApp)}, - {0x69, "app_global_del", opAppGlobalDel, proto("b:").stateExplain(opAppGlobalDelStateChange), 2, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("ib:a").appStateExplain(opAppLocalGetStateChange), 2, only(ModeApp)}, + {0x62, "app_local_get", opAppLocalGet, proto("ab:a").appStateExplain(opAppLocalGetStateChange), directRefEnabledVersion, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:aT").appStateExplain(opAppLocalGetExStateChange), 2, only(ModeApp)}, + {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:aT").appStateExplain(opAppLocalGetExStateChange), directRefEnabledVersion, only(ModeApp)}, + {0x64, "app_global_get", opAppGlobalGet, proto("b:a").appStateExplain(opAppGlobalGetStateChange), 2, only(ModeApp)}, + {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:aT").appStateExplain(opAppGlobalGetExStateChange), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("iba:").appStateExplain(opAppLocalPutStateChange), 2, only(ModeApp)}, + {0x66, "app_local_put", opAppLocalPut, proto("aba:").appStateExplain(opAppLocalPutStateChange), directRefEnabledVersion, only(ModeApp)}, + {0x67, "app_global_put", opAppGlobalPut, proto("ba:").appStateExplain(opAppGlobalPutStateChange), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("ib:").appStateExplain(opAppLocalDelStateChange), 2, only(ModeApp)}, + {0x68, "app_local_del", opAppLocalDel, proto("ab:").appStateExplain(opAppLocalDelStateChange), directRefEnabledVersion, only(ModeApp)}, + {0x69, "app_global_del", opAppGlobalDel, proto("b:").appStateExplain(opAppGlobalDelStateChange), 2, only(ModeApp)}, {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:aT"), 2, field("f", &AssetHoldingFields).only(ModeApp)}, {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:aT"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)}, {0x71, "asset_params_get", opAssetParamsGet, proto("i:aT"), 2, field("f", &AssetParamsFields).only(ModeApp)}, @@ -636,10 +671,6 @@ var OpSpecs = []OpSpec{ {0x98, "sha3_256", opSHA3_256, proto("b:b"), unlimitedStorage, costByLength(58, 4, 8)},}, */ - {0x99, "bn256_add", opBn256Add, proto("bb:b"), pairingVersion, costly(70)}, - {0x9a, "bn256_scalar_mul", opBn256ScalarMul, proto("bb:b"), pairingVersion, costly(970)}, - {0x9b, "bn256_pairing", opBn256Pairing, proto("bb:i"), pairingVersion, costly(8700)}, - // Byteslice math. {0xa0, "b+", opBytesPlus, proto("II:b"), 4, costly(10).typed(typeByteMath(maxByteMathSize + 1))}, {0xa1, "b-", opBytesMinus, proto("II:I"), 4, costly(10)}, @@ -670,13 +701,13 @@ var OpSpecs = []OpSpec{ {0xb8, "gitxna", opGitxna, proto(":a"), 6, immediates("t", "f", "i").field("f", &TxnArrayFields).only(ModeApp)}, // Unlimited Global Storage - Boxes - {0xb9, "box_create", opBoxCreate, proto("Ni:T").stateExplain(opBoxCreateStateChange), boxVersion, only(ModeApp)}, - {0xba, "box_extract", opBoxExtract, proto("Nii:b"), boxVersion, only(ModeApp)}, - {0xbb, "box_replace", opBoxReplace, proto("Nib:").stateExplain(opBoxReplaceStateChange), boxVersion, only(ModeApp)}, - {0xbc, "box_del", opBoxDel, proto("N:T").stateExplain(opBoxDelStateChange), boxVersion, only(ModeApp)}, - {0xbd, "box_len", opBoxLen, proto("N:iT"), boxVersion, only(ModeApp)}, - {0xbe, "box_get", opBoxGet, proto("N:bT"), boxVersion, only(ModeApp)}, - {0xbf, "box_put", opBoxPut, proto("Nb:").stateExplain(opBoxPutStateChange), boxVersion, only(ModeApp)}, + {0xb9, "box_create", opBoxCreate, proto("Ni:T").appStateExplain(opBoxCreateStateChange), boxVersion, only(ModeApp)}, + {0xba, "box_extract", opBoxExtract, proto("Nii:b").appStateExplain(opBoxExtractStateChange), boxVersion, only(ModeApp)}, + {0xbb, "box_replace", opBoxReplace, proto("Nib:").appStateExplain(opBoxReplaceStateChange), boxVersion, only(ModeApp)}, + {0xbc, "box_del", opBoxDel, proto("N:T").appStateExplain(opBoxDelStateChange), boxVersion, only(ModeApp)}, + {0xbd, "box_len", opBoxLen, proto("N:iT").appStateExplain(opBoxGetStateChange), boxVersion, only(ModeApp)}, + {0xbe, "box_get", opBoxGet, proto("N:bT").appStateExplain(opBoxGetStateChange), boxVersion, only(ModeApp)}, + {0xbf, "box_put", opBoxPut, proto("Nb:").appStateExplain(opBoxPutStateChange), boxVersion, only(ModeApp)}, // Dynamic indexing {0xc0, "txnas", opTxnas, proto("i:a"), 5, field("f", &TxnArrayFields)}, @@ -690,13 +721,72 @@ var OpSpecs = []OpSpec{ // randomness support {0xd0, "vrf_verify", opVrfVerify, proto("bbb:bT"), randomnessVersion, field("s", &VrfStandards).costs(5700)}, {0xd1, "block", opBlock, proto("i:a"), randomnessVersion, field("f", &BlockFields)}, -} -type sortByOpcode []OpSpec - -func (a sortByOpcode) Len() int { return len(a) } -func (a sortByOpcode) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a sortByOpcode) Less(i, j int) bool { return a[i].Opcode < a[j].Opcode } + {0xe0, "ec_add", opEcAdd, proto("bb:b"), pairingVersion, + costByField("g", &EcGroups, []int{ + BN254g1: 125, BN254g2: 170, + BLS12_381g1: 205, BLS12_381g2: 290})}, + + {0xe1, "ec_scalar_mul", opEcScalarMul, proto("bb:b"), pairingVersion, + costByField("g", &EcGroups, []int{ + BN254g1: 1810, BN254g2: 3430, + BLS12_381g1: 2950, BLS12_381g2: 6530})}, + + {0xe2, "ec_pairing_check", opEcPairingCheck, proto("bb:T"), pairingVersion, + costByFieldAndLength("g", &EcGroups, []linearCost{ + BN254g1: { + baseCost: 8000, + chunkCost: 7_400, + chunkSize: bn254g1Size, + }, + BN254g2: { + baseCost: 8000, + chunkCost: 7_400, + chunkSize: bn254g2Size, + }, + BLS12_381g1: { + baseCost: 13_000, + chunkCost: 10_000, + chunkSize: bls12381g1Size, + }, + BLS12_381g2: { + baseCost: 13_000, + chunkCost: 10_000, + chunkSize: bls12381g2Size, + }})}, + + {0xe3, "ec_multi_scalar_mul", opEcMultiScalarMul, proto("bb:b"), pairingVersion, + costByFieldAndLength("g", &EcGroups, []linearCost{ + BN254g1: { + baseCost: 3_600, + chunkCost: 90, + chunkSize: scalarSize, + }, + BN254g2: { + baseCost: 7_200, + chunkCost: 270, + chunkSize: scalarSize, + }, + BLS12_381g1: { + baseCost: 6_500, + chunkCost: 95, + chunkSize: scalarSize, + }, + BLS12_381g2: { + baseCost: 14_850, + chunkCost: 485, + chunkSize: scalarSize, + }})}, + + {0xe4, "ec_subgroup_check", opEcSubgroupCheck, proto("b:T"), pairingVersion, + costByField("g", &EcGroups, []int{ + BN254g1: 20, BN254g2: 3_100, // g1 subgroup is nearly a no-op + BLS12_381g1: 1_850, BLS12_381g2: 2_340})}, + {0xe5, "ec_map_to", opEcMapTo, proto("b:b"), pairingVersion, + costByField("g", &EcGroups, []int{ + BN254g1: 630, BN254g2: 3_300, + BLS12_381g1: 1_950, BLS12_381g2: 8_150})}, +} // OpcodesByVersion returns list of opcodes available in a specific version of TEAL // by copying v1 opcodes to v2, and then on to v3 to create a full list @@ -737,7 +827,9 @@ func OpcodesByVersion(version uint64) []OpSpec { } } result := maps.Values(subv) - sort.Sort(sortByOpcode(result)) + slices.SortFunc(result, func(a, b OpSpec) bool { + return a.Opcode < b.Opcode + }) return result } diff --git a/data/transactions/logic/pairing.go b/data/transactions/logic/pairing.go index 2988c35ec4..fa47e061d9 100644 --- a/data/transactions/logic/pairing.go +++ b/data/transactions/logic/pairing.go @@ -17,99 +17,887 @@ package logic import ( - "errors" + "fmt" "math/big" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark-crypto/ecc/bn254/fp" + bn254fp "github.com/consensys/gnark-crypto/ecc/bn254/fp" + bn254fr "github.com/consensys/gnark-crypto/ecc/bn254/fr" + + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + bls12381fp "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + bls12381fr "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" ) -func bytesToBN254Field(b []byte) (ret fp.Element) { - ret.SetBytes(b) - return -} +type sError string -func bytesToBN254G1(b []byte) (ret bn254.G1Affine) { - ret.X = bytesToBN254Field(b[:32]) - ret.Y = bytesToBN254Field(b[32:64]) - return -} +func (s sError) Error() string { return string(s) } + +const ( + errNotOnCurve = sError("point not on curve") + errWrongSubgroup = sError("wrong subgroup") + errEmptyInput = sError("empty input") +) -func bytesToBN254G1s(b []byte) (ret []bn254.G1Affine) { - for i := 0; i < len(b)/64; i++ { - ret = append(ret, bytesToBN254G1(b[(i*64):(i*64+64)])) +// Input: Two byte slices at top of stack, each an uncompressed point +// Output: Single byte slice on top of stack which is the uncompressed sum of inputs +func opEcAdd(cx *EvalContext) error { + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_add group %s", group) } - return -} -func bytesToBN254G2(b []byte) (ret bn254.G2Affine) { - ret.X.A0 = bytesToBN254Field(b[:32]) - ret.X.A1 = bytesToBN254Field(b[32:64]) - ret.Y.A0 = bytesToBN254Field(b[64:96]) - ret.Y.A1 = bytesToBN254Field(b[96:128]) - return -} + last := len(cx.Stack) - 1 + prev := last - 1 + a := cx.Stack[prev].Bytes + b := cx.Stack[last].Bytes -func bytesToBN254G2s(b []byte) (ret []bn254.G2Affine) { - for i := 0; i < len(b)/128; i++ { - ret = append(ret, bytesToBN254G2(b[(i*128):(i*128+128)])) + var res []byte + var err error + switch fs.field { + case BN254g1: + res, err = bn254G1Add(a, b) + case BN254g2: + res, err = bn254G2Add(a, b) + case BLS12_381g1: + res, err = bls12381G1Add(a, b) + case BLS12_381g2: + res, err = bls12381G2Add(a, b) + default: + err = fmt.Errorf("invalid ec_add group %s", group) } - return + cx.Stack[prev].Bytes = res + cx.Stack = cx.Stack[:last] + return err } -func bN254G1ToBytes(g1 *bn254.G1Affine) (ret []byte) { - retX := g1.X.Bytes() - retY := g1.Y.Bytes() - ret = append(retX[:], retY[:]...) - return -} +// Input: ToS is a scalar, encoded as an unsigned big-endian, second to top are +// bytes of an uncompressed point +// Output: Single byte slice on top of stack which contains uncompressed bytes +// for product of scalar and point +func opEcScalarMul(cx *EvalContext) error { + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_scalar_mul group %s", group) + } -func opBn256Add(cx *EvalContext) error { last := len(cx.Stack) - 1 prev := last - 1 aBytes := cx.Stack[prev].Bytes - bBytes := cx.Stack[last].Bytes - if len(aBytes) != 64 || len(bBytes) != 64 { - return errors.New("expect G1 in 64 bytes") - } - a := bytesToBN254G1(aBytes) - b := bytesToBN254G1(bBytes) - res := new(bn254.G1Affine).Add(&a, &b) - resBytes := bN254G1ToBytes(res) + kBytes := cx.Stack[last].Bytes + if len(kBytes) > scalarSize { + return fmt.Errorf("ec_scalar_mul scalar len is %d, exceeds 32", len(kBytes)) + } + k := new(big.Int).SetBytes(kBytes) + + var res []byte + var err error + switch fs.field { + case BN254g1: + res, err = bn254G1ScalarMul(aBytes, k) + case BN254g2: + res, err = bn254G2ScalarMul(aBytes, k) + case BLS12_381g1: + res, err = bls12381G1ScalarMul(aBytes, k) + case BLS12_381g2: + res, err = bls12381G2ScalarMul(aBytes, k) + default: + err = fmt.Errorf("invalid ec_scalar_mul group %s", group) + } + cx.Stack = cx.Stack[:last] - cx.Stack[prev].Bytes = resBytes - return nil + cx.Stack[prev].Bytes = res + return err } -func opBn256ScalarMul(cx *EvalContext) error { +// Input: Two byte slices, The first (deeper) is concatenated uncompressed bytes +// for k points of the curve given as the immediate value. The second (ToS) are +// k points for the "associated" curve. +// Output: bool (uint64=0,1) for whether pairing of inputs was identity +func opEcPairingCheck(cx *EvalContext) error { + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_pairing_check group %s", group) + } + last := len(cx.Stack) - 1 prev := last - 1 - aBytes := cx.Stack[prev].Bytes - if len(aBytes) != 64 { - return errors.New("expect G1 in 64 bytes") + g1Bytes := cx.Stack[prev].Bytes + g2Bytes := cx.Stack[last].Bytes + + var err error + ok = false + switch fs.field { + case BN254g2: + g1Bytes, g2Bytes = g2Bytes, g1Bytes + fallthrough + case BN254g1: + ok, err = bn254PairingCheck(g1Bytes, g2Bytes) + case BLS12_381g2: + g1Bytes, g2Bytes = g2Bytes, g1Bytes + fallthrough + case BLS12_381g1: + ok, err = bls12381PairingCheck(g1Bytes, g2Bytes) + default: + err = fmt.Errorf("invalid ec_pairing_check group %s", group) } - a := bytesToBN254G1(aBytes) - kBytes := cx.Stack[last].Bytes - k := new(big.Int).SetBytes(kBytes[:]) - res := new(bn254.G1Affine).ScalarMultiplication(&a, k) - resBytes := bN254G1ToBytes(res) + cx.Stack = cx.Stack[:last] - cx.Stack[prev].Bytes = resBytes - return nil + cx.Stack[prev] = boolToSV(ok) + return err } -func opBn256Pairing(cx *EvalContext) error { +// Input: Top of stack is slice of k scalars, second to top is slice of k group points as uncompressed bytes +// Output: Single byte slice that contains uncompressed bytes for point equivalent to +// p_1*e_1 + p_2*e_2 + ... + p_k*e_k, where p_i is i'th point from input and e_i is i'th scalar +func opEcMultiScalarMul(cx *EvalContext) error { + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_multi_scalar_mul group %s", group) + } + last := len(cx.Stack) - 1 prev := last - 1 - g1Bytes := cx.Stack[prev].Bytes - g2Bytes := cx.Stack[last].Bytes - g1 := bytesToBN254G1s(g1Bytes) - g2 := bytesToBN254G2s(g2Bytes) + pointBytes := cx.Stack[prev].Bytes + scalarBytes := cx.Stack[last].Bytes + + var res []byte + var err error + switch fs.field { + case BN254g1: + res, err = bn254G1MultiMul(pointBytes, scalarBytes) + case BN254g2: + res, err = bn254G2MultiMul(pointBytes, scalarBytes) + case BLS12_381g1: + res, err = bls12381G1MultiMul(pointBytes, scalarBytes) + case BLS12_381g2: + res, err = bls12381G2MultiMul(pointBytes, scalarBytes) + default: + err = fmt.Errorf("invalid ec_multi_scalar_mul group %s", group) + } + + cx.Stack = cx.Stack[:last] + cx.Stack[prev].Bytes = res + return err +} + +// Input: Single byte slice on top of stack containing uncompressed bytes for a point +// Output: bool (uint64=0,1) for whether the input was in the correct subgroup or not +func opEcSubgroupCheck(cx *EvalContext) error { + last := len(cx.Stack) - 1 + pointBytes := cx.Stack[last].Bytes + + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_pairing_check group %s", group) + } + + var err error + ok = false + switch fs.field { + case BN254g1: + ok, err = bn254G1SubgroupCheck(pointBytes) + case BN254g2: + ok, err = bn254G2SubgroupCheck(pointBytes) + case BLS12_381g1: + ok, err = bls12381G1SubgroupCheck(pointBytes) + case BLS12_381g2: + ok, err = bls12381G2SubgroupCheck(pointBytes) + default: + err = fmt.Errorf("invalid ec_pairing_check group %s", group) + } + + cx.Stack[last] = boolToSV(ok) + return err +} + +// Input: Single byte slice on top of stack representing single field element +// Output: Single byte slice on top of stack which contains uncompressed bytes +// for corresponding point (mapped to by input) +func opEcMapTo(cx *EvalContext) error { + last := len(cx.Stack) - 1 + fpBytes := cx.Stack[last].Bytes + + group := EcGroup(cx.program[cx.pc+1]) + fs, ok := ecGroupSpecByField(group) + if !ok { // no version check yet, both appeared at once + return fmt.Errorf("invalid ec_pairing_check group %s", group) + } + + var res []byte + var err error + switch fs.field { + case BN254g1: + res, err = bn254MapToG1(fpBytes) + case BN254g2: + res, err = bn254MapToG2(fpBytes) + case BLS12_381g1: + res, err = bls12381MapToG1(fpBytes) + case BLS12_381g2: + res, err = bls12381MapToG2(fpBytes) + default: + err = fmt.Errorf("invalid ec_pairing_check group %s", group) + } + cx.Stack[last].Bytes = res + return err +} + +const ( + bls12381fpSize = 48 + bls12381g1Size = 2 * bls12381fpSize + bls12381fp2Size = 2 * bls12381fpSize + bls12381g2Size = 2 * bls12381fp2Size + + bn254fpSize = 32 + bn254g1Size = 2 * bn254fpSize + bn254fp2Size = 2 * bn254fpSize + bn254g2Size = 2 * bn254fp2Size + + scalarSize = 32 +) + +var bls12381Modulus = bls12381fp.Modulus() + +func bytesToBLS12381Field(b []byte) (bls12381fp.Element, error) { + big := new(big.Int).SetBytes(b) + if big.Cmp(bls12381Modulus) >= 0 { + return bls12381fp.Element{}, fmt.Errorf("field element %s larger than modulus %s", big.String(), bls12381Modulus) + } + return *new(bls12381fp.Element).SetBigInt(big), nil +} + +func bytesToBLS12381G1(b []byte) (bls12381.G1Affine, error) { + if len(b) != bls12381g1Size { + return bls12381.G1Affine{}, fmt.Errorf("bad length %d. Expected %d", len(b), bls12381g1Size) + } + var point bls12381.G1Affine + var err error + point.X, err = bytesToBLS12381Field(b[:bls12381fpSize]) + if err != nil { + return bls12381.G1Affine{}, err + } + point.Y, err = bytesToBLS12381Field(b[bls12381fpSize:bls12381g1Size]) + if err != nil { + return bls12381.G1Affine{}, err + } + if !point.IsOnCurve() { + return bls12381.G1Affine{}, errNotOnCurve + } + return point, nil +} + +func bytesToBLS12381G1s(b []byte, checkSubgroup bool) ([]bls12381.G1Affine, error) { + if len(b)%bls12381g1Size != 0 { + return nil, fmt.Errorf("bad length %d. Expected %d multiple", len(b), bls12381g1Size) + } + if len(b) == 0 { + return nil, errEmptyInput + } + points := make([]bls12381.G1Affine, len(b)/bls12381g1Size) + for i := range points { + var err error + points[i], err = bytesToBLS12381G1(b[i*bls12381g1Size : (i+1)*bls12381g1Size]) + if err != nil { + return nil, err + } + if checkSubgroup && !points[i].IsInSubGroup() { + return nil, errWrongSubgroup + } + } + return points, nil +} + +func bytesToBLS12381G2(b []byte) (bls12381.G2Affine, error) { + if len(b) != bls12381g2Size { + return bls12381.G2Affine{}, fmt.Errorf("bad length %d. Expected %d", len(b), bls12381g2Size) + } + var err error + var point bls12381.G2Affine + point.X.A0, err = bytesToBLS12381Field(b[:bls12381fpSize]) + if err != nil { + return bls12381.G2Affine{}, err + } + point.X.A1, err = bytesToBLS12381Field(b[bls12381fpSize : 2*bls12381fpSize]) + if err != nil { + return bls12381.G2Affine{}, err + } + point.Y.A0, err = bytesToBLS12381Field(b[2*bls12381fpSize : 3*bls12381fpSize]) + if err != nil { + return bls12381.G2Affine{}, err + } + point.Y.A1, err = bytesToBLS12381Field(b[3*bls12381fpSize : 4*bls12381fpSize]) + if err != nil { + return bls12381.G2Affine{}, err + } + if !point.IsOnCurve() { + return bls12381.G2Affine{}, errNotOnCurve + } + return point, nil +} + +func bytesToBLS12381G2s(b []byte, checkSubgroup bool) ([]bls12381.G2Affine, error) { + if len(b)%bls12381g2Size != 0 { + return nil, fmt.Errorf("bad length %d. Expected %d multiple", len(b), bls12381g2Size) + } + if len(b) == 0 { + return nil, errEmptyInput + } + points := make([]bls12381.G2Affine, len(b)/bls12381g2Size) + for i := range points { + var err error + points[i], err = bytesToBLS12381G2(b[i*bls12381g2Size : (i+1)*bls12381g2Size]) + if err != nil { + return nil, err + } + if checkSubgroup && !points[i].IsInSubGroup() { + return nil, errWrongSubgroup + } + } + return points, nil +} + +func bls12381G1ToBytes(g1 *bls12381.G1Affine) []byte { + retX := g1.X.Bytes() + retY := g1.Y.Bytes() + pointBytes := make([]byte, bls12381g1Size) + copy(pointBytes, retX[:]) + copy(pointBytes[bls12381fpSize:], retY[:]) + return pointBytes +} + +func bls12381G2ToBytes(g2 *bls12381.G2Affine) []byte { + xFirst := g2.X.A0.Bytes() + xSecond := g2.X.A1.Bytes() + yFirst := g2.Y.A0.Bytes() + ySecond := g2.Y.A1.Bytes() + pointBytes := make([]byte, bls12381g2Size) + copy(pointBytes, xFirst[:]) + copy(pointBytes[bls12381fpSize:], xSecond[:]) + copy(pointBytes[bls12381fp2Size:], yFirst[:]) + copy(pointBytes[bls12381fp2Size+bls12381fpSize:], ySecond[:]) + return pointBytes +} + +func bls12381G1Add(aBytes, bBytes []byte) ([]byte, error) { + a, err := bytesToBLS12381G1(aBytes) + if err != nil { + return nil, err + } + b, err := bytesToBLS12381G1(bBytes) + if err != nil { + return nil, err + } + return bls12381G1ToBytes(a.Add(&a, &b)), nil +} + +func bls12381G2Add(aBytes, bBytes []byte) ([]byte, error) { + a, err := bytesToBLS12381G2(aBytes) + if err != nil { + return nil, err + } + b, err := bytesToBLS12381G2(bBytes) + if err != nil { + return nil, err + } + return bls12381G2ToBytes(a.Add(&a, &b)), nil +} + +func bls12381G1ScalarMul(aBytes []byte, k *big.Int) ([]byte, error) { + a, err := bytesToBLS12381G1(aBytes) + if err != nil { + return nil, err + } + return bls12381G1ToBytes(a.ScalarMultiplication(&a, k)), nil +} + +func bls12381G2ScalarMul(aBytes []byte, k *big.Int) ([]byte, error) { + a, err := bytesToBLS12381G2(aBytes) + if err != nil { + return nil, err + } + return bls12381G2ToBytes(a.ScalarMultiplication(&a, k)), nil +} + +func bls12381PairingCheck(g1Bytes, g2Bytes []byte) (bool, error) { + g1, err := bytesToBLS12381G1s(g1Bytes, true) + if err != nil { + return false, err + } + g2, err := bytesToBLS12381G2s(g2Bytes, true) + if err != nil { + return false, err + } + ok, err := bls12381.PairingCheck(g1, g2) + if err != nil { + return false, err + } + return ok, nil +} + +// We'll use a little concurrency to speed up the multiexp, but without a global +// mechanism to control parallelism across different modules, we'll just use 2. +var mecLimit = ecc.MultiExpConfig{ + NbTasks: 2, +} + +const bls12381G1MultiMulThreshold = 2 // determined by BenchmarkFindMultiMulCutoff + +func bls12381G1MultiMul(pointBytes, scalarBytes []byte) ([]byte, error) { + points, err := bytesToBLS12381G1s(pointBytes, false) + if err != nil { + return nil, err + } + if len(scalarBytes) != scalarSize*len(points) { + return nil, fmt.Errorf("bad scalars length %d. Expected %d", len(scalarBytes), scalarSize*len(points)) + } + if len(points) <= bls12381G1MultiMulThreshold { + return bls12381G1MultiMulSmall(points, scalarBytes) + } + return bls12381G1MultiMulLarge(points, scalarBytes) +} + +func bls12381G1MultiMulLarge(points []bls12381.G1Affine, scalarBytes []byte) ([]byte, error) { + scalars := make([]bls12381fr.Element, len(points)) + for i := range scalars { + scalars[i].SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + } + res, err := new(bls12381.G1Affine).MultiExp(points, scalars, mecLimit) + if err != nil { + return nil, err + } + return bls12381G1ToBytes(res), nil +} + +func bls12381G1MultiMulSmall(points []bls12381.G1Affine, scalarBytes []byte) ([]byte, error) { + // There must be at least one point. Start with it, rather than the identity. + k := new(big.Int).SetBytes(scalarBytes[:scalarSize]) + var sum bls12381.G1Jac + sum.ScalarMultiplicationAffine(&points[0], k) + for i := range points { + if i == 0 { + continue + } + k.SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + var prod bls12381.G1Jac + prod.ScalarMultiplicationAffine(&points[i], k) + sum.AddAssign(&prod) + } + var res bls12381.G1Affine + res.FromJacobian(&sum) + return bls12381G1ToBytes(&res), nil +} + +const bls12381G2MultiMulThreshold = 2 // determined by BenchmarkFindMultiMulCutoff + +func bls12381G2MultiMul(pointBytes, scalarBytes []byte) ([]byte, error) { + points, err := bytesToBLS12381G2s(pointBytes, false) + if err != nil { + return nil, err + } + if len(scalarBytes) != scalarSize*len(points) { + return nil, fmt.Errorf("bad scalars length %d. Expected %d", len(scalarBytes), scalarSize*len(points)) + } + if len(points) <= bls12381G2MultiMulThreshold { + return bls12381G2MultiMulSmall(points, scalarBytes) + } + return bls12381G2MultiMulLarge(points, scalarBytes) +} + +func bls12381G2MultiMulLarge(points []bls12381.G2Affine, scalarBytes []byte) ([]byte, error) { + scalars := make([]bls12381fr.Element, len(points)) + for i := range scalars { + scalars[i].SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + } + res, err := new(bls12381.G2Affine).MultiExp(points, scalars, mecLimit) + if err != nil { + return nil, err + } + return bls12381G2ToBytes(res), nil +} + +func bls12381G2MultiMulSmall(points []bls12381.G2Affine, scalarBytes []byte) ([]byte, error) { + // There must be at least one point. Start with it, rather than the identity. + k := new(big.Int).SetBytes(scalarBytes[:scalarSize]) + var sum bls12381.G2Jac + sum.FromAffine(&points[0]) + sum.ScalarMultiplication(&sum, k) + for i := range points { + if i == 0 { + continue + } + k.SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + var prod bls12381.G2Jac + prod.FromAffine(&points[i]) + prod.ScalarMultiplication(&prod, k) + sum.AddAssign(&prod) + } + var res bls12381.G2Affine + res.FromJacobian(&sum) + return bls12381G2ToBytes(&res), nil +} + +func bls12381MapToG1(fpBytes []byte) ([]byte, error) { + fp, err := bytesToBLS12381Field(fpBytes) + if err != nil { + return nil, err + } + point := bls12381.MapToG1(fp) + return bls12381G1ToBytes(&point), nil +} + +func bls12381MapToG2(fpBytes []byte) ([]byte, error) { + if len(fpBytes) != bls12381fp2Size { + return nil, fmt.Errorf("bad encoded element length: %d", len(fpBytes)) + } + g2 := bls12381.G2Affine{} + var err error + g2.X.A0, err = bytesToBLS12381Field(fpBytes[0:bls12381fpSize]) + if err != nil { + return nil, err + } + g2.X.A1, err = bytesToBLS12381Field(fpBytes[bls12381fpSize:]) + if err != nil { + return nil, err + } + point := bls12381.MapToG2(g2.X) + return bls12381G2ToBytes(&point), nil +} + +func bls12381G1SubgroupCheck(pointBytes []byte) (bool, error) { + point, err := bytesToBLS12381G1(pointBytes) + if err != nil { + return false, err + } + return point.IsInSubGroup(), nil +} + +func bls12381G2SubgroupCheck(pointBytes []byte) (bool, error) { + point, err := bytesToBLS12381G2(pointBytes) + if err != nil { + return false, err + } + return point.IsInSubGroup(), nil +} + +var bn254Modulus = bn254fp.Modulus() + +func bytesToBN254Field(b []byte) (bn254fp.Element, error) { + big := new(big.Int).SetBytes(b) + if big.Cmp(bn254Modulus) >= 0 { + return bn254fp.Element{}, fmt.Errorf("field element %s larger than modulus %s", big.String(), bn254Modulus) + } + return *new(bn254fp.Element).SetBigInt(big), nil +} + +func bytesToBN254G1(b []byte) (bn254.G1Affine, error) { + if len(b) != bn254g1Size { + return bn254.G1Affine{}, fmt.Errorf("bad length %d. Expected %d", len(b), bn254g1Size) + } + var point bn254.G1Affine + var err error + point.X, err = bytesToBN254Field(b[:bn254fpSize]) + if err != nil { + return bn254.G1Affine{}, err + } + point.Y, err = bytesToBN254Field(b[bn254fpSize:bn254g1Size]) + if err != nil { + return bn254.G1Affine{}, err + } + if !point.IsOnCurve() { + return bn254.G1Affine{}, errNotOnCurve + } + return point, nil +} + +func bytesToBN254G1s(b []byte, checkSubgroup bool) ([]bn254.G1Affine, error) { + if len(b)%bn254g1Size != 0 { + return nil, fmt.Errorf("bad length %d. Expected %d multiple", len(b), bn254g1Size) + } + if len(b) == 0 { + return nil, errEmptyInput + } + points := make([]bn254.G1Affine, len(b)/bn254g1Size) + for i := range points { + var err error + points[i], err = bytesToBN254G1(b[i*bn254g1Size : (i+1)*bn254g1Size]) + if err != nil { + return nil, err + } + if checkSubgroup && !points[i].IsInSubGroup() { + return nil, errWrongSubgroup + } + } + return points, nil +} + +func bytesToBN254G2(b []byte) (bn254.G2Affine, error) { + if len(b) != bn254g2Size { + return bn254.G2Affine{}, fmt.Errorf("bad length %d. Expected %d", len(b), bn254g2Size) + } + var err error + var point bn254.G2Affine + point.X.A0, err = bytesToBN254Field(b[:bn254fpSize]) + if err != nil { + return bn254.G2Affine{}, err + } + point.X.A1, err = bytesToBN254Field(b[bn254fpSize : 2*bn254fpSize]) + if err != nil { + return bn254.G2Affine{}, err + } + point.Y.A0, err = bytesToBN254Field(b[2*bn254fpSize : 3*bn254fpSize]) + if err != nil { + return bn254.G2Affine{}, err + } + point.Y.A1, err = bytesToBN254Field(b[3*bn254fpSize : 4*bn254fpSize]) + if err != nil { + return bn254.G2Affine{}, err + } + if !point.IsOnCurve() { + return bn254.G2Affine{}, errNotOnCurve + } + return point, nil +} + +func bytesToBN254G2s(b []byte, checkSubgroup bool) ([]bn254.G2Affine, error) { + if len(b)%bn254g2Size != 0 { + return nil, fmt.Errorf("bad length %d. Expected %d multiple", len(b), bn254g2Size) + } + if len(b) == 0 { + return nil, errEmptyInput + } + points := make([]bn254.G2Affine, len(b)/bn254g2Size) + for i := range points { + var err error + points[i], err = bytesToBN254G2(b[i*bn254g2Size : (i+1)*bn254g2Size]) + if err != nil { + return nil, err + } + if checkSubgroup && !points[i].IsInSubGroup() { + return nil, errWrongSubgroup + } + } + return points, nil +} + +func bn254G1ToBytes(g1 *bn254.G1Affine) []byte { + retX := g1.X.Bytes() + retY := g1.Y.Bytes() + pointBytes := make([]byte, bn254g1Size) + copy(pointBytes, retX[:]) + copy(pointBytes[bn254fpSize:], retY[:]) + return pointBytes +} + +func bn254G2ToBytes(g2 *bn254.G2Affine) []byte { + xFirst := g2.X.A0.Bytes() + xSecond := g2.X.A1.Bytes() + yFirst := g2.Y.A0.Bytes() + ySecond := g2.Y.A1.Bytes() + pointBytes := make([]byte, bn254g2Size) + copy(pointBytes, xFirst[:]) + copy(pointBytes[bn254fpSize:], xSecond[:]) + copy(pointBytes[bn254fp2Size:], yFirst[:]) + copy(pointBytes[bn254fp2Size+bn254fpSize:], ySecond[:]) + return pointBytes +} + +func bn254G1Add(aBytes, bBytes []byte) ([]byte, error) { + a, err := bytesToBN254G1(aBytes) + if err != nil { + return nil, err + } + b, err := bytesToBN254G1(bBytes) + if err != nil { + return nil, err + } + return bn254G1ToBytes(a.Add(&a, &b)), nil +} + +func bn254G2Add(aBytes, bBytes []byte) ([]byte, error) { + a, err := bytesToBN254G2(aBytes) + if err != nil { + return nil, err + } + b, err := bytesToBN254G2(bBytes) + if err != nil { + return nil, err + } + return bn254G2ToBytes(a.Add(&a, &b)), nil +} + +func bn254G1ScalarMul(aBytes []byte, k *big.Int) ([]byte, error) { + a, err := bytesToBN254G1(aBytes) + if err != nil { + return nil, err + } + return bn254G1ToBytes(a.ScalarMultiplication(&a, k)), nil +} + +func bn254G2ScalarMul(aBytes []byte, k *big.Int) ([]byte, error) { + a, err := bytesToBN254G2(aBytes) + if err != nil { + return nil, err + } + return bn254G2ToBytes(a.ScalarMultiplication(&a, k)), nil +} + +func bn254PairingCheck(g1Bytes, g2Bytes []byte) (bool, error) { + g1, err := bytesToBN254G1s(g1Bytes, true) + if err != nil { + return false, err + } + g2, err := bytesToBN254G2s(g2Bytes, true) + if err != nil { + return false, err + } ok, err := bn254.PairingCheck(g1, g2) if err != nil { - return errors.New("pairing failed") + return false, err } - cx.Stack = cx.Stack[:last] - cx.Stack[prev] = boolToSV(ok) - return nil + return ok, nil +} + +const bn254G1MultiMulThreshold = 3 // determined by BenchmarkFindMultiMulCutoff + +func bn254G1MultiMul(pointBytes, scalarBytes []byte) ([]byte, error) { + points, err := bytesToBN254G1s(pointBytes, false) + if err != nil { + return nil, err + } + if len(scalarBytes) != scalarSize*len(points) { + return nil, fmt.Errorf("bad scalars length %d. Expected %d", len(scalarBytes), scalarSize*len(points)) + } + if len(points) <= bn254G1MultiMulThreshold { + return bn254G1MultiMulSmall(points, scalarBytes) + } + return bn254G1MultiMulLarge(points, scalarBytes) +} + +func bn254G1MultiMulLarge(points []bn254.G1Affine, scalarBytes []byte) ([]byte, error) { + scalars := make([]bn254fr.Element, len(points)) + for i := range scalars { + scalars[i].SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + } + res, err := new(bn254.G1Affine).MultiExp(points, scalars, mecLimit) + if err != nil { + return nil, err + } + return bn254G1ToBytes(res), nil +} + +func bn254G1MultiMulSmall(points []bn254.G1Affine, scalarBytes []byte) ([]byte, error) { + // There must be at least one point. Start with it, rather than the identity. + k := new(big.Int).SetBytes(scalarBytes[:scalarSize]) + var sum bn254.G1Jac + sum.ScalarMultiplicationAffine(&points[0], k) + for i := range points { + if i == 0 { + continue + } + k.SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + var prod bn254.G1Jac + prod.ScalarMultiplicationAffine(&points[i], k) + sum.AddAssign(&prod) + } + var res bn254.G1Affine + res.FromJacobian(&sum) + return bn254G1ToBytes(&res), nil +} + +const bn254G2MultiMulThreshold = 2 // determined by BenchmarkFindMultiMulCutoff + +func bn254G2MultiMul(pointBytes, scalarBytes []byte) ([]byte, error) { + points, err := bytesToBN254G2s(pointBytes, false) + if err != nil { + return nil, err + } + if len(scalarBytes) != scalarSize*len(points) { + return nil, fmt.Errorf("bad scalars length %d. Expected %d", len(scalarBytes), scalarSize*len(points)) + } + if len(points) <= bn254G2MultiMulThreshold { + return bn254G2MultiMulSmall(points, scalarBytes) + } + return bn254G2MultiMulLarge(points, scalarBytes) +} + +func bn254G2MultiMulLarge(points []bn254.G2Affine, scalarBytes []byte) ([]byte, error) { + scalars := make([]bn254fr.Element, len(points)) + for i := range scalars { + scalars[i].SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + } + res, err := new(bn254.G2Affine).MultiExp(points, scalars, mecLimit) + if err != nil { + return nil, err + } + return bn254G2ToBytes(res), nil +} + +func bn254G2MultiMulSmall(points []bn254.G2Affine, scalarBytes []byte) ([]byte, error) { + // There must be at least one point. Start with it, rather than the identity. + k := new(big.Int).SetBytes(scalarBytes[:scalarSize]) + var sum bn254.G2Jac + sum.FromAffine(&points[0]) + sum.ScalarMultiplication(&sum, k) + for i := range points { + if i == 0 { + continue + } + k.SetBytes(scalarBytes[i*scalarSize : (i+1)*scalarSize]) + var prod bn254.G2Jac + prod.FromAffine(&points[i]) + prod.ScalarMultiplication(&prod, k) + sum.AddAssign(&prod) + } + var res bn254.G2Affine + res.FromJacobian(&sum) + return bn254G2ToBytes(&res), nil +} + +func bn254MapToG1(fpBytes []byte) ([]byte, error) { + fp, err := bytesToBN254Field(fpBytes) + if err != nil { + return nil, err + } + point := bn254.MapToG1(fp) + return bn254G1ToBytes(&point), nil +} + +func bn254MapToG2(fpBytes []byte) ([]byte, error) { + if len(fpBytes) != bn254fp2Size { + return nil, fmt.Errorf("bad encoded element length: %d", len(fpBytes)) + } + fp2 := bn254.G2Affine{}.X // no way to declare an fptower.E2 + var err error + fp2.A0, err = bytesToBN254Field(fpBytes[0:bn254fpSize]) + if err != nil { + return nil, err + } + fp2.A1, err = bytesToBN254Field(fpBytes[bn254fpSize:]) + if err != nil { + return nil, err + } + point := bn254.MapToG2(fp2) + return bn254G2ToBytes(&point), nil +} + +func bn254G1SubgroupCheck(pointBytes []byte) (bool, error) { + point, err := bytesToBN254G1(pointBytes) + if err != nil { + return false, err + } + return point.IsInSubGroup(), nil +} + +func bn254G2SubgroupCheck(pointBytes []byte) (bool, error) { + point, err := bytesToBN254G2(pointBytes) + if err != nil { + return false, err + } + return point.IsInSubGroup(), nil } diff --git a/data/transactions/logic/pairing_test.go b/data/transactions/logic/pairing_test.go index c96bd67197..914e303006 100644 --- a/data/transactions/logic/pairing_test.go +++ b/data/transactions/logic/pairing_test.go @@ -16,14 +16,878 @@ package logic +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "math/big" + "strings" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + bls12381fp "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" + bls12381fr "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + "github.com/consensys/gnark-crypto/ecc/bn254" + bn254fp "github.com/consensys/gnark-crypto/ecc/bn254/fp" + bn254fr "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/stretchr/testify/require" +) + const pairingNonsense = ` pushbytes 0x012345 dup - bn256_add + ec_add BN254g1 dup - bn256_scalar_mul + ec_scalar_mul BLS12_381g2 dup - bn256_pairing + ec_pairing_check BN254g1 + ec_multi_scalar_mul BLS12_381g2 + ec_subgroup_check BLS12_381g1 + ec_map_to BN254g2 ` -const pairingCompiled = "80030123454999499a499b" +const pairingCompiled = "800301234549e00049e10349e200e303e402e501" + +func bn254G1sToBytes(g1s []bn254.G1Affine) []byte { + var out []byte + for i := range g1s { + out = append(out, bn254G1ToBytes(&g1s[i])...) + } + return out +} + +func bn254G2sToBytes(g2s []bn254.G2Affine) []byte { + var out []byte + for i := range g2s { + out = append(out, bn254G2ToBytes(&g2s[i])...) + } + return out +} + +func bls12381G1sToBytes(g1s []bls12381.G1Affine) []byte { + var out []byte + for i := range g1s { + out = append(out, bls12381G1ToBytes(&g1s[i])...) + } + return out +} + +func bls12381G2sToBytes(g2s []bls12381.G2Affine) []byte { + var out []byte + for i := range g2s { + out = append(out, bls12381G2ToBytes(&g2s[i])...) + } + return out +} + +type pairConstants [2]curveConstants + +type curveConstants struct { + name string + size uint64 + q *big.Int // the size of the entire curve + r *big.Int // size of the main prime order group + rand func() []byte +} + +var bnCurves = pairConstants{ + {"BN254g1", 64, bn254fp.Modulus(), bn254fr.Modulus(), + func() []byte { p := bn254RandomG1(); return bn254G1ToBytes(&p) }}, + {"BN254g2", 128, bn254fp.Modulus(), bn254fr.Modulus(), + func() []byte { p := bn254RandomG2(); return bn254G2ToBytes(&p) }}, +} +var blsCurves = pairConstants{ + {"BLS12_381g1", 96, bls12381fp.Modulus(), bls12381fr.Modulus(), + func() []byte { p := bls12381RandomG1(); return bls12381G1ToBytes(&p) }}, + {"BLS12_381g2", 192, bls12381fp.Modulus(), bls12381fr.Modulus(), + func() []byte { p := bls12381RandomG2(); return bls12381G2ToBytes(&p) }}, +} + +func tealBytes(b []byte) string { + return fmt.Sprintf("byte 0x%s;", hex.EncodeToString(b)) +} + +func tealInt(i uint64) string { + return fmt.Sprintf("int %d;", i) +} + +func TestEcAdd(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + t.Run(c.name, func(t *testing.T) { + pt1 := tealBytes(c.rand()) + pt2 := tealBytes(c.rand()) + add := "ec_add " + c.name + ";" + testAccepts(t, pt1+pt2+add+"len", pairingVersion) + // rando + 0 = rando + testAccepts(t, pt1+tealInt(c.size)+"bzero;"+add+pt1+"==", pairingVersion) + // bad lengths, arg 2 + testPanics(t, pt1+tealInt(c.size+1)+"bzero;"+add+pt1+"==", pairingVersion, "bad length") + testPanics(t, pt1+tealInt(c.size-1)+"bzero;"+add+pt1+"==", pairingVersion, "bad length") + // 0 + rando = rando + testAccepts(t, tealInt(c.size)+"bzero;"+pt1+add+pt1+"==", pairingVersion) + // bad lengths, arg 1 + testPanics(t, tealInt(c.size+1)+"bzero;"+pt1+add+pt1+"==", pairingVersion, "bad length") + testPanics(t, tealInt(c.size-1)+"bzero;"+pt1+add+pt1+"==", pairingVersion, "bad length") + + // 0 + 0 = 0 + testAccepts(t, tealInt(c.size)+"bzero; dupn 2;"+add+"==", pairingVersion) + + // ought to test "on curve, but not in subgroup" but bn254g1 has no such points + }) + } + +} + +func TestEcScalarMul(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + t.Run(c.name, func(t *testing.T) { + pt := tealBytes(c.rand()) + mul := "ec_scalar_mul " + c.name + ";" + + testAccepts(t, pt+"int 0; itob;"+mul+tealInt(c.size)+"bzero; ==", pairingVersion) + testPanics(t, "int 63; bzero; int 1; itob;"+mul+"len", pairingVersion, "bad length") + testPanics(t, "int 65; bzero; int 1; itob;"+mul+"len", pairingVersion, "bad length") + testPanics(t, pt+"int 33; bzero;"+mul+tealInt(c.size)+"bzero; ==", pairingVersion, "ec_scalar_mul scalar len is 33") + // multiply by prime order gives 0,0 (the "point at infinity") + r := tealBytes(c.r.Bytes()) + testAccepts(t, pt+r+mul+tealInt(c.size)+"bzero; ==", pairingVersion) + + // multiplying by 1 does nothing + testAccepts(t, pt+"int 1; itob;"+mul+pt+"==", pairingVersion) + + // multiplying by r+1 does nothing + rp1 := big.NewInt(1) + rp1.Add(rp1, c.r) + testAccepts(t, pt+tealBytes(rp1.Bytes())+mul+pt+"==", pairingVersion) + + // shows that "short" big-endian scalars are ok + testAccepts(t, pt+` +dup +int 32; bzero; int 7; itob; b|;`+mul+` +swap +int 7; itob;`+mul+` +== +`, pairingVersion) + }) + } +} + +func TestPairCheck(t *testing.T) { + partitiontest.PartitionTest(t) + //nolint:paralleltest // Not parallel because it modifies testLogicBudget + + was := testLogicBudget + testLogicBudget = 16 * 20_000 + defer func() { testLogicBudget = was }() + + t.Run("bn254", func(t *testing.T) { + var g1GenNeg bn254.G1Affine + g1GenNeg.Neg(&bnG1Gen) + g1points := []bn254.G1Affine{g1GenNeg, bnG1Gen} + g2points := []bn254.G2Affine{bnG2Gen, bnG2Gen} + // -1 g1 g2 + g1 g2 = 0 + g1bytes := tealBytes(bn254G1sToBytes(g1points)) + g2bytes := tealBytes(bn254G2sToBytes(g2points)) + + testAccepts(t, g1bytes+g2bytes+`ec_pairing_check BN254g1`, pairingVersion) + testAccepts(t, g2bytes+g1bytes+`ec_pairing_check BN254g2`, pairingVersion) + }) + + t.Run("bls12-381", func(t *testing.T) { + var g1GenNeg bls12381.G1Affine + g1GenNeg.Neg(&blsG1Gen) + g1points := []bls12381.G1Affine{g1GenNeg, blsG1Gen} + g2points := []bls12381.G2Affine{blsG2Gen, blsG2Gen} + // -1 g1 g2 + g1 g2 = 0 + g1bytes := tealBytes(bls12381G1sToBytes(g1points)) + g2bytes := tealBytes(bls12381G2sToBytes(g2points)) + + testAccepts(t, g1bytes+g2bytes+`ec_pairing_check BLS12_381g1`, pairingVersion) + testAccepts(t, g2bytes+g1bytes+`ec_pairing_check BLS12_381g2`, pairingVersion) + }) +} + +func TestEcMultiExp(t *testing.T) { + partitiontest.PartitionTest(t) + //nolint:paralleltest // Not parallel because it modifies testLogicBudget + + was := testLogicBudget + testLogicBudget = 16 * 20_000 + defer func() { testLogicBudget = was }() + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + t.Run(c.name, func(t *testing.T) { + pt := tealBytes(c.rand()) + multiexp := "ec_multi_scalar_mul " + c.name + ";" + mul := "ec_scalar_mul " + c.name + ";" + + // multiply by 0 gives 0 + testAccepts(t, pt+"int 32; bzero;"+multiexp+tealInt(c.size)+"bzero; ==", pairingVersion) + // multiply by 1 gives 1 + testAccepts(t, pt+"int 32; bzero; int 1; itob; b|;"+multiexp+pt+"==", pairingVersion) + // two multiplies by 1 gives same as multiply 2 + testAccepts(t, pt+"dup; concat; int 32; bzero; int 1; itob; b|; dup; concat;"+multiexp+ + pt+"byte 0x02;"+mul+"==", pairingVersion) + }) + } +} + +func requireBlsG1Eq(t *testing.T, g1points []bls12381.G1Affine, kbytes []byte) { + b1, err := bls12381G1MultiMulSmall(g1points, kbytes) + require.NoError(t, err) + b2, err := bls12381G1MultiMulLarge(g1points, kbytes) + require.NoError(t, err) + require.Equal(t, b1, b2) +} + +func TestBlsG1LargeSmallEquivalent(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + zero := [32]byte{} + for i := 1; i < 10; i++ { + g1points := make([]bls12381.G1Affine, i) + for j := 0; j < i; j++ { + g1points[j] = bls12381RandomG1() + } + kbytes := make([]byte, i*scalarSize) + rand.Read(kbytes) + requireBlsG1Eq(t, g1points, kbytes) + g1points[0] = bls12381.G1Affine{} // Infinity at 0 + requireBlsG1Eq(t, g1points, kbytes) + g1points[0] = bls12381RandomG1() // change back to random + g1points[i-1] = bls12381.G1Affine{} // Infinity at end + requireBlsG1Eq(t, g1points, kbytes) + copy(kbytes, zero[:]) // zero scalar + requireBlsG1Eq(t, g1points, kbytes) + } +} + +func requireBlsG2Eq(t *testing.T, g2points []bls12381.G2Affine, kbytes []byte) { + b1, err := bls12381G2MultiMulSmall(g2points, kbytes) + require.NoError(t, err) + b2, err := bls12381G2MultiMulLarge(g2points, kbytes) + require.NoError(t, err) + require.Equal(t, b1, b2) +} + +func TestBlsG2LargeSmallEquivalent(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + zero := [32]byte{} + for i := 1; i < 10; i++ { + g2points := make([]bls12381.G2Affine, i) + for j := 0; j < i; j++ { + g2points[j] = bls12381RandomG2() + } + kbytes := make([]byte, i*scalarSize) + rand.Read(kbytes) + requireBlsG2Eq(t, g2points, kbytes) + g2points[0] = bls12381.G2Affine{} // Infinity at 0 + requireBlsG2Eq(t, g2points, kbytes) + g2points[0] = bls12381RandomG2() // change back to random + g2points[i-1] = bls12381.G2Affine{} // Infinity at end + requireBlsG2Eq(t, g2points, kbytes) + copy(kbytes, zero[:]) // zero scalar + requireBlsG2Eq(t, g2points, kbytes) + } +} + +func requireBnG1Eq(t *testing.T, g1points []bn254.G1Affine, kbytes []byte) { + b1, err := bn254G1MultiMulSmall(g1points, kbytes) + require.NoError(t, err) + b2, err := bn254G1MultiMulLarge(g1points, kbytes) + require.NoError(t, err) + require.Equal(t, b1, b2) +} + +func TestBnG1LargeSmallEquivalent(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + zero := [32]byte{} + for i := 1; i < 10; i++ { + g1points := make([]bn254.G1Affine, i) + for j := 0; j < i; j++ { + g1points[j] = bn254RandomG1() + } + kbytes := make([]byte, i*scalarSize) + rand.Read(kbytes) + requireBnG1Eq(t, g1points, kbytes) + g1points[0] = bn254.G1Affine{} // Infinity at 0 + requireBnG1Eq(t, g1points, kbytes) + g1points[0] = bn254RandomG1() // change back to random + g1points[i-1] = bn254.G1Affine{} // Infinity at end + requireBnG1Eq(t, g1points, kbytes) + copy(kbytes, zero[:]) // zero scalar + requireBnG1Eq(t, g1points, kbytes) + } +} + +func requireBnG2Eq(t *testing.T, g2points []bn254.G2Affine, kbytes []byte) { + b1, err := bn254G2MultiMulSmall(g2points, kbytes) + require.NoError(t, err) + b2, err := bn254G2MultiMulLarge(g2points, kbytes) + require.NoError(t, err) + require.Equal(t, b1, b2) +} + +func TestBnG2LargeSmallEquivalent(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + zero := [32]byte{} + for i := 1; i < 10; i++ { + g2points := make([]bn254.G2Affine, i) + for j := 0; j < i; j++ { + g2points[j] = bn254RandomG2() + } + kbytes := make([]byte, i*scalarSize) + rand.Read(kbytes) + requireBnG2Eq(t, g2points, kbytes) + g2points[0] = bn254.G2Affine{} // Infinity at 0 + requireBnG2Eq(t, g2points, kbytes) + g2points[0] = bn254RandomG2() // change back to random + g2points[i-1] = bn254.G2Affine{} // Infinity at end + requireBnG2Eq(t, g2points, kbytes) + copy(kbytes, zero[:]) // zero scalar + requireBnG2Eq(t, g2points, kbytes) + } +} + +// TestAgreement ensures that scalar muls and adds is the same as multi_exp +func TestAgreement(t *testing.T) { + partitiontest.PartitionTest(t) + //nolint:paralleltest // Not parallel because it modifies testLogicBudget + + was := testLogicBudget + testLogicBudget = 16 * 20_000 + defer func() { testLogicBudget = was }() + + k1 := "2F53" // any old int + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + t.Run(c.name, func(t *testing.T) { + pt1 := tealBytes(c.rand()) + pt2 := tealBytes(c.rand()) + + multiexp := "ec_multi_scalar_mul " + c.name + ";" + mul := "ec_scalar_mul " + c.name + ";" + add := "ec_add " + c.name + ";" + + // Try a normal k2 and one very big one + for _, k2 := range []string{"372D82", strings.Repeat("FE", 32)} { + testAccepts(t, fmt.Sprintf(` + %s + byte 0x%s;`+mul+` + %s + byte 0x%s;`+mul+add+` + %s; %s; concat + int 32; bzero; byte 0x%s; b|; + int 32; bzero; byte 0x%s; b|; + concat;`+multiexp+`==`, + pt1, k1, pt2, k2, + pt1, pt2, k1, k2), pairingVersion) + } + }) + } +} + +func TestSubgroupCheckInfinity(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + t.Run(c.name, func(t *testing.T) { + testAccepts(t, tealInt(c.size)+"bzero; ec_subgroup_check "+c.name, pairingVersion) + testPanics(t, tealInt(c.size+1)+"bzero; ec_subgroup_check "+c.name, pairingVersion, "bad length") + testPanics(t, tealInt(c.size-1)+"bzero; ec_subgroup_check "+c.name, pairingVersion, "bad length") + }) + } +} + +func TestSubgroupCheck(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + curves := []curveConstants{bnCurves[0], bnCurves[1], blsCurves[0], blsCurves[1]} + for _, c := range curves { + pt := tealBytes(c.rand()) + testAccepts(t, pt+"ec_subgroup_check "+c.name, pairingVersion) + + /* On BN curve, subgroup == on curve, we can't create a g1bytes that makes this Accept + pt = ??? + testAccepts(t, g1bytes1+"ec_subgroup_check BN254g1; !", pairingVersion) + */ + + // surely no longer in subgroup, but also not likely on curve, so we get a panic + changed := strings.Replace(pt, "a", "f", 1) + changed = strings.Replace(changed, "c", "a", 1) + testPanics(t, changed+"ec_subgroup_check "+c.name+"; !", pairingVersion, "point not on curve") + } +} + +func TestMapTo(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + for _, curve := range []string{"BN254g1", "BLS12_381g1"} { + testAccepts(t, fmt.Sprintf("int 27; itob; ec_map_to %s; ec_subgroup_check %s", + curve, curve), pairingVersion) + } + +} + +// TestSlowMapTo tests the G2 MapTo functions, which require more budget, and +// therefore mess with a global and prevent t.Parallel. +func TestSlowMapTo(t *testing.T) { + partitiontest.PartitionTest(t) + //nolint:paralleltest // Not parallel because it modifies testLogicBudget + + was := testLogicBudget + testLogicBudget = 16 * 20_000 + defer func() { testLogicBudget = was }() + for _, curve := range []string{"BN254g2", "BLS12_381g2"} { + testPanics(t, fmt.Sprintf("int 27; itob; ec_map_to %s; ec_subgroup_check %s", + curve, curve), pairingVersion, "bad encoded element length") + } + + testAccepts(t, ` +int 32; bzero +int 67; itob; b| +int 32; bzero +int 2783; itob; b| +concat +ec_map_to BN254g2 +ec_subgroup_check BN254g2`, pairingVersion) + + testAccepts(t, ` +int 48; bzero +int 67; itob; b| +int 48; bzero +int 2783; itob; b| +concat +ec_map_to BLS12_381g2 +ec_subgroup_check BLS12_381g2`, pairingVersion) + +} + +func BenchmarkBn254(b *testing.B) { + was := mecLimit.NbTasks + mecLimit.NbTasks = 1 + defer func() { mecLimit.NbTasks = was }() + + g1point := bn254RandomG1() + g1teal := tealBytes(bn254G1ToBytes(&g1point)) + + g2point := bn254RandomG2() + g2teal := tealBytes(bn254G2ToBytes(&g2point)) + + b.Run("g1 add", func(b *testing.B) { + benchmarkOperation(b, g1teal, "dup; ec_add BN254g1", "len") + }) + b.Run("g2 add", func(b *testing.B) { + benchmarkOperation(b, g2teal, "dup; ec_add BN254g2", "len") + }) + + b.Run("g1 scalar_mul", func(b *testing.B) { + benchmarkOperation(b, g1teal, "dup; extract 0 32; ec_scalar_mul BN254g1", "len") + }) + + for i := 0; i < 7; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + b.Run(fmt.Sprintf("g1 multi_exp %d", size), func(b *testing.B) { + benchmarkOperation(b, g1teal, dups+"dup; extract 0 32;"+dups+"ec_multi_scalar_mul BN254g1", "len") + }) + } + + b.Run("g2 scalar_mul", func(b *testing.B) { + benchmarkOperation(b, g2teal, "dup; extract 0 32; ec_scalar_mul BN254g2", "len") + }) + + for i := 0; i < 6; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + b.Run(fmt.Sprintf("g2 multi_exp %d", size), func(b *testing.B) { + benchmarkOperation(b, g2teal, dups+"dup; extract 0 32;"+dups+"ec_multi_scalar_mul BN254g2", "len") + }) + } + + var g1GenNeg bn254.G1Affine + g1GenNeg.Neg(&bnG1Gen) + g1points := []bn254.G1Affine{g1GenNeg, bnG1Gen} + g2points := []bn254.G2Affine{bnG2Gen, bnG2Gen} + // -1 g1 g2 + g1 g2 = 0 + g1pbytes := tealBytes(bn254G1sToBytes(g1points)) + g2pbytes := tealBytes(bn254G2sToBytes(g2points)) + + b.Run("pairing 1", func(b *testing.B) { + benchmarkOperation(b, "", g1teal+g2teal+"ec_pairing_check BN254g1; !; assert", "int 1") + }) + for i := 0; i < 4; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + + // size * 2 in name because we start with two points + b.Run(fmt.Sprintf("pairing %d", size*2), func(b *testing.B) { + benchmarkOperation(b, "", g1pbytes+dups+g2pbytes+dups+"ec_pairing_check BN254g1; assert", "int 1") + }) + } + + b.Run("g1 subgroup", func(b *testing.B) { + benchmarkOperation(b, "", g1teal+"ec_subgroup_check BN254g1; assert", "int 1") + }) + b.Run("g2 subgroup", func(b *testing.B) { + benchmarkOperation(b, "", g2teal+"ec_subgroup_check BN254g2; assert", "int 1") + }) + + fpbytes := fmt.Sprintf("byte 0x%s\n", + strings.Repeat("00", 1)+strings.Repeat("22", bn254fpSize-1)) + fp2bytes := fpbytes + fpbytes + "concat\n" + + b.Run("g1 map to", func(b *testing.B) { + benchmarkOperation(b, "", fpbytes+"ec_map_to BN254g1; pop", "int 1") + }) + b.Run("g2 map to", func(b *testing.B) { + benchmarkOperation(b, "", fp2bytes+"ec_map_to BN254g2; pop", "int 1") + }) + +} + +func BenchmarkFindMultiMulCutoff(b *testing.B) { + for i := 1; i < 5; i++ { + kbytes := make([]byte, i*scalarSize) + { + g1points := make([]bls12381.G1Affine, i) + b.Run(fmt.Sprintf("bls g1 small %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g1points[j] = bls12381RandomG1() + } + rand.Read(kbytes) + bls12381G1MultiMulSmall(g1points, kbytes) + } + }) + b.Run(fmt.Sprintf("bls g1 large %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g1points[j] = bls12381RandomG1() + } + rand.Read(kbytes) + bls12381G1MultiMulLarge(g1points, kbytes) + } + }) + + g2points := make([]bls12381.G2Affine, i) + b.Run(fmt.Sprintf("bls g2 small %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g2points[j] = bls12381RandomG2() + } + rand.Read(kbytes) + bls12381G2MultiMulSmall(g2points, kbytes) + } + }) + b.Run(fmt.Sprintf("bls g2 large %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g2points[j] = bls12381RandomG2() + } + rand.Read(kbytes) + bls12381G2MultiMulLarge(g2points, kbytes) + } + }) + } + + { + g1points := make([]bn254.G1Affine, i) + b.Run(fmt.Sprintf("bn g1 small %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g1points[j] = bn254RandomG1() + } + rand.Read(kbytes) + bn254G1MultiMulSmall(g1points, kbytes) + } + }) + b.Run(fmt.Sprintf("bn g1 large %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g1points[j] = bn254RandomG1() + } + rand.Read(kbytes) + bn254G1MultiMulLarge(g1points, kbytes) + } + }) + + g2points := make([]bn254.G2Affine, i) + b.Run(fmt.Sprintf("bn g2 small %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g2points[j] = bn254RandomG2() + } + rand.Read(kbytes) + bn254G2MultiMulSmall(g2points, kbytes) + } + }) + b.Run(fmt.Sprintf("bn g2 large %02d", i), func(b *testing.B) { + for r := 0; r < b.N; r++ { + for j := 0; j < i; j++ { + g2points[j] = bn254RandomG2() + } + rand.Read(kbytes) + bn254G2MultiMulLarge(g2points, kbytes) + } + }) + } + + } +} + +func bn254RandomG1() bn254.G1Affine { + var fp bn254fp.Element + fp.SetRandom() + return bn254.MapToG1(fp) +} + +func bn254RandomG2() bn254.G2Affine { + fp2 := bn254.G2Affine{}.X // no way to declare an fptower.E2 + fp2.SetRandom() + return bn254.MapToG2(fp2) +} + +func byteRepeat(bytes []byte, count int) string { + return "byte 0x" + strings.Repeat(hex.EncodeToString(bytes), count) + "\n" +} + +func BenchmarkBls12381(b *testing.B) { + was := mecLimit.NbTasks + mecLimit.NbTasks = 1 + defer func() { mecLimit.NbTasks = was }() + + g1point := bls12381RandomG1() + g1teal := tealBytes(bls12381G1ToBytes(&g1point)) + + g2point := bls12381RandomG2() + g2teal := tealBytes(bls12381G2ToBytes(&g2point)) + + b.Run("g1 add", func(b *testing.B) { + benchmarkOperation(b, g1teal, "dup; ec_add BLS12_381g1", "len") + }) + b.Run("g2 add", func(b *testing.B) { + benchmarkOperation(b, g2teal, "dup; ec_add BLS12_381g2", "len") + }) + + b.Run("g1 scalar_mul", func(b *testing.B) { + benchmarkOperation(b, g1teal, "dup; extract 0 32; ec_scalar_mul BLS12_381g1", "len") + }) + for i := 0; i < 6; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + b.Run(fmt.Sprintf("g1 multi_exp %d", size), func(b *testing.B) { + benchmarkOperation(b, g1teal, dups+"dup; extract 0 32;"+dups+"ec_multi_scalar_mul BLS12_381g1", "len") + }) + } + + b.Run("g2 scalar_mul", func(b *testing.B) { + benchmarkOperation(b, g2teal, "dup; extract 0 32; ec_scalar_mul BLS12_381g2", "len") + }) + for i := 0; i < 5; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + b.Run(fmt.Sprintf("g2 multi_exp %d", size), func(b *testing.B) { + benchmarkOperation(b, g2teal, dups+"dup; extract 0 32;"+dups+"ec_multi_scalar_mul BLS12_381g2", "len") + }) + } + + var g1GenNeg bls12381.G1Affine + g1GenNeg.Neg(&blsG1Gen) + g1points := []bls12381.G1Affine{g1GenNeg, blsG1Gen} + g2points := []bls12381.G2Affine{blsG2Gen, blsG2Gen} + // -1 g1 g2 + g1 g2 = 0 + g1pbytes := tealBytes(bls12381G1sToBytes(g1points)) + g2pbytes := tealBytes(bls12381G2sToBytes(g2points)) + + b.Run("g1 pairing f", func(b *testing.B) { + benchmarkOperation(b, "", g1teal+g2teal+"ec_pairing_check BLS12_381g1; !; assert", "int 1") + }) + for i := 0; i < 4; i++ { + size := 1 << uint(i) + dups := strings.Repeat("dup; concat;", i) + + // size * 2 in name because we start with two points + b.Run(fmt.Sprintf("pairing %d", size*2), func(b *testing.B) { + benchmarkOperation(b, "", g1pbytes+dups+g2pbytes+dups+"ec_pairing_check BLS12_381g1; assert", "int 1") + }) + } + + b.Run("g1 subgroup", func(b *testing.B) { + benchmarkOperation(b, "", g1teal+"ec_subgroup_check BLS12_381g1; pop", "int 1") + }) + b.Run("g2 subgroup", func(b *testing.B) { + benchmarkOperation(b, "", g2teal+"ec_subgroup_check BLS12_381g2; pop", "int 1") + }) + + fpbytes := fmt.Sprintf("byte 0x%s\n", + strings.Repeat("00", 1)+strings.Repeat("22", bls12381fpSize-1)) + fp2bytes := fpbytes + fpbytes + "concat\n" + + b.Run("g1 map to", func(b *testing.B) { + benchmarkOperation(b, "", fpbytes+"ec_map_to BLS12_381g1; pop", "int 1") + }) + b.Run("g2 map to", func(b *testing.B) { + benchmarkOperation(b, "", fp2bytes+"ec_map_to BLS12_381g2; pop", "int 1") + }) +} + +func bls12381RandomG1() bls12381.G1Affine { + var fp bls12381fp.Element + fp.SetRandom() + return bls12381.MapToG1(fp) +} + +func bls12381RandomG2() bls12381.G2Affine { + fp2 := bls12381.G2Affine{}.X // no way to declare an fptower.E2 + fp2.SetRandom() + return bls12381.MapToG2(fp2) +} + +var bnG1Gen bn254.G1Affine +var bnG2Gen bn254.G2Affine + +func init() { + var g1GenJac bn254.G1Jac + var g2GenJac bn254.G2Jac + + g1GenJac.X.SetOne() + g1GenJac.Y.SetUint64(2) + g1GenJac.Z.SetOne() + + g2GenJac.X.SetString( + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634") + g2GenJac.Y.SetString( + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531") + g2GenJac.Z.SetString("1", "0") + + bnG1Gen.FromJacobian(&g1GenJac) + bnG2Gen.FromJacobian(&g2GenJac) +} + +var blsG1Gen bls12381.G1Affine +var blsG2Gen bls12381.G2Affine + +func init() { + var g1GenJac bls12381.G1Jac + var g2GenJac bls12381.G2Jac + + g1GenJac.X.SetOne() + g1GenJac.Y.SetUint64(2) + g1GenJac.Z.SetOne() + + g1GenJac.X.SetString("3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507") + g1GenJac.Y.SetString("1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569") + g1GenJac.Z.SetOne() + + g2GenJac.X.SetString( + "352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160", + "3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758") + g2GenJac.Y.SetString( + "1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905", + "927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582") + g2GenJac.Z.SetString("1", "0") + + blsG1Gen.FromJacobian(&g1GenJac) + blsG2Gen.FromJacobian(&g2GenJac) +} + +// TestFieldCosts ensures that costs are calculated right for an opcodes +// whose costs depends on the immediate +func TestFieldCosts(t *testing.T) { //nolint:paralleltest // manipulates opcode table + partitiontest.PartitionTest(t) + + // make an opcode "xxx" that just performs a pop. But it takes an immediate + // - any of the "EC" constants. The first three fields have different + // costs. + + xxx := OpSpec{ + Opcode: 106, + Name: "xxx", + op: opPop, + Proto: proto("a:"), + OpDetails: costByField("f", &EcGroups, []int{10, 20, 30, 33}), + } + + withOpcode(t, LogicVersion, xxx, func(opcode byte) { + testApp(t, "int 32; bzero; xxx BN254g1; global OpcodeBudget; int 687; ==", nil) + testApp(t, "int 32; bzero; xxx BN254g2; global OpcodeBudget; int 677; ==", nil) + testApp(t, "int 32; bzero; xxx BLS12_381g1; global OpcodeBudget; int 667; ==", nil) + testApp(t, "int 32; bzero; xxx BLS12_381g2; global OpcodeBudget; int 664; ==", nil) + }) +} + +// TestLinearFieldCost ensures that costs are calculated right for an opcodes +// that have field AND arg length costs +func TestLinearFieldCost(t *testing.T) { //nolint:paralleltest // manipulates opcode table + partitiontest.PartitionTest(t) + + // make an opcode "xxx" that just performs a pop. But it takes an immediate + // - any of the "EC" constants. The first three fields have different + // costs, that depend on the length of the input + + xxx := OpSpec{ + Opcode: 106, + Name: "xxx", + op: opPop, + Proto: proto("a:"), + OpDetails: costByFieldAndLength("f", &EcGroups, []linearCost{{ + baseCost: 1, + chunkCost: 2, + chunkSize: 2, + }, { + baseCost: 5, + chunkCost: 2, + chunkSize: 10, + }, { + baseCost: 1, + chunkCost: 1, + chunkSize: 1, + }, { + baseCost: 1, + chunkCost: 1, + chunkSize: 1, + }}), + } + + withOpcode(t, LogicVersion, xxx, func(opcode byte) { + // starts at 1, goes up by two for each PAIR of bytes + testApp(t, "int 0; bzero; xxx BN254g1; global OpcodeBudget; int 696; ==", nil) + testApp(t, "int 1; bzero; xxx BN254g1; global OpcodeBudget; int 694; ==", nil) + testApp(t, "int 2; bzero; xxx BN254g1; global OpcodeBudget; int 694; ==", nil) + testApp(t, "int 3; bzero; xxx BN254g1; global OpcodeBudget; int 692; ==", nil) + testApp(t, "int 4; bzero; xxx BN254g1; global OpcodeBudget; int 692; ==", nil) + + // starts at 5, goes up by two for each 10 bytes + testApp(t, "int 0; bzero; xxx BN254g2; global OpcodeBudget; int 692; ==", nil) + testApp(t, "int 1; bzero; xxx BN254g2; global OpcodeBudget; int 690; ==", nil) + testApp(t, "int 2; bzero; xxx BN254g2; global OpcodeBudget; int 690; ==", nil) + testApp(t, "int 9; bzero; xxx BN254g2; global OpcodeBudget; int 690; ==", nil) + testApp(t, "int 10; bzero; xxx BN254g2; global OpcodeBudget; int 690; ==", nil) + testApp(t, "int 11; bzero; xxx BN254g2; global OpcodeBudget; int 688; ==", nil) + }) +} diff --git a/data/transactions/logic/sourcemap.go b/data/transactions/logic/sourcemap.go index 6bc516749a..fb12f50f6a 100644 --- a/data/transactions/logic/sourcemap.go +++ b/data/transactions/logic/sourcemap.go @@ -41,21 +41,21 @@ type SourceMap struct { // GetSourceMap returns a struct containing details about // the assembled file and encoded mappings to the source file. -func GetSourceMap(sourceNames []string, offsetToLine map[int]int) SourceMap { +func GetSourceMap(sourceNames []string, offsetToLocation map[int]SourceLocation) SourceMap { maxPC := 0 - for pc := range offsetToLine { + for pc := range offsetToLocation { if pc > maxPC { maxPC = pc } } // Array where index is the PC and value is the line for `mappings` field. - prevSourceLine := 0 + prevSourceLocation := SourceLocation{} pcToLine := make([]string, maxPC+1) for pc := range pcToLine { - if line, ok := offsetToLine[pc]; ok { - pcToLine[pc] = MakeSourceMapLine(0, 0, line-prevSourceLine, 0) - prevSourceLine = line + if location, ok := offsetToLocation[pc]; ok { + pcToLine[pc] = MakeSourceMapLine(0, 0, location.Line-prevSourceLocation.Line, location.Column-prevSourceLocation.Column) + prevSourceLocation = location } else { pcToLine[pc] = "" } diff --git a/data/transactions/logic/sourcemap_test.go b/data/transactions/logic/sourcemap_test.go index c3b1a73a80..f5c3455313 100644 --- a/data/transactions/logic/sourcemap_test.go +++ b/data/transactions/logic/sourcemap_test.go @@ -17,7 +17,6 @@ package logic import ( - "strings" "testing" "github.com/algorand/go-algorand/test/partitiontest" @@ -30,28 +29,22 @@ func TestGetSourceMap(t *testing.T) { a := require.New(t) sourceNames := []string{"test.teal"} - offsetToLine := map[int]int{ - 1: 1, - 2: 2, - 5: 3, + offsetToLocation := map[int]SourceLocation{ + 1: {Line: 1}, + 2: {Line: 2}, + 5: {Line: 3}, + 6: {Line: 3, Column: 1}, + 7: {Line: 4}, + 8: {Line: 5, Column: 5}, + 9: {Line: 5, Column: 6}, + 10: {Line: 6}, } - actualSourceMap := GetSourceMap(sourceNames, offsetToLine) + actualSourceMap := GetSourceMap(sourceNames, offsetToLocation) a.Equal(sourceMapVersion, actualSourceMap.Version) a.Equal(sourceNames, actualSourceMap.Sources) a.Equal([]string{}, actualSourceMap.Names) - - // Check encoding for `mappings` field. - splitMappings := strings.Split(actualSourceMap.Mappings, ";") - prevLine := 0 - for pc := range splitMappings { - if line, ok := offsetToLine[pc]; ok { - a.Equal(MakeSourceMapLine(0, 0, line-prevLine, 0), splitMappings[pc]) - prevLine = line - } else { - a.Equal("", splitMappings[pc]) - } - } + a.Equal(";AACA;AACA;;;AACA;AAAC;AACD;AACK;AAAC;AACN", actualSourceMap.Mappings) } func TestVLQ(t *testing.T) { diff --git a/data/transactions/logic/teal.tmLanguage.json b/data/transactions/logic/teal.tmLanguage.json index ddacfaa422..f63c91778a 100644 --- a/data/transactions/logic/teal.tmLanguage.json +++ b/data/transactions/logic/teal.tmLanguage.json @@ -76,7 +76,7 @@ }, { "name": "keyword.operator.teal", - "match": "^(\\!|\\!\\=|%|\u0026|\u0026\u0026|\\*|\\+|\\-|/|\\\u003c|\\\u003c\\=|\\=\\=|\\\u003e|\\\u003e\\=|\\^|addw|bitlen|bn256_add|bn256_pairing|bn256_scalar_mul|btoi|concat|divmodw|divw|ecdsa_pk_decompress|ecdsa_pk_recover|ecdsa_verify|ed25519verify|ed25519verify_bare|exp|expw|getbit|getbyte|itob|keccak256|len|mulw|setbit|setbyte|sha256|sha3_256|sha512_256|shl|shr|sqrt|vrf_verify|\\||\\|\\||\\~|b\\!\\=|b%|b\\*|b\\+|b\\-|b/|b\\\u003c|b\\\u003c\\=|b\\=\\=|b\\\u003e|b\\\u003e\\=|bsqrt|b\u0026|b\\^|b\\||b\\~|base64_decode|extract|extract3|extract_uint16|extract_uint32|extract_uint64|json_ref|replace2|replace3|substring|substring3|gitxn|gitxna|gitxnas|itxn|itxn_begin|itxn_field|itxn_next|itxn_submit|itxna|itxnas)\\b" + "match": "^(\\!|\\!\\=|%|\u0026|\u0026\u0026|\\*|\\+|\\-|/|\\\u003c|\\\u003c\\=|\\=\\=|\\\u003e|\\\u003e\\=|\\^|addw|bitlen|btoi|concat|divmodw|divw|ec_add|ec_map_to|ec_multi_scalar_mul|ec_pairing_check|ec_scalar_mul|ec_subgroup_check|ecdsa_pk_decompress|ecdsa_pk_recover|ecdsa_verify|ed25519verify|ed25519verify_bare|exp|expw|getbit|getbyte|itob|keccak256|len|mulw|setbit|setbyte|sha256|sha3_256|sha512_256|shl|shr|sqrt|vrf_verify|\\||\\|\\||\\~|b\\!\\=|b%|b\\*|b\\+|b\\-|b/|b\\\u003c|b\\\u003c\\=|b\\=\\=|b\\\u003e|b\\\u003e\\=|bsqrt|b\u0026|b\\^|b\\||b\\~|base64_decode|extract|extract3|extract_uint16|extract_uint32|extract_uint64|json_ref|replace2|replace3|substring|substring3|gitxn|gitxna|gitxnas|itxn|itxn_begin|itxn_field|itxn_next|itxn_submit|itxna|itxnas)\\b" } ] }, @@ -112,7 +112,7 @@ }, { "name": "variable.parameter.teal", - "match": "\\b(unknown|pay|keyreg|acfg|axfer|afrz|appl|NoOp|OptIn|CloseOut|ClearState|UpdateApplication|DeleteApplication|Secp256k1|Secp256r1|Sender|Fee|FirstValid|FirstValidTime|LastValid|Note|Lease|Receiver|Amount|CloseRemainderTo|VotePK|SelectionPK|VoteFirst|VoteLast|VoteKeyDilution|Type|TypeEnum|XferAsset|AssetAmount|AssetSender|AssetReceiver|AssetCloseTo|GroupIndex|TxID|ApplicationID|OnCompletion|NumAppArgs|NumAccounts|ApprovalProgram|ClearStateProgram|RekeyTo|ConfigAsset|ConfigAssetTotal|ConfigAssetDecimals|ConfigAssetDefaultFrozen|ConfigAssetUnitName|ConfigAssetName|ConfigAssetURL|ConfigAssetMetadataHash|ConfigAssetManager|ConfigAssetReserve|ConfigAssetFreeze|ConfigAssetClawback|FreezeAsset|FreezeAssetAccount|FreezeAssetFrozen|NumAssets|NumApplications|GlobalNumUint|GlobalNumByteSlice|LocalNumUint|LocalNumByteSlice|ExtraProgramPages|Nonparticipation|NumLogs|CreatedAssetID|CreatedApplicationID|LastLog|StateProofPK|NumApprovalProgramPages|NumClearStateProgramPages|MinTxnFee|MinBalance|MaxTxnLife|ZeroAddress|GroupSize|LogicSigVersion|Round|LatestTimestamp|CurrentApplicationID|CreatorAddress|CurrentApplicationAddress|GroupID|OpcodeBudget|CallerApplicationID|CallerApplicationAddress|AssetCreateMinBalance|AssetOptInMinBalance|ApplicationArgs|Accounts|Assets|Applications|Logs|ApprovalProgramPages|ClearStateProgramPages|URLEncoding|StdEncoding|JSONString|JSONUint64|JSONObject|AssetBalance|AssetFrozen|AssetTotal|AssetDecimals|AssetDefaultFrozen|AssetUnitName|AssetName|AssetURL|AssetMetadataHash|AssetManager|AssetReserve|AssetFreeze|AssetClawback|AssetCreator|AppApprovalProgram|AppClearStateProgram|AppGlobalNumUint|AppGlobalNumByteSlice|AppLocalNumUint|AppLocalNumByteSlice|AppExtraProgramPages|AppCreator|AppAddress|AcctBalance|AcctMinBalance|AcctAuthAddr|AcctTotalNumUint|AcctTotalNumByteSlice|AcctTotalExtraAppPages|AcctTotalAppsCreated|AcctTotalAppsOptedIn|AcctTotalAssetsCreated|AcctTotalAssets|AcctTotalBoxes|AcctTotalBoxBytes|VrfAlgorand|BlkSeed|BlkTimestamp)\\b" + "match": "\\b(unknown|pay|keyreg|acfg|axfer|afrz|appl|NoOp|OptIn|CloseOut|ClearState|UpdateApplication|DeleteApplication|Secp256k1|Secp256r1|Sender|Fee|FirstValid|FirstValidTime|LastValid|Note|Lease|Receiver|Amount|CloseRemainderTo|VotePK|SelectionPK|VoteFirst|VoteLast|VoteKeyDilution|Type|TypeEnum|XferAsset|AssetAmount|AssetSender|AssetReceiver|AssetCloseTo|GroupIndex|TxID|ApplicationID|OnCompletion|NumAppArgs|NumAccounts|ApprovalProgram|ClearStateProgram|RekeyTo|ConfigAsset|ConfigAssetTotal|ConfigAssetDecimals|ConfigAssetDefaultFrozen|ConfigAssetUnitName|ConfigAssetName|ConfigAssetURL|ConfigAssetMetadataHash|ConfigAssetManager|ConfigAssetReserve|ConfigAssetFreeze|ConfigAssetClawback|FreezeAsset|FreezeAssetAccount|FreezeAssetFrozen|NumAssets|NumApplications|GlobalNumUint|GlobalNumByteSlice|LocalNumUint|LocalNumByteSlice|ExtraProgramPages|Nonparticipation|NumLogs|CreatedAssetID|CreatedApplicationID|LastLog|StateProofPK|NumApprovalProgramPages|NumClearStateProgramPages|MinTxnFee|MinBalance|MaxTxnLife|ZeroAddress|GroupSize|LogicSigVersion|Round|LatestTimestamp|CurrentApplicationID|CreatorAddress|CurrentApplicationAddress|GroupID|OpcodeBudget|CallerApplicationID|CallerApplicationAddress|AssetCreateMinBalance|AssetOptInMinBalance|ApplicationArgs|Accounts|Assets|Applications|Logs|ApprovalProgramPages|ClearStateProgramPages|URLEncoding|StdEncoding|JSONString|JSONUint64|JSONObject|AssetBalance|AssetFrozen|AssetTotal|AssetDecimals|AssetDefaultFrozen|AssetUnitName|AssetName|AssetURL|AssetMetadataHash|AssetManager|AssetReserve|AssetFreeze|AssetClawback|AssetCreator|AppApprovalProgram|AppClearStateProgram|AppGlobalNumUint|AppGlobalNumByteSlice|AppLocalNumUint|AppLocalNumByteSlice|AppExtraProgramPages|AppCreator|AppAddress|AcctBalance|AcctMinBalance|AcctAuthAddr|AcctTotalNumUint|AcctTotalNumByteSlice|AcctTotalExtraAppPages|AcctTotalAppsCreated|AcctTotalAppsOptedIn|AcctTotalAssetsCreated|AcctTotalAssets|AcctTotalBoxes|AcctTotalBoxBytes|VrfAlgorand|BlkSeed|BlkTimestamp|BN254g1|BN254g2|BLS12_381g1|BLS12_381g2)\\b" } ] }, diff --git a/data/transactions/msgp_gen.go b/data/transactions/msgp_gen.go index 1cdd1e9701..7cc22db08a 100644 --- a/data/transactions/msgp_gen.go +++ b/data/transactions/msgp_gen.go @@ -405,11 +405,11 @@ func (_ *ApplicationCallTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ApplicationCallTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0006 int @@ -1202,11 +1202,11 @@ func (_ *ApplyData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ApplyData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1429,11 +1429,11 @@ func (_ *AssetConfigTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetConfigTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1581,11 +1581,11 @@ func (_ *AssetFreezeTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetFreezeTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1765,11 +1765,11 @@ func (_ *AssetTransferTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AssetTransferTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1950,11 +1950,11 @@ func (_ *BoxRef) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BoxRef) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -2176,11 +2176,11 @@ func (_ *EvalDelta) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *EvalDelta) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0006 int @@ -2647,11 +2647,11 @@ func (_ *Header) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Header) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -2990,11 +2990,11 @@ func (_ *KeyregTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *KeyregTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3228,11 +3228,11 @@ func (_ *LogicSig) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *LogicSig) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -3465,11 +3465,11 @@ func (_ OnCompletion) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OnCompletion) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -3555,11 +3555,11 @@ func (_ *PaymentTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *PaymentTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -3698,11 +3698,11 @@ func (_ Payset) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Payset) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0002 int var zb0003 bool zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) @@ -3827,11 +3827,11 @@ func (_ *SignedTxn) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SignedTxn) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -4129,11 +4129,11 @@ func (_ *SignedTxnInBlock) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SignedTxnInBlock) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -4553,11 +4553,11 @@ func (_ *SignedTxnWithAD) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *SignedTxnWithAD) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -4859,11 +4859,11 @@ func (_ *StateProofTxnFields) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProofTxnFields) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -5469,11 +5469,11 @@ func (_ *Transaction) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Transaction) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0007 int @@ -6729,11 +6729,11 @@ func (_ *TxGroup) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TxGroup) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int diff --git a/data/transactions/signedtxn.go b/data/transactions/signedtxn.go index 4f0823d7ff..30d0e90e11 100644 --- a/data/transactions/signedtxn.go +++ b/data/transactions/signedtxn.go @@ -71,15 +71,17 @@ func (s SignedTxnInBlock) ID() { // GetEncodedLength returns the length in bytes of the encoded transaction func (s SignedTxn) GetEncodedLength() int { - enc := s.MarshalMsg(protocol.GetEncodingBuf()) - defer protocol.PutEncodingBuf(enc) + buf := protocol.GetEncodingBuf() + enc := s.MarshalMsg(buf.Bytes()) + defer protocol.PutEncodingBuf(buf.Update(enc)) return len(enc) } // GetEncodedLength returns the length in bytes of the encoded transaction func (s SignedTxnInBlock) GetEncodedLength() int { - enc := s.MarshalMsg(protocol.GetEncodingBuf()) - defer protocol.PutEncodingBuf(enc) + buf := protocol.GetEncodingBuf() + enc := s.MarshalMsg(buf.Bytes()) + defer protocol.PutEncodingBuf(buf.Update(enc)) return len(enc) } @@ -116,16 +118,17 @@ func (s *SignedTxnInBlock) ToBeHashed() (protocol.HashID, []byte) { // Hash implements an optimized version of crypto.HashObj(s). func (s *SignedTxnInBlock) Hash() crypto.Digest { - enc := s.MarshalMsg(append(protocol.GetEncodingBuf(), []byte(protocol.SignedTxnInBlock)...)) - defer protocol.PutEncodingBuf(enc) - + buf := protocol.GetEncodingBuf() + enc := s.MarshalMsg(append(buf.Bytes(), []byte(protocol.SignedTxnInBlock)...)) + defer protocol.PutEncodingBuf(buf.Update(enc)) return crypto.Hash(enc) } // HashSHA256 implements an optimized version of crypto.HashObj(s) using SHA256 instead of the default SHA512_256. func (s *SignedTxnInBlock) HashSHA256() crypto.Digest { - enc := s.MarshalMsg(append(protocol.GetEncodingBuf(), []byte(protocol.SignedTxnInBlock)...)) - defer protocol.PutEncodingBuf(enc) + buf := protocol.GetEncodingBuf() + enc := s.MarshalMsg(append(buf.Bytes(), []byte(protocol.SignedTxnInBlock)...)) + defer protocol.PutEncodingBuf(buf.Update(enc)) return sha256.Sum256(enc) } diff --git a/data/transactions/teal.go b/data/transactions/teal.go index 6d09ab3140..85a162854c 100644 --- a/data/transactions/teal.go +++ b/data/transactions/teal.go @@ -89,9 +89,13 @@ func (ed EvalDelta) Equal(o EvalDelta) bool { // tedious) field comparisons. == is not defined on almost any of the // subfields because of slices. func (stx SignedTxn) equal(o SignedTxn) bool { - stxenc := stx.MarshalMsg(protocol.GetEncodingBuf()) - defer protocol.PutEncodingBuf(stxenc) - oenc := o.MarshalMsg(protocol.GetEncodingBuf()) - defer protocol.PutEncodingBuf(oenc) + buf1 := protocol.GetEncodingBuf() + stxenc := stx.MarshalMsg(buf1.Bytes()) + defer protocol.PutEncodingBuf(buf1.Update(stxenc)) + + buf2 := protocol.GetEncodingBuf() + oenc := o.MarshalMsg(buf2.Bytes()) + defer protocol.PutEncodingBuf(buf2.Update(oenc)) + return bytes.Equal(stxenc, oenc) } diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go index c3da26ca55..725f9d7ec2 100644 --- a/data/transactions/transaction.go +++ b/data/transactions/transaction.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "errors" "fmt" + "sync" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -177,31 +178,81 @@ func (tx Transaction) ToBeHashed() (protocol.HashID, []byte) { return protocol.Transaction, protocol.Encode(&tx) } +// txAllocSize returns the max possible size of a transaction without state proof fields. +// It is used to preallocate a buffer for encoding a transaction. +func txAllocSize() int { + return TransactionMaxSize() - StateProofTxnFieldsMaxSize() +} + +// txEncodingPool holds temporary byte slice buffers used for encoding transaction messages. +// Note, it prepends protocol.Transaction tag to the buffer economizing on subsequent append ops. +var txEncodingPool = sync.Pool{ + New: func() interface{} { + size := txAllocSize() + len(protocol.Transaction) + buf := make([]byte, len(protocol.Transaction), size) + copy(buf, []byte(protocol.Transaction)) + return &txEncodingBuf{b: buf} + }, +} + +// getTxEncodingBuf returns a wrapped byte slice that can be used for encoding a +// temporary message. The byte slice length of encoded Transaction{} object. +// The caller gets full ownership of the byte slice, +// but is encouraged to return it using putEncodingBuf(). +func getTxEncodingBuf() *txEncodingBuf { + buf := txEncodingPool.Get().(*txEncodingBuf) + return buf +} + +// putTxEncodingBuf places a byte slice into the pool of temporary buffers +// for encoding. The caller gives up ownership of the byte slice when +// passing it to putTxEncodingBuf(). +func putTxEncodingBuf(buf *txEncodingBuf) { + buf.b = buf.b[:len(protocol.Transaction)] + txEncodingPool.Put(buf) +} + +type txEncodingBuf struct { + b []byte +} + // ID returns the Txid (i.e., hash) of the transaction. func (tx Transaction) ID() Txid { - enc := tx.MarshalMsg(append(protocol.GetEncodingBuf(), []byte(protocol.Transaction)...)) - defer protocol.PutEncodingBuf(enc) + buf := getTxEncodingBuf() + enc := tx.MarshalMsg(buf.b) + if cap(enc) > cap(buf.b) { + // use a bigger buffer as New's estimate was too small + buf.b = enc + } + defer putTxEncodingBuf(buf) return Txid(crypto.Hash(enc)) } // IDSha256 returns the digest (i.e., hash) of the transaction. // This is different from the canonical ID computed with Sum512_256 hashing function. func (tx Transaction) IDSha256() crypto.Digest { - enc := tx.MarshalMsg(append(protocol.GetEncodingBuf(), []byte(protocol.Transaction)...)) - defer protocol.PutEncodingBuf(enc) + buf := getTxEncodingBuf() + enc := tx.MarshalMsg(buf.b) + if cap(enc) > cap(buf.b) { + buf.b = enc + } + defer putTxEncodingBuf(buf) return sha256.Sum256(enc) } // InnerID returns something akin to Txid, but folds in the parent Txid and the // index of the inner call. func (tx Transaction) InnerID(parent Txid, index int) Txid { - input := append(protocol.GetEncodingBuf(), []byte(protocol.Transaction)...) - input = append(input, parent[:]...) - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, uint64(index)) - input = append(input, buf...) + buf := getTxEncodingBuf() + input := append(buf.b, parent[:]...) + var indexBuf [8]byte + binary.BigEndian.PutUint64(indexBuf[:], uint64(index)) + input = append(input, indexBuf[:]...) enc := tx.MarshalMsg(input) - defer protocol.PutEncodingBuf(enc) + if cap(enc) > cap(buf.b) { + buf.b = enc + } + defer putTxEncodingBuf(buf) return Txid(crypto.Hash(enc)) } diff --git a/docker/README.md b/docker/README.md index d218b10139..e86729b604 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,7 +6,7 @@ General purpose algod container image. ## Image Configuration -Algorand maintains a Docker image with recent snapshot builds from our `master` branch on DockerHub to support users who prefer to run containerized processes. There are a couple of different images avaliable for running the latest stable or development versions of Algod. +Algorand maintains a Docker image with recent snapshot builds from our `master` branch on DockerHub to support users who prefer to run containerized processes. There are a couple of different images available for running the latest stable or development versions of Algod. - `algorand/algod:latest` is the latest stable release version of Algod (default) - `algorand/algod:stable` is the latest stable version of Algod @@ -60,7 +60,8 @@ Configuration can be modified by specifying certain files. These can be changed | /etc/algorand/algod.token | Override default randomized REST API token. | | /etc/algorand/algod.admin.token | Override default randomized REST API admin token. | | /etc/algorand/logging.config | Use a custom [logging.config](https://developer.algorand.org/docs/run-a-node/reference/telemetry-config/#configuration) file for configuring telemetry. | - | /etc/algorand/template.json | Override default private network topology. One of the nodes in the template must be named "data".| +| /etc/algorand/template.json | Override default private network topology. One of the nodes in the template must be named "data". | +| /etc/algorand/keys/ | Override this directory to provide pregenerated private network data. | ## Example Configuration @@ -117,3 +118,28 @@ On the host system, ensure the directory being mounted uses UID=999 and GID=999. Private networks work a little bit differently. They are configured with, potentially, several data directories. The default topology supplied with this container is installed to `/algod/`, and has a single node named `data`. This means the private network has a data directory at `/algod/data`, matching the production configuration. Because the root directory contains some metadata, if persistence of the private network is required, you should mount the volume `/algod/` instead of `/algod/data`. This will ensure the extra metadata is included when changing images. + +## Faster Private Network Startup + +Generating participation keys may take several minutes. By creating them ahead of time a new private network can be started more quickly. These keys can be reused for multiple networks. + +Note that you must provide a template.json file for this operation. [You can find a template here](https://github.com/algorand/go-algorand/blob/master/docker/files/run/devmode_template.json), be sure to replace `NUM_ROUNDS` with your desired number of rounds, such as 3000000. + +Use the `goal network pregen` command to generate the files in a mounted directory: +```bash +docker run --rm -it \ + --name pregen \ + -v /path/to/your/template.json:/etc/algorand/template.json \ + -v $(pwd)/pregen:/algod/pregen \ + --entrypoint "/node/bin/goal" \ + algorand/algod:stable network pregen -t /etc/algorand/template.json -p /algod/pregen +``` + +You will now have a local directory named `pregen` which can be mounted the next time you want to start a network with this template: +```bash +docker run --rm -it --name algod-pregen-run \ + -p 4190:8080 \ + -v /tmp/big_keys.json:/etc/algorand/template.json \ + -v $(pwd)/pregen:/etc/algorand/keys \ + algorand/algod:stable +``` diff --git a/docker/build/Dockerfile-deploy b/docker/build/Dockerfile-deploy index d01b505f41..ac1fb5b4e0 100644 --- a/docker/build/Dockerfile-deploy +++ b/docker/build/Dockerfile-deploy @@ -1,7 +1,7 @@ FROM --platform=linux/amd64 ubuntu:20.04 ARG GOLANG_VERSION -RUN apt-get update && apt-get install -y git wget autoconf jq bsdmainutils shellcheck +RUN apt-get update && apt-get install -y git wget autoconf jq bsdmainutils shellcheck make python3 libtool g++ WORKDIR /root RUN wget --quiet https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz && tar -xvf go${GOLANG_VERSION}.linux-amd64.tar.gz && mv go /usr/local ENV GOROOT=/usr/local/go \ diff --git a/docker/build/cicd.centos8.Dockerfile b/docker/build/cicd.centos8.Dockerfile index 28ec634841..76ec3e9cc5 100644 --- a/docker/build/cicd.centos8.Dockerfile +++ b/docker/build/cicd.centos8.Dockerfile @@ -3,7 +3,8 @@ ARG ARCH="amd64" FROM quay.io/centos/centos:stream8 ARG GOLANG_VERSION ARG ARCH="amd64" -RUN dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ +RUN dnf update rpm -y && \ + dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ dnf update -y && \ dnf install -y autoconf wget awscli git gnupg2 nfs-utils python3-devel expect jq \ libtool gcc-c++ libstdc++-devel rpmdevtools createrepo rpm-sign bzip2 which \ diff --git a/docker/files/run/run.sh b/docker/files/run/run.sh index eef16959fe..29b69423dc 100755 --- a/docker/files/run/run.sh +++ b/docker/files/run/run.sh @@ -27,15 +27,8 @@ fi #################### function catchup() { - local FAST_CATCHUP_URL="https://algorand-catchpoints.s3.us-east-2.amazonaws.com/channel/CHANNEL/latest.catchpoint" - local CATCHPOINT=$(curl -s ${FAST_CATCHUP_URL/CHANNEL/$NETWORK}) - if [[ "$(echo $CATCHPOINT | wc -l | tr -d ' ')" != "1" ]]; then - echo "Problem starting fast catchup." - exit 1 - fi - sleep 5 - goal node catchup "$CATCHPOINT" + goal node catchup --force --min 1000000 } function start_public_network() { @@ -87,6 +80,7 @@ function configure_data_dir() { fi algocfg -d . set -p EndpointAddress -v "0.0.0.0:${ALGOD_PORT}" + algocfg -d . set -p NodeExporterPath -v "$(which node_exporter)" # set token overrides for dir in ${ALGORAND_DATA}/../*/; do @@ -102,7 +96,7 @@ function configure_data_dir() { if [ "$TELEMETRY_NAME" != "" ]; then diagcfg telemetry name -n "$TELEMETRY_NAME" -d "$ALGORAND_DATA" diagcfg telemetry enable -d "$ALGORAND_DATA" - else + elif ! [ -f "/etc/algorand/logging.config" ]; then diagcfg telemetry disable fi @@ -190,7 +184,16 @@ function start_new_private_network() { fi fi sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "/node/run/$TEMPLATE" - goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE" + + # Check if keys are mounted, and if so, copy them over + # Use pregen keys in network create command + if [ -d "/etc/algorand/keys" ]; then + cp -r /etc/algorand/keys /node/run/keys + goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE" -p "/node/run/keys" + else + goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE" + fi + configure_data_dir start_private_network } diff --git a/go.mod b/go.mod index fb1fa811ef..b19e61cb69 100644 --- a/go.mod +++ b/go.mod @@ -2,23 +2,21 @@ module github.com/algorand/go-algorand go 1.20 -replace github.com/algorand/msgp => ./msgp - require ( github.com/DataDog/zstd v1.5.2 github.com/algorand/avm-abi v0.2.0 github.com/algorand/falcon v0.1.0 github.com/algorand/go-codec/codec v1.1.10 - github.com/algorand/go-deadlock v0.2.2 + github.com/algorand/go-deadlock v0.2.3 github.com/algorand/go-sumhash v0.1.0 github.com/algorand/graphtrace v0.1.0 - github.com/algorand/msgp v1.1.55 + github.com/algorand/msgp v1.1.60 github.com/algorand/oapi-codegen v1.12.0-algorand.0 github.com/algorand/sortition v1.0.0 github.com/algorand/websocket v1.4.6 - github.com/aws/aws-sdk-go v1.33.0 + github.com/aws/aws-sdk-go v1.34.0 github.com/cockroachdb/pebble v0.0.0-20230807162746-af8c5f279001 - github.com/consensys/gnark-crypto v0.7.0 + github.com/consensys/gnark-crypto v0.12.0 github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c github.com/dchest/siphash v1.2.1 github.com/fatih/color v1.13.0 @@ -32,57 +30,89 @@ require ( github.com/karalabe/usb v0.0.2 github.com/labstack/echo/v4 v4.9.1 github.com/libp2p/go-libp2p v0.29.1 + github.com/libp2p/go-libp2p-pubsub v0.9.3 + github.com/libp2p/go-yamux/v4 v4.0.1 github.com/mattn/go-sqlite3 v1.14.16 github.com/miekg/dns v1.1.55 github.com/multiformats/go-multiaddr v0.10.1 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/olivere/elastic v6.2.14+incompatible github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.3.0 + github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.11.0 + golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/sys v0.10.0 - golang.org/x/text v0.11.0 + golang.org/x/sys v0.13.0 + golang.org/x/text v0.13.0 gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 pgregory.net/rapid v0.6.2 ) require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/errors v1.8.1 // indirect github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect github.com/cockroachdb/redact v1.0.8 // indirect github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.2 // indirect + github.com/flynn/noise v1.0.0 // indirect github.com/fortytw2/leaktest v1.3.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/swag v0.19.5 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect + github.com/huin/goupnp v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/invopop/yaml v0.1.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/kr/pretty v0.2.1 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.1.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-nat v0.2.0 // indirect + github.com/libp2p/go-netroute v0.2.1 // indirect + github.com/libp2p/go-reuseport v0.3.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect @@ -95,6 +125,9 @@ require ( github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect + github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -102,6 +135,13 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-19 v0.3.3 // indirect + github.com/quic-go/qtls-go1-20 v0.2.3 // indirect + github.com/quic-go/quic-go v0.36.3 // indirect + github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -109,16 +149,19 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/fx v1.20.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/term v0.10.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/term v0.13.0 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect golang.org/x/tools v0.11.0 // indirect google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index f727495411..91453673df 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -13,20 +15,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -35,7 +23,6 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -45,18 +32,21 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= @@ -71,43 +61,41 @@ github.com/algorand/falcon v0.1.0 h1:xl832kfZ7hHG6B4p90DQynjfKFGbIUgUOnsRiMZXfAo github.com/algorand/falcon v0.1.0/go.mod h1:OkQyHlGvS0kLNcIWbC21/uQcnbfwSOQm+wiqWwBG9pQ= 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/go-deadlock v0.2.2 h1:L7AKATSUCzoeVuOgpTipfCEjdUu5ECmlje8R7lP9DOY= -github.com/algorand/go-deadlock v0.2.2/go.mod h1:Hat1OXKqKNUcN/iv74FjGhF4hsOE2l7gOgQ9ZVIq6Fk= +github.com/algorand/go-deadlock v0.2.3 h1:ek9rjUyUF1HhUm0I2DyaCN8+3S850ONJNl5jQr9kZOA= +github.com/algorand/go-deadlock v0.2.3/go.mod h1:Gli2d0Cb7kgXzSpJLC4Vn0DCLgjNVi6fNldY/mOtO/U= github.com/algorand/go-sumhash v0.1.0 h1:b/QRhyLuF//vOcicBIxBXYW8bERNoeLxieht/dUYpVg= github.com/algorand/go-sumhash v0.1.0/go.mod h1:OOe7jdDWUhLkuP1XytkK5gnLu9entAviN5DfDZh6XAc= github.com/algorand/graphtrace v0.1.0 h1:QemP1iT0W56SExD0NfiU6rsG34/v0Je6bg5UZnptEUM= github.com/algorand/graphtrace v0.1.0/go.mod h1:HscLQrzBdH1BH+5oehs3ICd8SYcXvnSL9BjfTu8WHCc= -github.com/algorand/msgp v1.1.55 h1:kWc9Xc08xtxCTWUiq1cRW5XGF+DFcfSGihYf0IZ/ivs= -github.com/algorand/msgp v1.1.55/go.mod h1:RqZQBzAFDWpwh5TlabzZkWy+6kwL9cvXfLbU0gD99EA= +github.com/algorand/msgp v1.1.60 h1:+IVUC34+tSj1P2M1mkYtl4GLyfzdzXfBLSw6TDT19M8= +github.com/algorand/msgp v1.1.60/go.mod h1:RqZQBzAFDWpwh5TlabzZkWy+6kwL9cvXfLbU0gD99EA= github.com/algorand/oapi-codegen v1.12.0-algorand.0 h1:W9PvED+wAJc+9EeXPONnA+0zE9UhynEqoDs4OgAxKhk= github.com/algorand/oapi-codegen v1.12.0-algorand.0/go.mod h1:tIWJ9K/qrLDVDt5A1p82UmxZIEGxv2X+uoujdhEAL48= github.com/algorand/sortition v1.0.0 h1:PJiZtdSTBm4nArQrZXBnhlljHXhuyAXRJBqVWowQu3E= github.com/algorand/sortition v1.0.0/go.mod h1:23CZwAbTWPv0bBsq+Php/2J6Y/iXDyzlfcZyepeY5Fo= github.com/algorand/websocket v1.4.6 h1:I0kV4EYwatuUrKtNiwzYYgojgwh6pksDmlqntKG2Woc= github.com/algorand/websocket v1.4.6/go.mod h1:HJmdGzFtnlUQ4nTzZP6WrT29oGYf1t6Ybi64vROcT+M= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.33.0 h1:Bq5Y6VTLbfnJp1IV8EL/qUU5qO1DYHda/zis/sqevkY= -github.com/aws/aws-sdk-go v1.33.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.0 h1:brux2dRrlwCF5JhTL7MUT3WUwo9zfDHZZp3+g3Mvlmo= +github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -116,19 +104,9 @@ github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz7 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= @@ -145,16 +123,25 @@ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6 h1:DJK8W/iB+s/qkTtmXSrHA49lp5O3OsR7E6z4byOLy34= github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/gnark-crypto v0.7.0 h1:rwdy8+ssmLYRqKp+ryRRgQJl/rCq2uv+n83cOydm5UE= -github.com/consensys/gnark-crypto v0.7.0/go.mod h1:KPSuJzyxkJA8xZ/+CV47tyqkr9MmpZA3PXivK4VPrVg= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.0 h1:1OnSpOykNkUIBIBJKdhwy2p0JlW5o+Az02ICzZmvvdg= +github.com/consensys/gnark-crypto v0.12.0/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -169,38 +156,40 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3 github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getkin/kin-openapi v0.107.0 h1:bxhL6QArW7BXQj8NjXfIJQy680NsMKd25nwhvpCXchg= github.com/getkin/kin-openapi v0.107.0/go.mod h1:9Dhr+FasATJZjS4iOLvB0hkaxgYdulrNYm2e9epLWOo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -215,6 +204,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -224,10 +214,15 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.7.0 h1:pGFUjl501gafK9HBt1VGL1KCOd/YhIooID+xgyJCf3g= github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -243,7 +238,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -251,7 +246,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -268,11 +263,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -286,19 +279,18 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -306,63 +298,35 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= +github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -370,12 +334,19 @@ github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= @@ -384,7 +355,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -416,13 +386,15 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -437,29 +409,44 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= github.com/libp2p/go-libp2p v0.29.1 h1:yNeg6XgP8gbdc4YSrwiIt5T1TGOrVjH8dzl8h0GIOfQ= github.com/libp2p/go-libp2p v0.29.1/go.mod h1:20El+LLy3/YhdUYIvGbLnvVJN32nMdqY6KXBENRAfLY= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= +github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= +github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= +github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= +github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= +github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= +github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= +github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= @@ -473,22 +460,23 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -532,34 +520,39 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/olivere/elastic v6.2.14+incompatible h1:k+KadwNP/dkXE0/eu+T6otk1+5fe0tEpPyQJ4XVm5i8= github.com/olivere/elastic v6.2.14+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -571,58 +564,89 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= +github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= +github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.36.3 h1:f+yOqeGhMoRX7/M3wmEw/djhzKWr15FtQysox85/834= +github.com/quic-go/quic-go v0.36.3/go.mod h1:qxQumdeKw5GmWs1OsTZZnOxzSI+RJWuhf1O8FN35L2o= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0= -github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -639,10 +663,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -651,6 +675,8 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -665,46 +691,45 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= +go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -719,6 +744,7 @@ golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xpp golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -729,8 +755,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -739,22 +763,21 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -765,7 +788,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -782,40 +804,25 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -828,16 +835,18 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -848,11 +857,8 @@ golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -877,65 +883,47 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -952,7 +940,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -981,17 +968,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= @@ -1000,6 +978,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1016,31 +997,20 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1063,47 +1033,16 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1116,22 +1055,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1144,7 +1067,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -1157,14 +1079,13 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 h1:q/fZgS8MMadqFFGa8WL4Oyz+TmjiZfi8UrzWhTl8d5w= gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009/go.mod h1:O0bY1e/dSoxMYZYTHP0SWKxG5EWLEvKR9/cOjWPPMKU= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1176,6 +1097,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1190,3 +1113,7 @@ pgregory.net/rapid v0.6.2/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/installer/config.json.example b/installer/config.json.example index 094c7ca7df..fccf558c44 100644 --- a/installer/config.json.example +++ b/installer/config.json.example @@ -1,5 +1,5 @@ { - "Version": 30, + "Version": 31, "AccountUpdatesStatsInterval": 5000000000, "AccountsRebuildSynchronousMode": 1, "AgreementIncomingBundlesQueueLength": 15, @@ -8,11 +8,13 @@ "AnnounceParticipationKey": true, "Archival": false, "BaseLoggerDebugLevel": 4, + "BlockDBDir": "", "BlockServiceCustomFallbackEndpoints": "", "BlockServiceMemCap": 500000000, "BroadcastConnectionsLimit": -1, "CadaverDirectory": "", "CadaverSizeTarget": 0, + "CatchpointDir": "", "CatchpointFileHistoryLength": 365, "CatchpointInterval": 10000, "CatchpointTracking": 0, @@ -23,8 +25,10 @@ "CatchupHTTPBlockFetchTimeoutSec": 4, "CatchupLedgerDownloadRetryAttempts": 50, "CatchupParallelBlocks": 16, + "ColdDataDir": "", "ConnectionsRateLimitingCount": 60, "ConnectionsRateLimitingWindowSeconds": 1, + "CrashDBDir": "", "DNSBootstrapID": ".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)", "DNSSecurityFlags": 1, "DeadlockDetection": 0, @@ -39,7 +43,7 @@ "EnableAgreementTimeMetrics": false, "EnableAssembleStats": false, "EnableBlockService": false, - "EnableBlockServiceFallbackToArchiver": true, + "EnableBlockServiceFallbackToArchiver": false, "EnableCatchupFromArchiveServers": false, "EnableDeveloperAPI": false, "EnableExperimentalAPI": false, @@ -49,6 +53,7 @@ "EnableLedgerService": false, "EnableMetricReporting": false, "EnableOutgoingNetworkMessageFiltering": true, + "EnableP2P": false, "EnablePingHandler": true, "EnableProcessBlockStats": false, "EnableProfiler": false, @@ -65,16 +70,20 @@ "ForceRelayMessages": false, "GossipFanout": 4, "HeartbeatUpdateInterval": 600, + "HotDataDir": "", "IncomingConnectionsLimit": 2400, "IncomingMessageFilterBucketCount": 5, "IncomingMessageFilterBucketSize": 512, "LedgerSynchronousMode": 2, + "LogArchiveDir": "", "LogArchiveMaxAge": "", "LogArchiveName": "node.archive.log", + "LogFileDir": "", "LogSizeLimit": 1073741824, "MaxAPIBoxPerApplication": 100000, "MaxAPIResourcesPerAccount": 100000, "MaxAcctLookback": 4, + "MaxBlockHistoryLookback": 0, "MaxCatchpointDownloadDuration": 43200000000000, "MaxConnectionsPerIP": 15, "MinCatchpointFileDownloadBytesPerSecond": 20480, @@ -86,8 +95,7 @@ "OptimizeAccountsDatabaseOnStartup": false, "OutgoingMessageFilterBucketCount": 3, "OutgoingMessageFilterBucketSize": 128, - "P2PEnable": false, - "P2PPersistPeerID": true, + "P2PPersistPeerID": false, "P2PPrivateKeyLocation": "", "ParticipationKeysRefreshInterval": 60000000000, "PeerConnectionsUpdateInterval": 3600, @@ -102,12 +110,14 @@ "RestReadTimeoutSeconds": 15, "RestWriteTimeoutSeconds": 120, "RunHosted": false, + "StateproofDir": "", "StorageEngine": "sqlite", "SuggestedFeeBlockHistory": 3, "SuggestedFeeSlidingWindowSize": 50, "TLSCertFile": "", "TLSKeyFile": "", "TelemetryToLog": true, + "TrackerDBDir": "", "TransactionSyncDataExchangeRate": 0, "TransactionSyncSignificantMessageThreshold": 0, "TxBacklogReservedCapacityPerPeer": 20, diff --git a/ledger/acctonline_test.go b/ledger/acctonline_test.go index 1aaf7080c5..2897e87ffe 100644 --- a/ledger/acctonline_test.go +++ b/ledger/acctonline_test.go @@ -1605,8 +1605,6 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { postCommitUnlockedReleaseLock: make(chan struct{}), postCommitEntryLock: make(chan struct{}), postCommitReleaseLock: make(chan struct{}), - alwaysLock: false, - shouldLockPostCommit: false, } conf := config.GetDefaultLocal() @@ -1632,7 +1630,7 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } - stallingTracker.shouldLockPostCommit = true + stallingTracker.shouldLockPostCommit.Store(true) updateAccountsRoutine := func() { var updates ledgercore.AccountDeltas @@ -1698,8 +1696,6 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { postCommitUnlockedReleaseLock: make(chan struct{}), postCommitEntryLock: make(chan struct{}), postCommitReleaseLock: make(chan struct{}), - alwaysLock: false, - shouldLockPostCommit: false, } conf := config.GetDefaultLocal() @@ -1725,7 +1721,7 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } - stallingTracker.shouldLockPostCommit = true + stallingTracker.shouldLockPostCommit.Store(true) updateAccountsRoutine := func() { var updates ledgercore.AccountDeltas diff --git a/ledger/acctupdates.go b/ledger/acctupdates.go index d6876f13e9..ca9cd55f72 100644 --- a/ledger/acctupdates.go +++ b/ledger/acctupdates.go @@ -93,7 +93,7 @@ const initializingAccountCachesMessageTimeout = 3 * time.Second // where we end up batching up to 1000 rounds in a single update. const accountsUpdatePerRoundHighWatermark = 1 * time.Second -// forceCatchpointFileGeneration defines the CatchpointTracking mode that would be used to +// forceCatchpointFileGenerationTrackingMode defines the CatchpointTracking mode that would be used to // force a node to generate catchpoint files. const forceCatchpointFileGenerationTrackingMode = 99 @@ -692,6 +692,13 @@ func (au *accountUpdates) LatestTotals() (basics.Round, ledgercore.AccountTotals return au.latestTotalsImpl() } +// Totals returns the totals of all accounts for the given round +func (au *accountUpdates) Totals(rnd basics.Round) (ledgercore.AccountTotals, error) { + au.accountsMu.RLock() + defer au.accountsMu.RUnlock() + return au.totalsImpl(rnd) +} + // ReadCloseSizer interface implements the standard io.Reader and io.Closer as well // as supporting the Size() function that let the caller know what the size of the stream would be (in bytes). type ReadCloseSizer interface { @@ -722,6 +729,15 @@ func (au *accountUpdates) latestTotalsImpl() (basics.Round, ledgercore.AccountTo return rnd, au.roundTotals[offset], nil } +// totalsImpl returns the totals of all accounts for the given round +func (au *accountUpdates) totalsImpl(rnd basics.Round) (ledgercore.AccountTotals, error) { + offset, err := au.roundOffset(rnd) + if err != nil { + return ledgercore.AccountTotals{}, err + } + return au.roundTotals[offset], nil +} + // initializeFromDisk performs the atomic operation of loading the accounts data information from disk // and preparing the accountUpdates for operation. func (au *accountUpdates) initializeFromDisk(l ledgerForTracker, lastBalancesRound basics.Round) error { diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index b900f3e7dd..1f3ca086a2 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -2192,8 +2192,8 @@ func testAcctUpdatesLookupRetry(t *testing.T, assertFn func(au *accountUpdates, postCommitUnlockedReleaseLock: make(chan struct{}), postCommitEntryLock: make(chan struct{}), postCommitReleaseLock: make(chan struct{}), - alwaysLock: true, } + stallingTracker.alwaysLock.Store(true) ml.trackers.trackers = append([]ledgerTracker{stallingTracker}, ml.trackers.trackers...) // kick off another round diff --git a/ledger/catchpointtracker.go b/ledger/catchpointtracker.go index 7e59406025..d3bf1f87ea 100644 --- a/ledger/catchpointtracker.go +++ b/ledger/catchpointtracker.go @@ -90,7 +90,9 @@ func catchpointStage1Decoder(r io.Reader) (io.ReadCloser, error) { } type catchpointTracker struct { - // dbDirectory is the directory where the ledger and block sql file resides as well as the parent directory for the catchup files to be generated + // tmpDir is the path to the currently building catchpoint file + tmpDir string + // dbDirectory is the path to the finished/cold data of catchpoint dbDirectory string // catchpointInterval is the configured interval at which the catchpointTracker would generate catchpoint labels and catchpoint files. @@ -155,37 +157,22 @@ type catchpointTracker struct { } // initialize initializes the catchpointTracker structure -func (ct *catchpointTracker) initialize(cfg config.Local, dbPathPrefix string) { - ct.dbDirectory = filepath.Dir(dbPathPrefix) +func (ct *catchpointTracker) initialize(cfg config.Local, paths DirsAndPrefix) { + // catchpoint uses the cold data directories, except for the temp file + ct.dbDirectory = paths.CatchpointGenesisDir + // the temp file uses the hot data directories + ct.tmpDir = paths.HotGenesisDir - switch cfg.CatchpointTracking { - case -1: - // No catchpoints. - default: - // Give a warning, then fall through to case 0. - logging.Base().Warnf("catchpointTracker: the CatchpointTracking field in the config.json file contains an invalid value (%d). The default value of 0 would be used instead.", cfg.CatchpointTracking) - fallthrough - case 0: - if cfg.Archival && (cfg.CatchpointInterval > 0) { - ct.catchpointInterval = cfg.CatchpointInterval - ct.enableGeneratingCatchpointFiles = true - } - case 1: - if cfg.CatchpointInterval > 0 { - ct.catchpointInterval = cfg.CatchpointInterval - ct.enableGeneratingCatchpointFiles = cfg.Archival - } - case 2: - if cfg.CatchpointInterval > 0 { - ct.catchpointInterval = cfg.CatchpointInterval - ct.enableGeneratingCatchpointFiles = true - } - case forceCatchpointFileGenerationTrackingMode: - if cfg.CatchpointInterval > 0 { - ct.catchpointInterval = cfg.CatchpointInterval - ct.enableGeneratingCatchpointFiles = true - ct.forceCatchpointFileWriting = true - } + if cfg.TracksCatchpoints() { + ct.catchpointInterval = cfg.CatchpointInterval + } + ct.enableGeneratingCatchpointFiles = cfg.StoresCatchpoints() + + // Overwrite previous options if forceCatchpointFileGenerationTrackingMode + if cfg.CatchpointTracking == forceCatchpointFileGenerationTrackingMode && cfg.CatchpointInterval > 0 { + ct.catchpointInterval = cfg.CatchpointInterval + ct.forceCatchpointFileWriting = true + ct.enableGeneratingCatchpointFiles = true } ct.catchpointFileHistoryLength = cfg.CatchpointFileHistoryLength @@ -281,10 +268,8 @@ func (ct *catchpointTracker) finishFirstStageAfterCrash(dbRound basics.Round) er } // First, delete the unfinished data file. - relCatchpointDataFilePath := filepath.Join( - trackerdb.CatchpointDirName, - makeCatchpointDataFilePath(dbRound)) - err = trackerdb.RemoveSingleCatchpointFileFromDisk(ct.dbDirectory, relCatchpointDataFilePath) + relCatchpointDataFilePath := filepath.Join(trackerdb.CatchpointDirName, makeCatchpointDataFilePath(dbRound)) + err = trackerdb.RemoveSingleCatchpointFileFromDisk(ct.tmpDir, relCatchpointDataFilePath) if err != nil { return err } @@ -300,9 +285,7 @@ func (ct *catchpointTracker) finishCatchpointsAfterCrash(catchpointLookback uint for _, record := range records { // First, delete the unfinished catchpoint file. - relCatchpointFilePath := filepath.Join( - trackerdb.CatchpointDirName, - trackerdb.MakeCatchpointFilePath(basics.Round(record.Round))) + relCatchpointFilePath := filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(basics.Round(record.Round))) err = trackerdb.RemoveSingleCatchpointFileFromDisk(ct.dbDirectory, relCatchpointFilePath) if err != nil { return err @@ -776,7 +759,7 @@ func (ct *catchpointTracker) createCatchpoint(ctx context.Context, accountsRound return nil } - catchpointDataFilePath := filepath.Join(ct.dbDirectory, trackerdb.CatchpointDirName) + catchpointDataFilePath := filepath.Join(ct.tmpDir, trackerdb.CatchpointDirName) catchpointDataFilePath = filepath.Join(catchpointDataFilePath, makeCatchpointDataFilePath(accountsRound)) @@ -802,8 +785,8 @@ func (ct *catchpointTracker) createCatchpoint(ctx context.Context, accountsRound BlockHeaderDigest: blockHash, } - relCatchpointFilePath := - filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(round)) + relCatchpointFilePath := filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(round)) + absCatchpointFilePath := filepath.Join(ct.dbDirectory, relCatchpointFilePath) err = os.MkdirAll(filepath.Dir(absCatchpointFilePath), 0700) @@ -842,6 +825,7 @@ func (ct *catchpointTracker) createCatchpoint(ctx context.Context, accountsRound With("accountsCount", dataInfo.TotalAccounts). With("kvsCount", dataInfo.TotalKVs). With("fileSize", fileInfo.Size()). + With("filepath", relCatchpointFilePath). With("catchpointLabel", label). Infof("Catchpoint file was created") @@ -1149,9 +1133,8 @@ func (ct *catchpointTracker) generateCatchpointData(ctx context.Context, account startTime := time.Now() - catchpointDataFilePath := filepath.Join(ct.dbDirectory, trackerdb.CatchpointDirName) - catchpointDataFilePath = - filepath.Join(catchpointDataFilePath, makeCatchpointDataFilePath(accountsRound)) + catchpointDataFilePath := filepath.Join(ct.tmpDir, trackerdb.CatchpointDirName) + catchpointDataFilePath = filepath.Join(catchpointDataFilePath, makeCatchpointDataFilePath(accountsRound)) more := true const shortChunkExecutionDuration = 50 * time.Millisecond @@ -1399,8 +1382,7 @@ func (ct *catchpointTracker) GetCatchpointStream(round basics.Round) (ReadCloseS } // if the database doesn't know about that round, see if we have that file anyway: - relCatchpointFilePath := - filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(round)) + relCatchpointFilePath := filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(round)) absCatchpointFilePath := filepath.Join(ct.dbDirectory, relCatchpointFilePath) file, err := os.OpenFile(absCatchpointFilePath, os.O_RDONLY, 0666) if err == nil && file != nil { diff --git a/ledger/catchpointtracker_test.go b/ledger/catchpointtracker_test.go index d5ed7621e8..4e14a1ab1d 100644 --- a/ledger/catchpointtracker_test.go +++ b/ledger/catchpointtracker_test.go @@ -67,7 +67,13 @@ func newCatchpointTracker(tb testing.TB, l *mockLedgerForTracker, conf config.Lo ct := &catchpointTracker{} ao := &onlineAccounts{} au.initialize(conf) - ct.initialize(conf, dbPathPrefix) + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: dbPathPrefix, + HotGenesisDir: dbPathPrefix, + }, + } + ct.initialize(conf, paths) ao.initialize(conf) _, err := trackerDBInitialize(l, ct.catchpointEnabled(), dbPathPrefix) require.NoError(tb, err) @@ -100,6 +106,7 @@ func TestCatchpointGetCatchpointStream(t *testing.T) { require.NoError(t, err) ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory // Create the catchpoint files with dummy data for i := 0; i < filesToCreate; i++ { @@ -169,6 +176,7 @@ func TestCatchpointsDeleteStored(t *testing.T) { ct := newCatchpointTracker(t, ml, conf, ".") defer ct.close() ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory dummyCatchpointFilesToCreate := 42 @@ -245,9 +253,16 @@ func TestCatchpointsDeleteStoredOnSchemaUpdate(t *testing.T) { ct := &catchpointTracker{} conf := config.GetDefaultLocal() conf.CatchpointInterval = 1 - ct.initialize(conf, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(conf, paths) defer ct.close() ct.dbDirectory = temporaryDirectroy + ct.tmpDir = temporaryDirectroy _, err = trackerDBInitialize(ml, true, ct.dbDirectory) require.NoError(t, err) @@ -301,9 +316,16 @@ func TestRecordCatchpointFile(t *testing.T) { conf.CatchpointFileHistoryLength = 3 conf.Archival = true - ct.initialize(conf, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(conf, paths) defer ct.close() ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory _, err := trackerDBInitialize(ml, true, ct.dbDirectory) require.NoError(t, err) @@ -358,9 +380,17 @@ func TestCatchpointCommitErrorHandling(t *testing.T) { conf := config.GetDefaultLocal() conf.Archival = true - ct.initialize(conf, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(conf, paths) + defer ct.close() ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory _, err := trackerDBInitialize(ml, true, ct.dbDirectory) require.NoError(t, err) @@ -439,9 +469,17 @@ func TestCatchpointFileWithLargeSpVerification(t *testing.T) { conf := config.GetDefaultLocal() conf.Archival = true - ct.initialize(conf, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(conf, paths) + defer ct.close() ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory _, err := trackerDBInitialize(ml, true, ct.dbDirectory) require.NoError(t, err) @@ -503,7 +541,13 @@ func BenchmarkLargeCatchpointDataWriting(b *testing.B) { cfg := config.GetDefaultLocal() cfg.Archival = true ct := catchpointTracker{} - ct.initialize(cfg, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(cfg, paths) temporaryDirectroy := b.TempDir() catchpointsDirectory := filepath.Join(temporaryDirectroy, trackerdb.CatchpointDirName) @@ -511,6 +555,7 @@ func BenchmarkLargeCatchpointDataWriting(b *testing.B) { require.NoError(b, err) ct.dbDirectory = temporaryDirectroy + ct.tmpDir = temporaryDirectroy err = ct.loadFromDisk(ml, 0) require.NoError(b, err) @@ -733,9 +778,9 @@ type blockingTracker struct { postCommitEntryLock chan struct{} postCommitReleaseLock chan struct{} committedUpToRound int64 - alwaysLock bool - shouldLockPostCommit bool - shouldLockPostCommitUnlocked bool + alwaysLock atomic.Bool + shouldLockPostCommit atomic.Bool + shouldLockPostCommitUnlocked atomic.Bool } // loadFromDisk is not implemented in the blockingTracker. @@ -770,7 +815,7 @@ func (bt *blockingTracker) commitRound(context.Context, trackerdb.TransactionSco // postCommit implements entry/exit blockers, designed for testing. func (bt *blockingTracker) postCommit(ctx context.Context, dcc *deferredCommitContext) { - if bt.alwaysLock || dcc.catchpointFirstStage || bt.shouldLockPostCommit { + if bt.alwaysLock.Load() || dcc.catchpointFirstStage || bt.shouldLockPostCommit.Load() { bt.postCommitEntryLock <- struct{}{} <-bt.postCommitReleaseLock } @@ -778,7 +823,7 @@ func (bt *blockingTracker) postCommit(ctx context.Context, dcc *deferredCommitCo // postCommitUnlocked implements entry/exit blockers, designed for testing. func (bt *blockingTracker) postCommitUnlocked(ctx context.Context, dcc *deferredCommitContext) { - if bt.alwaysLock || dcc.catchpointFirstStage || bt.shouldLockPostCommitUnlocked { + if bt.alwaysLock.Load() || dcc.catchpointFirstStage || bt.shouldLockPostCommitUnlocked.Load() { bt.postCommitUnlockedEntryLock <- struct{}{} <-bt.postCommitUnlockedReleaseLock } @@ -959,8 +1004,8 @@ func TestCatchpointTrackerWaitNotBlocking(t *testing.T) { writeStallingTracker := &blockingTracker{ postCommitUnlockedEntryLock: make(chan struct{}), postCommitUnlockedReleaseLock: make(chan struct{}), - shouldLockPostCommitUnlocked: true, } + writeStallingTracker.shouldLockPostCommitUnlocked.Store(true) ledger.trackerMu.Lock() ledger.trackers.mu.Lock() ledger.trackers.trackers = append(ledger.trackers.trackers, writeStallingTracker) @@ -987,7 +1032,7 @@ func TestCatchpointTrackerWaitNotBlocking(t *testing.T) { // consume to unblock <-writeStallingTracker.postCommitUnlockedEntryLock // disable further blocking - writeStallingTracker.shouldLockPostCommitUnlocked = false + writeStallingTracker.shouldLockPostCommitUnlocked.Store(false) // wait the writeStallingTracker.postCommitUnlockedReleaseLock passes wg.Wait() @@ -1148,6 +1193,7 @@ func TestCatchpointFirstStageInfoPruning(t *testing.T) { require.NoError(t, err) ct.dbDirectory = temporaryDirectory + ct.tmpDir = temporaryDirectory expectedNumEntries := protoParams.CatchpointLookback / cfg.CatchpointInterval @@ -1247,8 +1293,7 @@ func TestCatchpointFirstStagePersistence(t *testing.T) { cfg.CatchpointInterval = 4 cfg.CatchpointTracking = 2 cfg.MaxAcctLookback = 0 - ct := newCatchpointTracker( - t, ml, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct := newCatchpointTracker(t, ml, cfg, tempDirectory) defer ct.close() // Add blocks until the first catchpoint first stage round. @@ -1299,8 +1344,7 @@ func TestCatchpointFirstStagePersistence(t *testing.T) { require.NoError(t, err) // Create a catchpoint tracker and let it restart catchpoint's first stage. - ct2 := newCatchpointTracker( - t, ml2, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct2 := newCatchpointTracker(t, ml2, cfg, tempDirectory) defer ct2.close() // Check that the catchpoint data file was rewritten. @@ -1348,8 +1392,7 @@ func TestCatchpointSecondStagePersistence(t *testing.T) { cfg.CatchpointInterval = 4 cfg.CatchpointTracking = 2 cfg.MaxAcctLookback = 0 - ct := newCatchpointTracker( - t, ml, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct := newCatchpointTracker(t, ml, cfg, tempDirectory) defer ct.close() isCatchpointRound := func(rnd basics.Round) bool { @@ -1443,8 +1486,7 @@ func TestCatchpointSecondStagePersistence(t *testing.T) { require.NoError(t, err) // Create a catchpoint tracker and let it restart catchpoint's second stage. - ct2 := newCatchpointTracker( - t, ml2, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct2 := newCatchpointTracker(t, ml2, cfg, tempDirectory) defer ct2.close() // Check that the catchpoint data file was rewritten. @@ -1493,8 +1535,7 @@ func TestCatchpointSecondStageDeletesUnfinishedCatchpointRecord(t *testing.T) { cfg.CatchpointInterval = 4 cfg.CatchpointTracking = 0 cfg.MaxAcctLookback = 0 - ct := newCatchpointTracker( - t, ml, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct := newCatchpointTracker(t, ml, cfg, tempDirectory) defer ct.close() secondStageRound := basics.Round(36) @@ -1526,8 +1567,7 @@ func TestCatchpointSecondStageDeletesUnfinishedCatchpointRecord(t *testing.T) { // Configure a new catchpoint tracker with catchpoints enabled. cfg.CatchpointTracking = 2 - ct2 := newCatchpointTracker( - t, ml2, cfg, filepath.Join(tempDirectory, config.LedgerFilenamePrefix)) + ct2 := newCatchpointTracker(t, ml2, cfg, tempDirectory) defer ct2.close() // Add the last block. @@ -1967,3 +2007,36 @@ func TestCatchpointLargeAccountCountCatchpointGeneration(t *testing.T) { // Garbage collection helps prevent trashing for next tests runtime.GC() } + +func TestMakeCatchpointFilePath(t *testing.T) { + partitiontest.PartitionTest(t) + + type testCase struct { + round int + expectedDataFilePath string + expectedCatchpointFilePath string + } + + tcs := []testCase{ + {10, "10.data", "10.catchpoint"}, + {100, "100.data", "100.catchpoint"}, + // MakeCatchpointFilePath divides the round by 256 to create subdirecories + {257, "257.data", "01/257.catchpoint"}, + {511, "511.data", "01/511.catchpoint"}, + {512, "512.data", "02/512.catchpoint"}, + // 256 * 256 = 65536 + {65536, "65536.data", "00/01/65536.catchpoint"}, + {65537, "65537.data", "00/01/65537.catchpoint"}, + // 645536 * 3 = 193609728 + {193609727, "193609727.data", "3f/8a/0b/193609727.catchpoint"}, + {193609728, "193609728.data", "40/8a/0b/193609728.catchpoint"}, + // 256 * 256 * 256 = 16777216 + {16777216, "16777216.data", "00/00/01/16777216.catchpoint"}, + } + + for _, tc := range tcs { + require.Equal(t, tc.expectedCatchpointFilePath, trackerdb.MakeCatchpointFilePath(basics.Round(tc.round))) + require.Equal(t, tc.expectedDataFilePath, makeCatchpointDataFilePath(basics.Round(tc.round))) + } + +} diff --git a/ledger/encoded/msgp_gen.go b/ledger/encoded/msgp_gen.go index 4ac41dbd40..cc2422ded0 100644 --- a/ledger/encoded/msgp_gen.go +++ b/ledger/encoded/msgp_gen.go @@ -80,11 +80,11 @@ func (_ *BalanceRecordV5) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BalanceRecordV5) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -257,11 +257,11 @@ func (_ *BalanceRecordV6) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BalanceRecordV6) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -501,11 +501,11 @@ func (_ *KVRecordV6) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *KVRecordV6) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/ledger/ledger.go b/ledger/ledger.go index 337072f128..dc3baaf766 100644 --- a/ledger/ledger.go +++ b/ledger/ledger.go @@ -20,7 +20,7 @@ import ( "context" "database/sql" "fmt" - "os" + "path/filepath" "time" "github.com/algorand/go-deadlock" @@ -98,17 +98,23 @@ type Ledger struct { cfg config.Local - dbPathPrefix string + dirsAndPrefix DirsAndPrefix tracer logic.EvalTracer } +// DirsAndPrefix is a struct that holds the genesis directories and the database file prefix, so ledger can construct full paths to database files +type DirsAndPrefix struct { + config.ResolvedGenesisDirs + DBFilePrefix string // the prefix of the database files, appended to genesis directories +} + // OpenLedger creates a Ledger object, using SQLite database filenames // based on dbPathPrefix (in-memory if dbMem is true). genesisInitState.Blocks and // genesisInitState.Accounts specify the initial blocks and accounts to use if the -// database wasn't initialized before. -func OpenLedger( - log logging.Logger, dbPathPrefix string, dbMem bool, genesisInitState ledgercore.InitState, cfg config.Local, +func OpenLedger[T string | DirsAndPrefix]( + // database wasn't initialized before. + log logging.Logger, dbPathPrefix T, dbMem bool, genesisInitState ledgercore.InitState, cfg config.Local, ) (*Ledger, error) { var err error verifiedCacheSize := cfg.VerifiedTranscationsCacheSize @@ -121,6 +127,20 @@ func OpenLedger( tracer = eval.MakeTxnGroupDeltaTracer(cfg.MaxAcctLookback) } + var dirs DirsAndPrefix + // if only a string path has been supplied for the ledger, use it for all resources + // don't set the prefix, only tests provide a string for the path, and they manage paths explicitly + if s, ok := any(dbPathPrefix).(string); ok { + dirs.HotGenesisDir = s + dirs.TrackerGenesisDir = s + dirs.ColdGenesisDir = s + dirs.BlockGenesisDir = s + dirs.CatchpointGenesisDir = s + } else if ds, ok := any(dbPathPrefix).(DirsAndPrefix); ok { + // if a DirsAndPrefix has been supplied, use it. + dirs = ds + } + l := &Ledger{ log: log, archival: cfg.Archival, @@ -132,7 +152,7 @@ func OpenLedger( accountsRebuildSynchronousMode: db.SynchronousMode(cfg.AccountsRebuildSynchronousMode), verifiedTxnCache: verify.MakeVerifiedTransactionCache(verifiedCacheSize), cfg: cfg, - dbPathPrefix: dbPathPrefix, + dirsAndPrefix: dirs, tracer: tracer, } @@ -142,7 +162,7 @@ func OpenLedger( } }() - l.trackerDBs, l.blockDBs, err = openLedgerDB(dbPathPrefix, dbMem, cfg, log) + l.trackerDBs, l.blockDBs, err = openLedgerDB(dirs, dbMem, cfg, log) if err != nil { err = fmt.Errorf("OpenLedger.openLedgerDB %v", err) return nil, err @@ -222,7 +242,8 @@ func (l *Ledger) reloadLedger() error { l.accts.initialize(l.cfg) l.acctsOnline.initialize(l.cfg) - l.catchpoint.initialize(l.cfg, l.dbPathPrefix) + + l.catchpoint.initialize(l.cfg, l.dirsAndPrefix) err = l.trackers.initialize(l, trackers, l.cfg) if err != nil { @@ -280,43 +301,29 @@ func (l *Ledger) verifyMatchingGenesisHash() (err error) { return } -func openLedgerDB(dbPathPrefix string, dbMem bool, cfg config.Local, log logging.Logger) (trackerDBs trackerdb.Store, blockDBs db.Pair, err error) { - // Backwards compatibility: we used to store both blocks and tracker - // state in a single SQLite db file. - if !dbMem { - commonDBFilename := dbPathPrefix + ".sqlite" - _, err = os.Stat(commonDBFilename) - if !os.IsNotExist(err) { - // before launch, we used to have both blocks and tracker - // state in a single SQLite db file. We don't have that anymore, - // and we want to fail when that's the case. - err = fmt.Errorf("a single ledger database file '%s' was detected. This is no longer supported by current binary", commonDBFilename) - return - } - } - +func openLedgerDB(dbPrefixes DirsAndPrefix, dbMem bool, cfg config.Local, log logging.Logger) (trackerDBs trackerdb.Store, blockDBs db.Pair, err error) { outErr := make(chan error, 2) go func() { + trackerDBPrefix := filepath.Join(dbPrefixes.ResolvedGenesisDirs.TrackerGenesisDir, dbPrefixes.DBFilePrefix) var lerr error switch cfg.StorageEngine { case "pebbledb": - dir := dbPathPrefix + "/tracker.pebble" + dir := trackerDBPrefix + "/tracker.pebble" trackerDBs, lerr = pebbledbdriver.Open(dir, dbMem, config.Consensus[protocol.ConsensusCurrentVersion], log) // anything else will initialize a sqlite engine. case "sqlite": fallthrough default: - file := dbPathPrefix + ".tracker.sqlite" - trackerDBs, lerr = sqlitedriver.Open(file, dbMem, log) + trackerDBs, lerr = sqlitedriver.Open(trackerDBPrefix+".tracker.sqlite", dbMem, log) } outErr <- lerr }() go func() { + blockDBPrefix := filepath.Join(dbPrefixes.ResolvedGenesisDirs.BlockGenesisDir, dbPrefixes.DBFilePrefix) var lerr error - blockDBFilename := dbPathPrefix + ".block.sqlite" - blockDBs, lerr = db.OpenPair(blockDBFilename, dbMem) + blockDBs, lerr = db.OpenPair(blockDBPrefix+".block.sqlite", dbMem) if lerr != nil { outErr <- lerr return @@ -447,6 +454,11 @@ func (l *Ledger) notifyCommit(r basics.Round) basics.Round { }() minToSave := l.trackers.committedUpTo(r) + // Check if additional block history is configured, and adjust minToSave if so. + if configuredMinToSave := r.SubSaturate(basics.Round(l.cfg.MaxBlockHistoryLookback)); configuredMinToSave < minToSave { + minToSave = configuredMinToSave + } + if l.archival { // Do not forget any blocks. minToSave = 0 @@ -625,6 +637,13 @@ func (l *Ledger) LatestTotals() (basics.Round, ledgercore.AccountTotals, error) return l.accts.LatestTotals() } +// Totals returns the totals of all accounts for the given round. +func (l *Ledger) Totals(rnd basics.Round) (ledgercore.AccountTotals, error) { + l.trackerMu.RLock() + defer l.trackerMu.RUnlock() + return l.accts.Totals(rnd) +} + // OnlineCirculation returns the online totals of all accounts at the end of round rnd. // It implements agreement's calls for Circulation(rnd) func (l *Ledger) OnlineCirculation(rnd basics.Round, voteRnd basics.Round) (basics.MicroAlgos, error) { diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 5edb6d20e1..19bb6a0796 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -2334,7 +2334,19 @@ func TestLedgerMigrateV6ShrinkDeltas(t *testing.T) { cfg.MaxAcctLookback = proto.MaxBalLookback log := logging.TestingLog(t) log.SetLevel(logging.Info) // prevent spamming with ledger.AddValidatedBlock debug message - trackerDB, blockDB, err := openLedgerDB(dbName, inMem, cfg, log) + // Set basic Directory for all resources + dirs := DirsAndPrefix{ + DBFilePrefix: "", + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + RootGenesisDir: dbName, + HotGenesisDir: dbName, + ColdGenesisDir: dbName, + TrackerGenesisDir: dbName, + BlockGenesisDir: dbName, + CatchpointGenesisDir: dbName, + }, + } + trackerDB, blockDB, err := openLedgerDB(dirs, inMem, cfg, log) require.NoError(t, err) defer func() { trackerDB.Close() @@ -3004,7 +3016,7 @@ func TestLedgerSPVerificationTracker(t *testing.T) { } verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofContextTargetRound), - 1, proto.StateProofInterval, false, any) + 1, proto.StateProofInterval, false, spverDBLoc) addEmptyValidatedBlock(t, l, genesisInitState.Accounts) @@ -3028,7 +3040,7 @@ func TestLedgerSPVerificationTracker(t *testing.T) { triggerTrackerFlush(t, l, genesisInitState) verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofContextTargetRound), - numOfStateProofs, proto.StateProofInterval, true, any) + numOfStateProofs, proto.StateProofInterval, true, spverDBLoc) blk := makeNewEmptyBlock(t, l, t.Name(), genesisInitState.Accounts) var stateProofReceived bookkeeping.StateProofTrackingData @@ -3054,9 +3066,9 @@ func TestLedgerSPVerificationTracker(t *testing.T) { triggerTrackerFlush(t, l, genesisInitState) verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofContextTargetRound), - 1, proto.StateProofInterval, false, any) + 1, proto.StateProofInterval, false, spverDBLoc) verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofContextTargetRound+proto.StateProofInterval), - numOfStateProofs-1, proto.StateProofInterval, true, any) + numOfStateProofs-1, proto.StateProofInterval, true, spverDBLoc) } func TestLedgerReloadStateProofVerificationTracker(t *testing.T) { @@ -3160,7 +3172,7 @@ func TestLedgerCatchpointSPVerificationTracker(t *testing.T) { numTrackedDataFirstCatchpoint := (cfg.CatchpointInterval - proto.MaxBalLookback) / proto.StateProofInterval verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofDataTargetRound), - numTrackedDataFirstCatchpoint, proto.StateProofInterval, true, any) + numTrackedDataFirstCatchpoint, proto.StateProofInterval, true, spverDBLoc) l.Close() l, err = OpenLedger(log, dbName, inMem, genesisInitState, cfg) @@ -3168,11 +3180,12 @@ func TestLedgerCatchpointSPVerificationTracker(t *testing.T) { defer l.Close() verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofDataTargetRound), - numTrackedDataFirstCatchpoint, proto.StateProofInterval, false, any) + numTrackedDataFirstCatchpoint, proto.StateProofInterval, false, spverDBLoc) catchpointAccessor, accessorProgress := initializeTestCatchupAccessor(t, l, uint64(len(initkeys))) - relCatchpointFilePath := filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(basics.Round(cfg.CatchpointInterval))) + relCatchpointFilePath := filepath.Join(dbName, trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(basics.Round(cfg.CatchpointInterval))) + catchpointData := readCatchpointFile(t, relCatchpointFilePath) err = catchpointAccessor.ProcessStagingBalances(context.Background(), catchpointData[1].headerName, catchpointData[1].data, &accessorProgress) @@ -3181,7 +3194,7 @@ func TestLedgerCatchpointSPVerificationTracker(t *testing.T) { require.NoError(t, err) verifyStateProofVerificationTracking(t, &l.spVerification, basics.Round(firstStateProofDataTargetRound), - numTrackedDataFirstCatchpoint, proto.StateProofInterval, true, any) + numTrackedDataFirstCatchpoint, proto.StateProofInterval, true, spverDBLoc) } func TestLedgerSPTrackerAfterReplay(t *testing.T) { @@ -3218,7 +3231,7 @@ func TestLedgerSPTrackerAfterReplay(t *testing.T) { } // 1024 - verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, any) + verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, spverDBLoc) a.Equal(0, len(l.spVerification.pendingDeleteContexts)) // Add StateProof transaction (for round 512) and apply without validating, advancing the NextStateProofRound to 768 @@ -3227,7 +3240,7 @@ func TestLedgerSPTrackerAfterReplay(t *testing.T) { a.NoError(err) a.Equal(1, len(l.spVerification.pendingDeleteContexts)) // To be deleted, but not yet deleted (waiting for commit) - verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, any) + verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, spverDBLoc) // first ensure the block is committed into blockdb l.WaitForCommit(l.Latest()) @@ -3246,5 +3259,35 @@ func TestLedgerSPTrackerAfterReplay(t *testing.T) { a.NoError(err) a.Equal(1, len(l.spVerification.pendingDeleteContexts)) - verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, any) + verifyStateProofVerificationTracking(t, &l.spVerification, firstStateProofRound, 1, proto.StateProofInterval, true, spverDBLoc) +} + +func TestLedgerMaxBlockHistoryLookback(t *testing.T) { + partitiontest.PartitionTest(t) + + genBalances, _, _ := ledgertesting.NewTestGenesis() + var genHash crypto.Digest + crypto.RandBytes(genHash[:]) + cfg := config.GetDefaultLocal() + // set the max lookback to 1400 + cfg.MaxBlockHistoryLookback = 1400 + l := newSimpleLedgerFull(t, genBalances, protocol.ConsensusCurrentVersion, genHash, cfg, simpleLedgerNotArchival()) + defer l.Close() + + // make 1500 blocks + for i := 0; i < 1500; i++ { + eval := nextBlock(t, l) + endBlock(t, l, eval) + } + require.Equal(t, basics.Round(1500), l.Latest()) + + // make sure we can get the last 1400 blocks + blk, err := l.Block(100) + require.NoError(t, err) + require.NotEmpty(t, blk) + + // make sure we can't get a block before the max lookback + blk, err = l.Block(90) + require.Error(t, err) + require.Empty(t, blk) } diff --git a/ledger/ledgercore/msgp_gen.go b/ledger/ledgercore/msgp_gen.go index 63d1a9750b..0f89af32aa 100644 --- a/ledger/ledgercore/msgp_gen.go +++ b/ledger/ledgercore/msgp_gen.go @@ -174,11 +174,11 @@ func (_ *AccountTotals) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AccountTotals) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -729,11 +729,11 @@ func (_ *AlgoCount) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *AlgoCount) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -881,11 +881,11 @@ func (_ *OnlineRoundParamsData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *OnlineRoundParamsData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1056,11 +1056,11 @@ func (_ *StateProofVerificationContext) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProofVerificationContext) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/ledger/lruaccts.go b/ledger/lruaccts.go index 9351604de2..7d3b601454 100644 --- a/ledger/lruaccts.go +++ b/ledger/lruaccts.go @@ -20,6 +20,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/store/trackerdb" "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/util" ) // lruAccounts provides a storage class for the most recently used accounts data. @@ -28,10 +29,10 @@ import ( type lruAccounts struct { // accountsList contain the list of persistedAccountData, where the front ones are the most "fresh" // and the ones on the back are the oldest. - accountsList *persistedAccountDataList + accountsList *util.List[*trackerdb.PersistedAccountData] // accounts provides fast access to the various elements in the list by using the account address // if lruAccounts is set with pendingWrites 0, then accounts is nil - accounts map[basics.Address]*persistedAccountDataListNode + accounts map[basics.Address]*util.ListNode[*trackerdb.PersistedAccountData] // pendingAccounts are used as a way to avoid taking a write-lock. When the caller needs to "materialize" these, // it would call flushPendingWrites and these would be merged into the accounts/accountsList // if lruAccounts is set with pendingWrites 0, then pendingAccounts is nil @@ -50,8 +51,8 @@ type lruAccounts struct { // thread locking semantics : write lock func (m *lruAccounts) init(log logging.Logger, pendingWrites int, pendingWritesWarnThreshold int) { if pendingWrites > 0 { - m.accountsList = newPersistedAccountList().allocateFreeNodes(pendingWrites) - m.accounts = make(map[basics.Address]*persistedAccountDataListNode, pendingWrites) + m.accountsList = util.NewList[*trackerdb.PersistedAccountData]().AllocateFreeNodes(pendingWrites) + m.accounts = make(map[basics.Address]*util.ListNode[*trackerdb.PersistedAccountData], pendingWrites) m.pendingAccounts = make(chan trackerdb.PersistedAccountData, pendingWrites) m.notFound = make(map[basics.Address]struct{}, pendingWrites) m.pendingNotFound = make(chan basics.Address, pendingWrites) @@ -141,10 +142,10 @@ func (m *lruAccounts) write(acctData trackerdb.PersistedAccountData) { // we update with a newer version. el.Value = &acctData } - m.accountsList.moveToFront(el) + m.accountsList.MoveToFront(el) } else { // new entry. - m.accounts[acctData.Addr] = m.accountsList.pushFront(&acctData) + m.accounts[acctData.Addr] = m.accountsList.PushFront(&acctData) } } @@ -159,9 +160,9 @@ func (m *lruAccounts) prune(newSize int) (removed int) { if len(m.accounts) <= newSize { break } - back := m.accountsList.back() + back := m.accountsList.Back() delete(m.accounts, back.Value.Addr) - m.accountsList.remove(back) + m.accountsList.Remove(back) removed++ } diff --git a/ledger/lrukv.go b/ledger/lrukv.go index 8c407a9fc5..ef283faec6 100644 --- a/ledger/lrukv.go +++ b/ledger/lrukv.go @@ -19,6 +19,7 @@ package ledger import ( "github.com/algorand/go-algorand/ledger/store/trackerdb" "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/util" ) //msgp:ignore cachedKVData @@ -35,11 +36,11 @@ type cachedKVData struct { type lruKV struct { // kvList contain the list of persistedKVData, where the front ones are the most "fresh" // and the ones on the back are the oldest. - kvList *persistedKVDataList + kvList *util.List[*cachedKVData] // kvs provides fast access to the various elements in the list by using the key // if lruKV is set with pendingWrites 0, then kvs is nil - kvs map[string]*persistedKVDataListNode + kvs map[string]*util.ListNode[*cachedKVData] // pendingKVs are used as a way to avoid taking a write-lock. When the caller needs to "materialize" these, // it would call flushPendingWrites and these would be merged into the kvs/kvList @@ -57,8 +58,8 @@ type lruKV struct { // thread locking semantics : write lock func (m *lruKV) init(log logging.Logger, pendingWrites int, pendingWritesWarnThreshold int) { if pendingWrites > 0 { - m.kvList = newPersistedKVList().allocateFreeNodes(pendingWrites) - m.kvs = make(map[string]*persistedKVDataListNode, pendingWrites) + m.kvList = util.NewList[*cachedKVData]().AllocateFreeNodes(pendingWrites) + m.kvs = make(map[string]*util.ListNode[*cachedKVData], pendingWrites) m.pendingKVs = make(chan cachedKVData, pendingWrites) } m.log = log @@ -116,10 +117,10 @@ func (m *lruKV) write(kvData trackerdb.PersistedKVData, key string) { // we update with a newer version. el.Value = &cachedKVData{PersistedKVData: kvData, key: key} } - m.kvList.moveToFront(el) + m.kvList.MoveToFront(el) } else { // new entry. - m.kvs[key] = m.kvList.pushFront(&cachedKVData{PersistedKVData: kvData, key: key}) + m.kvs[key] = m.kvList.PushFront(&cachedKVData{PersistedKVData: kvData, key: key}) } } @@ -134,9 +135,9 @@ func (m *lruKV) prune(newSize int) (removed int) { if len(m.kvs) <= newSize { break } - back := m.kvList.back() + back := m.kvList.Back() delete(m.kvs, back.Value.key) - m.kvList.remove(back) + m.kvList.Remove(back) removed++ } return diff --git a/ledger/lruonlineaccts.go b/ledger/lruonlineaccts.go index 40f08917b2..297fb03328 100644 --- a/ledger/lruonlineaccts.go +++ b/ledger/lruonlineaccts.go @@ -20,6 +20,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/store/trackerdb" "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/util" ) // lruAccounts provides a storage class for the most recently used accounts data. @@ -28,10 +29,10 @@ import ( type lruOnlineAccounts struct { // accountsList contain the list of persistedAccountData, where the front ones are the most "fresh" // and the ones on the back are the oldest. - accountsList *persistedOnlineAccountDataList + accountsList *util.List[*trackerdb.PersistedOnlineAccountData] // accounts provides fast access to the various elements in the list by using the account address // if lruOnlineAccounts is set with pendingWrites 0, then accounts is nil - accounts map[basics.Address]*persistedOnlineAccountDataListNode + accounts map[basics.Address]*util.ListNode[*trackerdb.PersistedOnlineAccountData] // pendingAccounts are used as a way to avoid taking a write-lock. When the caller needs to "materialize" these, // it would call flushPendingWrites and these would be merged into the accounts/accountsList // if lruOnlineAccounts is set with pendingWrites 0, then pendingAccounts is nil @@ -46,8 +47,8 @@ type lruOnlineAccounts struct { // thread locking semantics : write lock func (m *lruOnlineAccounts) init(log logging.Logger, pendingWrites int, pendingWritesWarnThreshold int) { if pendingWrites > 0 { - m.accountsList = newPersistedOnlineAccountList().allocateFreeNodes(pendingWrites) - m.accounts = make(map[basics.Address]*persistedOnlineAccountDataListNode, pendingWrites) + m.accountsList = util.NewList[*trackerdb.PersistedOnlineAccountData]().AllocateFreeNodes(pendingWrites) + m.accounts = make(map[basics.Address]*util.ListNode[*trackerdb.PersistedOnlineAccountData], pendingWrites) m.pendingAccounts = make(chan trackerdb.PersistedOnlineAccountData, pendingWrites) } m.log = log @@ -105,10 +106,10 @@ func (m *lruOnlineAccounts) write(acctData trackerdb.PersistedOnlineAccountData) // we update with a newer version. el.Value = &acctData } - m.accountsList.moveToFront(el) + m.accountsList.MoveToFront(el) } else { // new entry. - m.accounts[acctData.Addr] = m.accountsList.pushFront(&acctData) + m.accounts[acctData.Addr] = m.accountsList.PushFront(&acctData) } } @@ -123,9 +124,9 @@ func (m *lruOnlineAccounts) prune(newSize int) (removed int) { if len(m.accounts) <= newSize { break } - back := m.accountsList.back() + back := m.accountsList.Back() delete(m.accounts, back.Value.Addr) - m.accountsList.remove(back) + m.accountsList.Remove(back) removed++ } return diff --git a/ledger/lruresources.go b/ledger/lruresources.go index f0a536350e..6a0367b7af 100644 --- a/ledger/lruresources.go +++ b/ledger/lruresources.go @@ -20,6 +20,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/store/trackerdb" "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/util" ) //msgp:ignore cachedResourceData @@ -35,11 +36,11 @@ type cachedResourceData struct { type lruResources struct { // resourcesList contain the list of persistedResourceData, where the front ones are the most "fresh" // and the ones on the back are the oldest. - resourcesList *persistedResourcesDataList + resourcesList *util.List[*cachedResourceData] // resources provides fast access to the various elements in the list by using the account address // if lruResources is set with pendingWrites 0, then resources is nil - resources map[accountCreatable]*persistedResourcesDataListNode + resources map[accountCreatable]*util.ListNode[*cachedResourceData] // pendingResources are used as a way to avoid taking a write-lock. When the caller needs to "materialize" these, // it would call flushPendingWrites and these would be merged into the resources/resourcesList @@ -61,8 +62,8 @@ type lruResources struct { // thread locking semantics : write lock func (m *lruResources) init(log logging.Logger, pendingWrites int, pendingWritesWarnThreshold int) { if pendingWrites > 0 { - m.resourcesList = newPersistedResourcesList().allocateFreeNodes(pendingWrites) - m.resources = make(map[accountCreatable]*persistedResourcesDataListNode, pendingWrites) + m.resourcesList = util.NewList[*cachedResourceData]().AllocateFreeNodes(pendingWrites) + m.resources = make(map[accountCreatable]*util.ListNode[*cachedResourceData], pendingWrites) m.pendingResources = make(chan cachedResourceData, pendingWrites) m.notFound = make(map[accountCreatable]struct{}, pendingWrites) m.pendingNotFound = make(chan accountCreatable, pendingWrites) @@ -163,10 +164,10 @@ func (m *lruResources) write(resData trackerdb.PersistedResourcesData, addr basi // we update with a newer version. el.Value = &cachedResourceData{PersistedResourcesData: resData, address: addr} } - m.resourcesList.moveToFront(el) + m.resourcesList.MoveToFront(el) } else { // new entry. - m.resources[accountCreatable{address: addr, index: resData.Aidx}] = m.resourcesList.pushFront(&cachedResourceData{PersistedResourcesData: resData, address: addr}) + m.resources[accountCreatable{address: addr, index: resData.Aidx}] = m.resourcesList.PushFront(&cachedResourceData{PersistedResourcesData: resData, address: addr}) } } @@ -181,9 +182,9 @@ func (m *lruResources) prune(newSize int) (removed int) { if len(m.resources) <= newSize { break } - back := m.resourcesList.back() + back := m.resourcesList.Back() delete(m.resources, accountCreatable{address: back.Value.address, index: back.Value.Aidx}) - m.resourcesList.remove(back) + m.resourcesList.Remove(back) removed++ } diff --git a/ledger/msgp_gen.go b/ledger/msgp_gen.go index 19fb667a25..603c83fdcc 100644 --- a/ledger/msgp_gen.go +++ b/ledger/msgp_gen.go @@ -80,11 +80,11 @@ func (_ CatchpointCatchupState) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *CatchpointCatchupState) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 int32 zb0001, bts, err = msgp.ReadInt32Bytes(bts) @@ -224,11 +224,11 @@ func (_ *CatchpointFileHeader) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *CatchpointFileHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -465,11 +465,11 @@ func (_ *catchpointFileBalancesChunkV5) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *catchpointFileBalancesChunkV5) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -655,11 +655,11 @@ func (_ *catchpointFileChunkV6) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *catchpointFileChunkV6) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -892,11 +892,11 @@ func (_ *catchpointStateProofVerificationContext) CanMarshalMsg(z interface{}) b // UnmarshalMsg implements msgp.Unmarshaler func (z *catchpointStateProofVerificationContext) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int diff --git a/ledger/persistedaccts_list.go b/ledger/persistedaccts_list.go deleted file mode 100644 index cd2a46b94a..0000000000 --- a/ledger/persistedaccts_list.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import "github.com/algorand/go-algorand/ledger/store/trackerdb" - -// persistedAccountDataList represents a doubly linked list. -// must initiate with newPersistedAccountList. -type persistedAccountDataList struct { - root persistedAccountDataListNode // sentinel list element, only &root, root.prev, and root.next are used - freeList *persistedAccountDataListNode // preallocated nodes location -} - -type persistedAccountDataListNode struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *persistedAccountDataListNode - - Value *trackerdb.PersistedAccountData -} - -func newPersistedAccountList() *persistedAccountDataList { - l := new(persistedAccountDataList) - l.root.next = &l.root - l.root.prev = &l.root - // used as a helper but does not store value - l.freeList = new(persistedAccountDataListNode) - - return l -} - -func (l *persistedAccountDataList) insertNodeToFreeList(otherNode *persistedAccountDataListNode) { - otherNode.next = l.freeList.next - otherNode.prev = nil - otherNode.Value = nil - - l.freeList.next = otherNode -} - -func (l *persistedAccountDataList) getNewNode() *persistedAccountDataListNode { - if l.freeList.next == nil { - return new(persistedAccountDataListNode) - } - newNode := l.freeList.next - l.freeList.next = newNode.next - - return newNode -} - -func (l *persistedAccountDataList) allocateFreeNodes(numAllocs int) *persistedAccountDataList { - if l.freeList == nil { - return l - } - for i := 0; i < numAllocs; i++ { - l.insertNodeToFreeList(new(persistedAccountDataListNode)) - } - - return l -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *persistedAccountDataList) back() *persistedAccountDataListNode { - isEmpty := func(list *persistedAccountDataList) bool { - // assumes we are inserting correctly to the list - using pushFront. - return list.root.next == &list.root - } - - if isEmpty(l) { - return nil - } - return l.root.prev -} - -// remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *persistedAccountDataList) remove(e *persistedAccountDataListNode) { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - - l.insertNodeToFreeList(e) -} - -// pushFront inserts a new element e with value v at the front of list l and returns e. -func (l *persistedAccountDataList) pushFront(v *trackerdb.PersistedAccountData) *persistedAccountDataListNode { - newNode := l.getNewNode() - newNode.Value = v - return l.insertValue(newNode, &l.root) -} - -// insertValue inserts e after at, increments l.len, and returns e. -func (l *persistedAccountDataList) insertValue(newNode *persistedAccountDataListNode, at *persistedAccountDataListNode) *persistedAccountDataListNode { - n := at.next - at.next = newNode - newNode.prev = at - newNode.next = n - n.prev = newNode - - return newNode -} - -// moveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *persistedAccountDataList) moveToFront(e *persistedAccountDataListNode) { - if l.root.next == e { - return - } - l.move(e, &l.root) -} - -// move moves e to next to at and returns e. -func (l *persistedAccountDataList) move(e, at *persistedAccountDataListNode) *persistedAccountDataListNode { - if e == at { - return e - } - e.prev.next = e.next - e.next.prev = e.prev - - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - - return e -} diff --git a/ledger/persistedaccts_list_test.go b/ledger/persistedaccts_list_test.go deleted file mode 100644 index d4c8599444..0000000000 --- a/ledger/persistedaccts_list_test.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import ( - "testing" - - "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/ledger/store/trackerdb" - "github.com/algorand/go-algorand/test/partitiontest" -) - -type dataListNode interface { - getNext() dataListNode - getPrev() dataListNode -} - -type dataList interface { - getRoot() dataListNode -} - -func (l *persistedAccountDataList) getRoot() dataListNode { - return &l.root -} - -func (l *persistedAccountDataListNode) getNext() dataListNode { - // get rid of returning nil wrapped into an interface to let i = x.getNext(); i != nil work. - if l.next == nil { - return nil - } - return l.next -} - -func (l *persistedAccountDataListNode) getPrev() dataListNode { - if l.prev == nil { - return nil - } - return l.prev -} - -func checkLen(list dataList) int { - if list.getRoot().getNext() == list.getRoot() { - return 0 - } - - return countListSize(list.getRoot()) -} - -func countListSize(head dataListNode) (counter int) { - for i := head.getNext(); i != head && i != nil; i = i.getNext() { - counter++ - } - return counter -} - -func checkListLen(t *testing.T, l dataList, len int) bool { - if n := checkLen(l); n != len { - t.Errorf("l.Len() = %d, want %d", n, len) - return true - } - return false -} - -func TestRemoveFromListAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedAccountList() - e1 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{1}}) - e2 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{2}}) - e3 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{3}}) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e3, e2, e1}) - - l.remove(e2) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e3, e1}) - l.remove(e3) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e1}) -} - -func TestAddingNewNodeWithAllocatedFreeListAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedAccountList().allocateFreeNodes(10) - checkListPointersAD(t, l, []*persistedAccountDataListNode{}) - if countListSize(l.freeList) != 10 { - t.Errorf("free list did not allocate nodes") - return - } - // test elements - e1 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{1}}) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e1}) - - if countListSize(l.freeList) != 9 { - t.Errorf("free list did not provide a node on new list entry") - return - } -} - -func checkListPointers(t *testing.T, l dataList, es []dataListNode) { - root := l.getRoot() - - if failed := checkListLen(t, l, len(es)); failed { - return - } - - if failed := zeroListInspection(t, l, len(es), root); failed { - return - } - - pointerInspection(t, es, root) -} - -// inspect that the list seems like the array -func checkListPointersAD(t *testing.T, l *persistedAccountDataList, es []*persistedAccountDataListNode) { - es2 := make([]dataListNode, len(es)) - for i, el := range es { - es2[i] = el - } - - checkListPointers(t, l, es2) -} - -func zeroListInspection(t *testing.T, l dataList, len int, root dataListNode) bool { - // zero length lists must be the zero value or properly initialized (sentinel circle) - if len == 0 { - if l.getRoot().getNext() != nil && l.getRoot().getNext() != root || l.getRoot().getPrev() != nil && l.getRoot().getPrev() != root { - t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.getRoot().getNext(), l.getRoot().getPrev(), root) - } - return true - } - return false -} - -func pointerInspection(t *testing.T, es []dataListNode, root dataListNode) { - // check internal and external prev/next connections - for i, e := range es { - prev := root - if i > 0 { - prev = es[i-1] - } - if p := e.getPrev(); p != prev { - t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev) - } - - next := root - if i < len(es)-1 { - next = es[i+1] - } - if n := e.getNext(); n != next { - t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next) - } - } -} - -func TestMultielementListPositioningAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedAccountList() - checkListPointersAD(t, l, []*persistedAccountDataListNode{}) - // test elements - e2 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{2}}) - e1 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{1}}) - e3 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{3}}) - e4 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{4}}) - e5 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{5}}) - - checkListPointersAD(t, l, []*persistedAccountDataListNode{e5, e4, e3, e1, e2}) - - l.move(e4, e1) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e5, e3, e1, e4, e2}) - - l.remove(e5) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e3, e1, e4, e2}) - - l.move(e1, e4) // swap in middle - checkListPointersAD(t, l, []*persistedAccountDataListNode{e3, e4, e1, e2}) - - l.moveToFront(e4) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e4, e3, e1, e2}) - - l.remove(e2) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e4, e3, e1}) - - l.moveToFront(e3) // move from middle - checkListPointersAD(t, l, []*persistedAccountDataListNode{e3, e4, e1}) - - l.moveToFront(e1) // move from end - checkListPointersAD(t, l, []*persistedAccountDataListNode{e1, e3, e4}) - - l.moveToFront(e1) // no movement - checkListPointersAD(t, l, []*persistedAccountDataListNode{e1, e3, e4}) - - e2 = l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{2}}) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2, e1, e3, e4}) - - l.remove(e3) // removing from middle - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2, e1, e4}) - - l.remove(e4) // removing from end - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2, e1}) - - l.move(e2, e1) // swapping between two elements - checkListPointersAD(t, l, []*persistedAccountDataListNode{e1, e2}) - - l.remove(e1) // removing front - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2}) - - l.move(e2, l.back()) // swapping element with itself. - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2}) - - l.remove(e2) // remove last one - checkListPointersAD(t, l, []*persistedAccountDataListNode{}) -} - -func TestSingleElementListPositioningAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedAccountList() - checkListPointersAD(t, l, []*persistedAccountDataListNode{}) - e := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{1}}) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e}) - l.moveToFront(e) - checkListPointersAD(t, l, []*persistedAccountDataListNode{e}) - l.remove(e) - checkListPointersAD(t, l, []*persistedAccountDataListNode{}) -} - -func TestRemovedNodeShouldBeMovedToFreeListAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedAccountList() - e1 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{1}}) - e2 := l.pushFront(&trackerdb.PersistedAccountData{Addr: basics.Address{2}}) - - checkListPointersAD(t, l, []*persistedAccountDataListNode{e2, e1}) - - e := l.back() - l.remove(e) - - for i := l.freeList.next; i != nil; i = i.next { - if i == e { - // stopping the tst with good results: - return - } - } - t.Error("expected the removed node to appear at the freelist") -} diff --git a/ledger/persistedkvs_test.go b/ledger/persistedkvs_test.go deleted file mode 100644 index 5d9620d27b..0000000000 --- a/ledger/persistedkvs_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import ( - "testing" - - "github.com/algorand/go-algorand/test/partitiontest" -) - -func (l *persistedKVDataList) getRoot() dataListNode { - return &l.root -} - -func (l *persistedKVDataListNode) getNext() dataListNode { - // get rid of returning nil wrapped into an interface to let i = x.getNext(); i != nil work. - if l.next == nil { - return nil - } - return l.next -} - -func (l *persistedKVDataListNode) getPrev() dataListNode { - if l.prev == nil { - return nil - } - return l.prev -} - -// inspect that the list seems like the array -func checkListPointersBD(t *testing.T, l *persistedKVDataList, es []*persistedKVDataListNode) { - es2 := make([]dataListNode, len(es)) - for i, el := range es { - es2[i] = el - } - - checkListPointers(t, l, es2) -} - -func TestRemoveFromListBD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedKVList() - e1 := l.pushFront(&cachedKVData{key: "key1"}) - e2 := l.pushFront(&cachedKVData{key: "key2"}) - e3 := l.pushFront(&cachedKVData{key: "key3"}) - checkListPointersBD(t, l, []*persistedKVDataListNode{e3, e2, e1}) - - l.remove(e2) - checkListPointersBD(t, l, []*persistedKVDataListNode{e3, e1}) - l.remove(e3) - checkListPointersBD(t, l, []*persistedKVDataListNode{e1}) -} - -func TestAddingNewNodeWithAllocatedFreeListBD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedKVList().allocateFreeNodes(10) - checkListPointersBD(t, l, []*persistedKVDataListNode{}) - if countListSize(l.freeList) != 10 { - t.Errorf("free list did not allocate nodes") - return - } - // test elements - e1 := l.pushFront(&cachedKVData{key: "key1"}) - checkListPointersBD(t, l, []*persistedKVDataListNode{e1}) - - if countListSize(l.freeList) != 9 { - t.Errorf("free list did not provide a node on new list entry") - return - } -} - -func TestMultielementListPositioningBD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedKVList() - checkListPointersBD(t, l, []*persistedKVDataListNode{}) - // test elements - e2 := l.pushFront(&cachedKVData{key: "key1"}) - e1 := l.pushFront(&cachedKVData{key: "key2"}) - e3 := l.pushFront(&cachedKVData{key: "key3"}) - e4 := l.pushFront(&cachedKVData{key: "key4"}) - e5 := l.pushFront(&cachedKVData{key: "key5"}) - - checkListPointersBD(t, l, []*persistedKVDataListNode{e5, e4, e3, e1, e2}) - - l.move(e4, e1) - checkListPointersBD(t, l, []*persistedKVDataListNode{e5, e3, e1, e4, e2}) - - l.remove(e5) - checkListPointersBD(t, l, []*persistedKVDataListNode{e3, e1, e4, e2}) - - l.move(e1, e4) // swap in middle - checkListPointersBD(t, l, []*persistedKVDataListNode{e3, e4, e1, e2}) - - l.moveToFront(e4) - checkListPointersBD(t, l, []*persistedKVDataListNode{e4, e3, e1, e2}) - - l.remove(e2) - checkListPointersBD(t, l, []*persistedKVDataListNode{e4, e3, e1}) - - l.moveToFront(e3) // move from middle - checkListPointersBD(t, l, []*persistedKVDataListNode{e3, e4, e1}) - - l.moveToFront(e1) // move from end - checkListPointersBD(t, l, []*persistedKVDataListNode{e1, e3, e4}) - - l.moveToFront(e1) // no movement - checkListPointersBD(t, l, []*persistedKVDataListNode{e1, e3, e4}) - - e2 = l.pushFront(&cachedKVData{key: "key2"}) - checkListPointersBD(t, l, []*persistedKVDataListNode{e2, e1, e3, e4}) - - l.remove(e3) // removing from middle - checkListPointersBD(t, l, []*persistedKVDataListNode{e2, e1, e4}) - - l.remove(e4) // removing from end - checkListPointersBD(t, l, []*persistedKVDataListNode{e2, e1}) - - l.move(e2, e1) // swapping between two elements - checkListPointersBD(t, l, []*persistedKVDataListNode{e1, e2}) - - l.remove(e1) // removing front - checkListPointersBD(t, l, []*persistedKVDataListNode{e2}) - - l.move(e2, l.back()) // swapping element with itself. - checkListPointersBD(t, l, []*persistedKVDataListNode{e2}) - - l.remove(e2) // remove last one - checkListPointersBD(t, l, []*persistedKVDataListNode{}) -} - -func TestSingleElementListPositioningBD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedKVList() - checkListPointersBD(t, l, []*persistedKVDataListNode{}) - e := l.pushFront(&cachedKVData{key: "key1"}) - checkListPointersBD(t, l, []*persistedKVDataListNode{e}) - l.moveToFront(e) - checkListPointersBD(t, l, []*persistedKVDataListNode{e}) - l.remove(e) - checkListPointersBD(t, l, []*persistedKVDataListNode{}) -} - -func TestRemovedNodeShouldBeMovedToFreeListBD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedKVList() - e1 := l.pushFront(&cachedKVData{key: "key1"}) - e2 := l.pushFront(&cachedKVData{key: "key2"}) - - checkListPointersBD(t, l, []*persistedKVDataListNode{e2, e1}) - - e := l.back() - l.remove(e) - - for i := l.freeList.next; i != nil; i = i.next { - if i == e { - // stopping the tst with good results: - return - } - } - t.Error("expected the removed node to appear at the freelist") -} diff --git a/ledger/persistedonlineaccts_list.go b/ledger/persistedonlineaccts_list.go deleted file mode 100644 index 0f080e5916..0000000000 --- a/ledger/persistedonlineaccts_list.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import "github.com/algorand/go-algorand/ledger/store/trackerdb" - -// persistedOnlineAccountDataList represents a doubly linked list. -// must initiate with newPersistedAccountList. -type persistedOnlineAccountDataList struct { - root persistedOnlineAccountDataListNode // sentinel list element, only &root, root.prev, and root.next are used - freeList *persistedOnlineAccountDataListNode // preallocated nodes location -} - -type persistedOnlineAccountDataListNode struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *persistedOnlineAccountDataListNode - - Value *trackerdb.PersistedOnlineAccountData -} - -func newPersistedOnlineAccountList() *persistedOnlineAccountDataList { - l := new(persistedOnlineAccountDataList) - l.root.next = &l.root - l.root.prev = &l.root - // used as a helper but does not store value - l.freeList = new(persistedOnlineAccountDataListNode) - - return l -} - -func (l *persistedOnlineAccountDataList) insertNodeToFreeList(otherNode *persistedOnlineAccountDataListNode) { - otherNode.next = l.freeList.next - otherNode.prev = nil - otherNode.Value = nil - - l.freeList.next = otherNode -} - -func (l *persistedOnlineAccountDataList) getNewNode() *persistedOnlineAccountDataListNode { - if l.freeList.next == nil { - return new(persistedOnlineAccountDataListNode) - } - newNode := l.freeList.next - l.freeList.next = newNode.next - - return newNode -} - -func (l *persistedOnlineAccountDataList) allocateFreeNodes(numAllocs int) *persistedOnlineAccountDataList { - if l.freeList == nil { - return l - } - for i := 0; i < numAllocs; i++ { - l.insertNodeToFreeList(new(persistedOnlineAccountDataListNode)) - } - - return l -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *persistedOnlineAccountDataList) back() *persistedOnlineAccountDataListNode { - isEmpty := func(list *persistedOnlineAccountDataList) bool { - // assumes we are inserting correctly to the list - using pushFront. - return list.root.next == &list.root - } - - if isEmpty(l) { - return nil - } - return l.root.prev -} - -// remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *persistedOnlineAccountDataList) remove(e *persistedOnlineAccountDataListNode) { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - - l.insertNodeToFreeList(e) -} - -// pushFront inserts a new element e with value v at the front of list l and returns e. -func (l *persistedOnlineAccountDataList) pushFront(v *trackerdb.PersistedOnlineAccountData) *persistedOnlineAccountDataListNode { - newNode := l.getNewNode() - newNode.Value = v - return l.insertValue(newNode, &l.root) -} - -// insertValue inserts e after at, increments l.len, and returns e. -func (l *persistedOnlineAccountDataList) insertValue(newNode *persistedOnlineAccountDataListNode, at *persistedOnlineAccountDataListNode) *persistedOnlineAccountDataListNode { - n := at.next - at.next = newNode - newNode.prev = at - newNode.next = n - n.prev = newNode - - return newNode -} - -// moveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *persistedOnlineAccountDataList) moveToFront(e *persistedOnlineAccountDataListNode) { - if l.root.next == e { - return - } - l.move(e, &l.root) -} - -// move moves e to next to at and returns e. -func (l *persistedOnlineAccountDataList) move(e, at *persistedOnlineAccountDataListNode) *persistedOnlineAccountDataListNode { - if e == at { - return e - } - e.prev.next = e.next - e.next.prev = e.prev - - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - - return e -} diff --git a/ledger/persistedonlineaccts_list_test.go b/ledger/persistedonlineaccts_list_test.go deleted file mode 100644 index afbfbe9a8a..0000000000 --- a/ledger/persistedonlineaccts_list_test.go +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import ( - "testing" - - "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/ledger/store/trackerdb" - "github.com/algorand/go-algorand/test/partitiontest" -) - -func (l *persistedOnlineAccountDataList) getRoot() dataListNode { - return &l.root -} - -func (l *persistedOnlineAccountDataListNode) getNext() dataListNode { - // get rid of returning nil wrapped into an interface to let i = x.getNext(); i != nil work. - if l.next == nil { - return nil - } - return l.next -} - -func (l *persistedOnlineAccountDataListNode) getPrev() dataListNode { - if l.prev == nil { - return nil - } - return l.prev -} - -func TestRemoveFromListOAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedOnlineAccountList() - e1 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{1}}) - e2 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{2}}) - e3 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{3}}) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e3, e2, e1}) - - l.remove(e2) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e3, e1}) - l.remove(e3) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e1}) -} - -func TestAddingNewNodeWithAllocatedFreeListOAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedOnlineAccountList().allocateFreeNodes(10) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{}) - if countListSize(l.freeList) != 10 { - t.Errorf("free list did not allocate nodes") - return - } - // test elements - e1 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{1}}) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e1}) - - if countListSize(l.freeList) != 9 { - t.Errorf("free list did not provide a node on new list entry") - return - } -} - -// inspect that the list seems like the array -func checkListPointersOAD(t *testing.T, l *persistedOnlineAccountDataList, es []*persistedOnlineAccountDataListNode) { - es2 := make([]dataListNode, len(es)) - for i, el := range es { - es2[i] = el - } - - checkListPointers(t, l, es2) -} - -func TestMultielementListPositioningOAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedOnlineAccountList() - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{}) - // test elements - e2 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{2}}) - e1 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{1}}) - e3 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{3}}) - e4 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{4}}) - e5 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{5}}) - - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e5, e4, e3, e1, e2}) - - l.move(e4, e1) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e5, e3, e1, e4, e2}) - - l.remove(e5) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e3, e1, e4, e2}) - - l.move(e1, e4) // swap in middle - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e3, e4, e1, e2}) - - l.moveToFront(e4) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e4, e3, e1, e2}) - - l.remove(e2) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e4, e3, e1}) - - l.moveToFront(e3) // move from middle - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e3, e4, e1}) - - l.moveToFront(e1) // move from end - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e1, e3, e4}) - - l.moveToFront(e1) // no movement - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e1, e3, e4}) - - e2 = l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{2}}) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2, e1, e3, e4}) - - l.remove(e3) // removing from middle - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2, e1, e4}) - - l.remove(e4) // removing from end - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2, e1}) - - l.move(e2, e1) // swapping between two elements - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e1, e2}) - - l.remove(e1) // removing front - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2}) - - l.move(e2, l.back()) // swapping element with itself. - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2}) - - l.remove(e2) // remove last one - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{}) -} - -func TestSingleElementListPositioningOD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedOnlineAccountList() - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{}) - e := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{1}}) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e}) - l.moveToFront(e) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e}) - l.remove(e) - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{}) -} - -func TestRemovedNodeShouldBeMovedToFreeListOAD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedOnlineAccountList() - e1 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{1}}) - e2 := l.pushFront(&trackerdb.PersistedOnlineAccountData{Addr: basics.Address{2}}) - - checkListPointersOAD(t, l, []*persistedOnlineAccountDataListNode{e2, e1}) - - e := l.back() - l.remove(e) - - for i := l.freeList.next; i != nil; i = i.next { - if i == e { - // stopping the tst with good results: - return - } - } - t.Error("expected the removed node to appear at the freelist") -} diff --git a/ledger/persistedresources_list.go b/ledger/persistedresources_list.go deleted file mode 100644 index 2bf9cb6a2e..0000000000 --- a/ledger/persistedresources_list.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -// persistedResourcesDataList represents a doubly linked list. -// must initiate with newPersistedResourcesList. -type persistedResourcesDataList struct { - root persistedResourcesDataListNode // sentinel list element, only &root, root.prev, and root.next are used - freeList *persistedResourcesDataListNode // preallocated nodes location -} - -type persistedResourcesDataListNode struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *persistedResourcesDataListNode - - Value *cachedResourceData -} - -func newPersistedResourcesList() *persistedResourcesDataList { - l := new(persistedResourcesDataList) - l.root.next = &l.root - l.root.prev = &l.root - // used as a helper but does not store value - l.freeList = new(persistedResourcesDataListNode) - - return l -} - -func (l *persistedResourcesDataList) insertNodeToFreeList(otherNode *persistedResourcesDataListNode) { - otherNode.next = l.freeList.next - otherNode.prev = nil - otherNode.Value = nil - - l.freeList.next = otherNode -} - -func (l *persistedResourcesDataList) getNewNode() *persistedResourcesDataListNode { - if l.freeList.next == nil { - return new(persistedResourcesDataListNode) - } - newNode := l.freeList.next - l.freeList.next = newNode.next - - return newNode -} - -func (l *persistedResourcesDataList) allocateFreeNodes(numAllocs int) *persistedResourcesDataList { - if l.freeList == nil { - return l - } - for i := 0; i < numAllocs; i++ { - l.insertNodeToFreeList(new(persistedResourcesDataListNode)) - } - - return l -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *persistedResourcesDataList) back() *persistedResourcesDataListNode { - isEmpty := func(list *persistedResourcesDataList) bool { - // assumes we are inserting correctly to the list - using pushFront. - return list.root.next == &list.root - } - if isEmpty(l) { - return nil - } - return l.root.prev -} - -// remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *persistedResourcesDataList) remove(e *persistedResourcesDataListNode) { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - - l.insertNodeToFreeList(e) -} - -// pushFront inserts a new element e with value v at the front of list l and returns e. -func (l *persistedResourcesDataList) pushFront(v *cachedResourceData) *persistedResourcesDataListNode { - newNode := l.getNewNode() - newNode.Value = v - return l.insertValue(newNode, &l.root) -} - -// insertValue inserts e after at, increments l.len, and returns e. -func (l *persistedResourcesDataList) insertValue(newNode *persistedResourcesDataListNode, at *persistedResourcesDataListNode) *persistedResourcesDataListNode { - n := at.next - at.next = newNode - newNode.prev = at - newNode.next = n - n.prev = newNode - - return newNode -} - -// moveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *persistedResourcesDataList) moveToFront(e *persistedResourcesDataListNode) { - if l.root.next == e { - return - } - l.move(e, &l.root) -} - -// move moves e to next to at and returns e. -func (l *persistedResourcesDataList) move(e, at *persistedResourcesDataListNode) *persistedResourcesDataListNode { - if e == at { - return e - } - e.prev.next = e.next - e.next.prev = e.prev - - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - - return e -} diff --git a/ledger/persistedresources_list_test.go b/ledger/persistedresources_list_test.go deleted file mode 100644 index 484af13956..0000000000 --- a/ledger/persistedresources_list_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (C) 2019-2023 Algorand, Inc. -// This file is part of go-algorand -// -// go-algorand is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// go-algorand is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with go-algorand. If not, see . - -package ledger - -import ( - "testing" - - "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/test/partitiontest" -) - -func (l *persistedResourcesDataList) getRoot() dataListNode { - return &l.root -} - -func (l *persistedResourcesDataListNode) getNext() dataListNode { - // get rid of returning nil wrapped into an interface to let i = x.getNext(); i != nil work. - if l.next == nil { - return nil - } - return l.next -} - -func (l *persistedResourcesDataListNode) getPrev() dataListNode { - if l.prev == nil { - return nil - } - return l.prev -} - -// inspect that the list seems like the array -func checkListPointersRD(t *testing.T, l *persistedResourcesDataList, es []*persistedResourcesDataListNode) { - es2 := make([]dataListNode, len(es)) - for i, el := range es { - es2[i] = el - } - - checkListPointers(t, l, es2) -} - -func TestRemoveFromListRD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedResourcesList() - e1 := l.pushFront(&cachedResourceData{address: basics.Address{1}}) - e2 := l.pushFront(&cachedResourceData{address: basics.Address{2}}) - e3 := l.pushFront(&cachedResourceData{address: basics.Address{3}}) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e3, e2, e1}) - - l.remove(e2) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e3, e1}) - l.remove(e3) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e1}) -} - -func TestAddingNewNodeWithAllocatedFreeListRD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedResourcesList().allocateFreeNodes(10) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{}) - if countListSize(l.freeList) != 10 { - t.Errorf("free list did not allocate nodes") - return - } - // test elements - e1 := l.pushFront(&cachedResourceData{address: basics.Address{1}}) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e1}) - - if countListSize(l.freeList) != 9 { - t.Errorf("free list did not provide a node on new list entry") - return - } -} - -func TestMultielementListPositioningRD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedResourcesList() - checkListPointersRD(t, l, []*persistedResourcesDataListNode{}) - // test elements - e2 := l.pushFront(&cachedResourceData{address: basics.Address{2}}) - e1 := l.pushFront(&cachedResourceData{address: basics.Address{1}}) - e3 := l.pushFront(&cachedResourceData{address: basics.Address{3}}) - e4 := l.pushFront(&cachedResourceData{address: basics.Address{4}}) - e5 := l.pushFront(&cachedResourceData{address: basics.Address{5}}) - - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e5, e4, e3, e1, e2}) - - l.move(e4, e1) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e5, e3, e1, e4, e2}) - - l.remove(e5) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e3, e1, e4, e2}) - - l.move(e1, e4) // swap in middle - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e3, e4, e1, e2}) - - l.moveToFront(e4) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e4, e3, e1, e2}) - - l.remove(e2) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e4, e3, e1}) - - l.moveToFront(e3) // move from middle - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e3, e4, e1}) - - l.moveToFront(e1) // move from end - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e1, e3, e4}) - - l.moveToFront(e1) // no movement - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e1, e3, e4}) - - e2 = l.pushFront(&cachedResourceData{address: basics.Address{2}}) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2, e1, e3, e4}) - - l.remove(e3) // removing from middle - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2, e1, e4}) - - l.remove(e4) // removing from end - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2, e1}) - - l.move(e2, e1) // swapping between two elements - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e1, e2}) - - l.remove(e1) // removing front - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2}) - - l.move(e2, l.back()) // swapping element with itself. - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2}) - - l.remove(e2) // remove last one - checkListPointersRD(t, l, []*persistedResourcesDataListNode{}) -} - -func TestSingleElementListPositioningRD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedResourcesList() - checkListPointersRD(t, l, []*persistedResourcesDataListNode{}) - e := l.pushFront(&cachedResourceData{address: basics.Address{1}}) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e}) - l.moveToFront(e) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e}) - l.remove(e) - checkListPointersRD(t, l, []*persistedResourcesDataListNode{}) -} - -func TestRemovedNodeShouldBeMovedToFreeListRD(t *testing.T) { - partitiontest.PartitionTest(t) - l := newPersistedResourcesList() - e1 := l.pushFront(&cachedResourceData{address: basics.Address{1}}) - e2 := l.pushFront(&cachedResourceData{address: basics.Address{2}}) - - checkListPointersRD(t, l, []*persistedResourcesDataListNode{e2, e1}) - - e := l.back() - l.remove(e) - - for i := l.freeList.next; i != nil; i = i.next { - if i == e { - // stopping the tst with good results: - return - } - } - t.Error("expected the removed node to appear at the freelist") -} diff --git a/ledger/simulation/initialStates.go b/ledger/simulation/initialStates.go new file mode 100644 index 0000000000..e374719ccc --- /dev/null +++ b/ledger/simulation/initialStates.go @@ -0,0 +1,179 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package simulation + +import ( + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions/logic" + "github.com/algorand/go-algorand/util" +) + +// AppKVPairs constructs a KV pair between state key and state value +type AppKVPairs map[string]basics.TealValue + +// SingleAppInitialStates gathers all relevant application on-chain states, including +// - Application Box states +// - Application Global states +// - Application Local states (which is tied to basics.Address) +type SingleAppInitialStates struct { + AppBoxes AppKVPairs + CreatedBoxes util.Set[string] + + AppGlobals AppKVPairs + CreatedGlobals util.Set[string] + + AppLocals map[basics.Address]AppKVPairs + CreatedLocals map[basics.Address]util.Set[string] +} + +// AppsInitialStates maintains a map from basics.AppIndex to SingleAppInitialStates +type AppsInitialStates map[basics.AppIndex]SingleAppInitialStates + +// ResourcesInitialStates gathers all initial states of resources that were accessed during simulation +type ResourcesInitialStates struct { + // AllAppsInitialStates gathers all initial states of apps that were touched (but not created) during simulation + AllAppsInitialStates AppsInitialStates + // CreatedApp gathers all created applications by appID, blocking initial app states in these apps being recorded + CreatedApp util.Set[basics.AppIndex] +} + +func newResourcesInitialStates(request Request) *ResourcesInitialStates { + if !request.TraceConfig.State { + return nil + } + return &ResourcesInitialStates{ + AllAppsInitialStates: make(AppsInitialStates), + CreatedApp: make(util.Set[basics.AppIndex]), + } +} + +// hasBeenRecorded checks if an application state kv-pair has been recorded in SingleAppInitialStates. +func (appIS SingleAppInitialStates) hasBeenRecorded(state logic.AppStateEnum, key string, addr basics.Address) (recorded bool) { + switch state { + case logic.BoxState: + _, recorded = appIS.AppBoxes[key] + case logic.GlobalState: + _, recorded = appIS.AppGlobals[key] + case logic.LocalState: + if kvs, addrLocalExists := appIS.AppLocals[addr]; addrLocalExists { + _, recorded = kvs[key] + } + } + return +} + +// hasBeenCreated checks if an application state kv-pair has been created during simulation. +func (appIS SingleAppInitialStates) hasBeenCreated(state logic.AppStateEnum, key string, addr basics.Address) (created bool) { + switch state { + case logic.BoxState: + created = appIS.CreatedBoxes.Contains(key) + case logic.GlobalState: + created = appIS.CreatedGlobals.Contains(key) + case logic.LocalState: + if kvs, addrLocalExists := appIS.CreatedLocals[addr]; addrLocalExists { + created = kvs.Contains(key) + } + } + return +} + +// recordCreation records a newly created application state kv-pair in SingleAppInitialStates during simulation. +func (appIS SingleAppInitialStates) recordCreation(state logic.AppStateEnum, key string, addr basics.Address) { + switch state { + case logic.BoxState: + appIS.CreatedBoxes.Add(key) + case logic.GlobalState: + appIS.CreatedGlobals.Add(key) + case logic.LocalState: + if _, addrLocalExists := appIS.CreatedLocals[addr]; !addrLocalExists { + appIS.CreatedLocals[addr] = make(util.Set[string]) + } + appIS.CreatedLocals[addr].Add(key) + } +} + +func (appsIS AppsInitialStates) increment(cx *logic.EvalContext) { + appState, stateOp, appID, acctAddr, stateKey := cx.GetOpSpec().AppStateExplain(cx) + // No matter read or write, once this code-path is triggered, something must be recorded into initial state + if _, ok := appsIS[appID]; !ok { + appsIS[appID] = SingleAppInitialStates{ + AppGlobals: make(AppKVPairs), + CreatedGlobals: make(util.Set[string]), + + AppBoxes: make(AppKVPairs), + CreatedBoxes: make(util.Set[string]), + + AppLocals: make(map[basics.Address]AppKVPairs), + CreatedLocals: make(map[basics.Address]util.Set[string]), + } + } + + // if the state has been recorded, pass + if appsIS[appID].hasBeenRecorded(appState, stateKey, acctAddr) { + return + } + + // if this state is created during simulation, pass + if appsIS[appID].hasBeenCreated(appState, stateKey, acctAddr) { + return + } + + tv := logic.AppStateQuerying(cx, appState, stateOp, appID, acctAddr, stateKey) + switch stateOp { + case logic.AppStateWrite: + // if the unrecorded value to write to is nil, pass + // this case means it is creating a state + if tv == (basics.TealValue{}) { + appsIS[appID].recordCreation(appState, stateKey, acctAddr) + return + } + fallthrough + case logic.AppStateDelete: + fallthrough + case logic.AppStateRead: + switch appState { + case logic.BoxState: + appsIS[appID].AppBoxes[stateKey] = tv + case logic.GlobalState: + appsIS[appID].AppGlobals[stateKey] = tv + case logic.LocalState: + if appsIS[appID].AppLocals[acctAddr] == nil { + appsIS[appID].AppLocals[acctAddr] = make(AppKVPairs) + } + appsIS[appID].AppLocals[acctAddr][stateKey] = tv + } + } +} + +// increment is the entry point of (potentially) adding new initial states to ResourcesInitialStates during simulation. +// This method is the top entry point of simulate-initial-state, for ResourcesInitialStates captures all initial states. +// By checking if current opcode has related `Explain` function, this method dispatch incrementing initial states by: +// +- AppStateExplain exists, then dispatch to AppsInitialStates.increment. +func (is *ResourcesInitialStates) increment(cx *logic.EvalContext) { + // This method only applies on logic.ModeApp + if cx.RunMode() == logic.ModeSig { + return + } + // If this method triggers application state changes + if cx.GetOpSpec().AppStateExplain != nil { + if is.CreatedApp.Contains(cx.AppID()) { + return + } + is.AllAppsInitialStates.increment(cx) + } + // TODO asset? +} diff --git a/ledger/simulation/simulation_eval_test.go b/ledger/simulation/simulation_eval_test.go index 747494ea80..3fbe61512c 100644 --- a/ledger/simulation/simulation_eval_test.go +++ b/ledger/simulation/simulation_eval_test.go @@ -38,6 +38,7 @@ import ( ledgertesting "github.com/algorand/go-algorand/ledger/testing" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" + "github.com/algorand/go-algorand/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -49,6 +50,10 @@ func uint64ToBytes(num uint64) []byte { return ibytes } +func bytesToUint64(b []byte) uint64 { + return binary.BigEndian.Uint64(b) +} + type simulationTestCase struct { input simulation.Request developerAPI bool @@ -1399,6 +1404,66 @@ int 1`, }) } +func TestStartRound(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + env := simulationtesting.PrepareSimulatorTest(t) + defer env.Close() + s := simulation.MakeSimulator(env.Ledger, false) + sender := env.Accounts[0] + + txn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: sender.Addr, + ApprovalProgram: `#pragma version 8 +global Round +itob +log +int 1`, + ClearStateProgram: `#pragma version 8 +int 1`, + }) + txn.FirstValid = 0 + txn.LastValid = 1000 + stxn := txn.Txn().Sign(sender.Sk) + + for i := uint64(0); i <= env.Config.MaxAcctLookback; i++ { + // Each of these transactions happens in a separate block + env.TransferAlgos(sender.Addr, sender.Addr, 0) + } + + latestRound := env.TxnInfo.LatestHeader.Round + + t.Run("default", func(t *testing.T) { + // By default, should use latest round + result, err := s.Simulate(simulation.Request{TxnGroups: [][]transactions.SignedTxn{{stxn}}}) + require.NoError(t, err) + require.Len(t, result.TxnGroups, 1) + require.Empty(t, result.TxnGroups[0].FailureMessage) + require.Len(t, result.TxnGroups[0].Txns, 1) + require.Len(t, result.TxnGroups[0].Txns[0].Txn.ApplyData.EvalDelta.Logs, 1) + require.Equal(t, uint64(latestRound+1), bytesToUint64([]byte(result.TxnGroups[0].Txns[0].Txn.ApplyData.EvalDelta.Logs[0]))) + }) + + for i := uint64(0); i <= env.Config.MaxAcctLookback; i++ { + t.Run(fmt.Sprintf("%d rounds before latest", i), func(t *testing.T) { + result, err := s.Simulate(simulation.Request{Round: latestRound - basics.Round(i), TxnGroups: [][]transactions.SignedTxn{{stxn}}}) + require.NoError(t, err) + require.Len(t, result.TxnGroups, 1) + require.Empty(t, result.TxnGroups[0].FailureMessage) + require.Len(t, result.TxnGroups[0].Txns, 1) + require.Len(t, result.TxnGroups[0].Txns[0].Txn.ApplyData.EvalDelta.Logs, 1) + require.Equal(t, uint64(latestRound-basics.Round(i)+1), bytesToUint64([]byte(result.TxnGroups[0].Txns[0].Txn.ApplyData.EvalDelta.Logs[0]))) + }) + } + + t.Run("1 round in the future", func(t *testing.T) { + _, err := s.Simulate(simulation.Request{Round: latestRound + 1, TxnGroups: [][]transactions.SignedTxn{{stxn}}}) + require.ErrorContains(t, err, fmt.Sprintf("ledger does not have entry %d", latestRound+1)) + }) +} + // TestDefaultSignatureCheck tests signature checking when SignaturesOption is NOT enabled. func TestDefaultSignatureCheck(t *testing.T) { partitiontest.PartitionTest(t) @@ -2242,6 +2307,7 @@ byte "hello"; log; int 1`, TraceConfig: simulation.ExecTraceConfig{ Enable: true, Stack: true, + State: true, }, }, developerAPI: true, @@ -2251,6 +2317,7 @@ byte "hello"; log; int 1`, TraceConfig: simulation.ExecTraceConfig{ Enable: true, Stack: true, + State: true, }, TxnGroups: []simulation.TxnGroupResult{ { @@ -2326,6 +2393,10 @@ byte "hello"; log; int 1`, AppBudgetConsumed: 3, }, }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{}, + CreatedApp: util.MakeSet(basics.AppIndex(1002)), + }, }, } }) @@ -3330,6 +3401,10 @@ int 1`, AppBudgetConsumed: 44, }, }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: make(simulation.AppsInitialStates), + CreatedApp: util.MakeSet(futureAppID), + }, }, } }) @@ -3340,17 +3415,7 @@ func TestAppLocalGlobalStateChange(t *testing.T) { t.Parallel() simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { - sender := env.Accounts[0] - - futureAppID := basics.AppIndex(1001) - - createTxn := env.TxnInfo.NewTxn(txntest.Txn{ - Type: protocol.ApplicationCallTx, - Sender: sender.Addr, - ApplicationID: 0, - GlobalStateSchema: basics.StateSchema{NumUint: 1, NumByteSlice: 1}, - LocalStateSchema: basics.StateSchema{NumUint: 1, NumByteSlice: 1}, - ApprovalProgram: `#pragma version 8 + approvalProgramSrc := `#pragma version 8 txn ApplicationID bz end // Do nothing during create @@ -3387,12 +3452,19 @@ global: end: int 1 -`, +` + + sender := env.Accounts[0] + + createdAppID := env.CreateApp(sender.Addr, simulationtesting.AppParams{ + GlobalStateSchema: basics.StateSchema{NumUint: 1, NumByteSlice: 1}, + LocalStateSchema: basics.StateSchema{NumUint: 1, NumByteSlice: 1}, + ApprovalProgram: approvalProgramSrc, ClearStateProgram: `#pragma version 8 int 1`, }) - op, err := logic.AssembleString(createTxn.ApprovalProgram.(string)) + op, err := logic.AssembleString(approvalProgramSrc) require.NoError(t, err) progHash := crypto.Hash(op.Program) @@ -3400,26 +3472,25 @@ int 1`, Type: protocol.ApplicationCallTx, OnCompletion: transactions.OptInOC, Sender: sender.Addr, - ApplicationID: futureAppID, + ApplicationID: createdAppID, }) globalStateCall := env.TxnInfo.NewTxn(txntest.Txn{ Type: protocol.ApplicationCallTx, Sender: sender.Addr, - ApplicationID: futureAppID, + ApplicationID: createdAppID, ApplicationArgs: [][]byte{[]byte("global")}, }) localStateCall := env.TxnInfo.NewTxn(txntest.Txn{ Type: protocol.ApplicationCallTx, Sender: sender.Addr, - ApplicationID: futureAppID, + ApplicationID: createdAppID, ApplicationArgs: [][]byte{[]byte("local")}, }) - txntest.Group(&createTxn, &optIn, &globalStateCall, &localStateCall) + txntest.Group(&optIn, &globalStateCall, &localStateCall) - signedCreate := createTxn.Txn().Sign(sender.Sk) signedOptin := optIn.Txn().Sign(sender.Sk) signedGlobalStateCall := globalStateCall.Txn().Sign(sender.Sk) signedLocalStateCall := localStateCall.Txn().Sign(sender.Sk) @@ -3427,7 +3498,7 @@ int 1`, return simulationTestCase{ input: simulation.Request{ TxnGroups: [][]transactions.SignedTxn{ - {signedCreate, signedOptin, signedGlobalStateCall, signedLocalStateCall}, + {signedOptin, signedGlobalStateCall, signedLocalStateCall}, }, TraceConfig: simulation.ExecTraceConfig{ Enable: true, @@ -3449,44 +3520,6 @@ int 1`, TxnGroups: []simulation.TxnGroupResult{ { Txns: []simulation.TxnResult{ - // App creation - { - Txn: transactions.SignedTxnWithAD{ - ApplyData: transactions.ApplyData{ - ApplicationID: futureAppID, - }, - }, - AppBudgetConsumed: 4, - Trace: &simulation.TransactionTrace{ - ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ - { - PC: 1, - }, - { - PC: 4, - StackAdded: []basics.TealValue{ - { - Type: basics.TealUintType, - }, - }, - }, - { - PC: 6, - StackPopCount: 1, - }, - { - PC: 154, - StackAdded: []basics.TealValue{ - { - Type: basics.TealUintType, - Uint: 1, - }, - }, - }, - }, - ApprovalProgramHash: progHash, - }, - }, // Optin { AppBudgetConsumed: 8, @@ -3500,7 +3533,7 @@ int 1`, StackAdded: []basics.TealValue{ { Type: basics.TealUintType, - Uint: uint64(futureAppID), + Uint: uint64(createdAppID), }, }, }, @@ -3582,7 +3615,7 @@ int 1`, StackAdded: []basics.TealValue{ { Type: basics.TealUintType, - Uint: uint64(futureAppID), + Uint: uint64(createdAppID), }, }, }, @@ -3675,7 +3708,7 @@ int 1`, { AppStateOp: logic.AppStateWrite, AppState: logic.GlobalState, - AppID: futureAppID, + AppID: createdAppID, Key: "global-int-key", NewValue: basics.TealValue{ Type: basics.TealUintType, @@ -3710,7 +3743,7 @@ int 1`, { AppStateOp: logic.AppStateWrite, AppState: logic.GlobalState, - AppID: futureAppID, + AppID: createdAppID, Key: "global-bytes-key", NewValue: basics.TealValue{ Type: basics.TealBytesType, @@ -3764,7 +3797,7 @@ int 1`, StackAdded: []basics.TealValue{ { Type: basics.TealUintType, - Uint: uint64(futureAppID), + Uint: uint64(createdAppID), }, }, }, @@ -3866,7 +3899,7 @@ int 1`, { AppStateOp: logic.AppStateWrite, AppState: logic.LocalState, - AppID: futureAppID, + AppID: createdAppID, Key: "local-int-key", NewValue: basics.TealValue{ Type: basics.TealUintType, @@ -3910,7 +3943,7 @@ int 1`, { AppStateOp: logic.AppStateWrite, AppState: logic.LocalState, - AppID: futureAppID, + AppID: createdAppID, Key: "local-bytes-key", NewValue: basics.TealValue{ Type: basics.TealBytesType, @@ -3935,9 +3968,24 @@ int 1`, }, }, }, - AppBudgetAdded: 2800, - AppBudgetConsumed: 52, + AppBudgetAdded: 2100, + AppBudgetConsumed: 48, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + createdAppID: simulation.SingleAppInitialStates{ + AppLocals: map[basics.Address]simulation.AppKVPairs{}, + AppGlobals: simulation.AppKVPairs{}, + AppBoxes: simulation.AppKVPairs{}, + CreatedGlobals: util.MakeSet("global-bytes-key", "global-int-key"), + CreatedBoxes: make(util.Set[string]), + CreatedLocals: map[basics.Address]util.Set[string]{ + sender.Addr: util.MakeSet("local-bytes-key", "local-int-key"), + }, + }, }, + CreatedApp: util.Set[basics.AppIndex]{}, }, }, } @@ -4173,6 +4221,10 @@ int 1`, AppBudgetConsumed: 14, }, }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: make(simulation.AppsInitialStates), + CreatedApp: util.MakeSet(futureAppID), + }, }, } }) @@ -4353,122 +4405,1705 @@ int 1`, AppBudgetConsumed: 8, }, }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: make(simulation.AppsInitialStates), + CreatedApp: util.MakeSet(futureAppID), + }, }, } }) } -// TestBalanceChangesWithApp sends a payment transaction to a new account and confirms its balance -// within a subsequent app call -func TestBalanceChangesWithApp(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() +type BoxInitialStatesTestCase struct { + boxOpsForPrepare []boxOperation + boxOpsForSimulate []boxOperation + initialBoxStates simulation.AppKVPairs +} + +func testBoxInitialStatesHelper(t *testing.T, testcase BoxInitialStatesTestCase) { + t.Helper() + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { - sender := env.Accounts[0] - senderBalance := sender.AcctData.MicroAlgos.Raw - sendAmount := senderBalance - 500_000 // Leave 0.5 Algos in the sender account - receiver := env.Accounts[1] - receiverBalance := receiver.AcctData.MicroAlgos.Raw + proto := env.TxnInfo.CurrentProtocolParams() + appCreator := env.Accounts[0] - futureAppID := basics.AppIndex(1001) - createTxn := env.TxnInfo.NewTxn(txntest.Txn{ - Type: protocol.ApplicationCallTx, - Sender: sender.Addr, - ApprovalProgram: `#pragma version 6 -txn ApplicationID // [appId] -bz end // [] -int 1 // [1] -balance // [bal[1]] -itob // [itob(bal[1])] -txn ApplicationArgs 0 // [itob(bal[1]), args[0]] -== // [itob(bal[1])=?=args[0]] -assert -end: -int 1 // [1] -`, - ClearStateProgram: `#pragma version 6 + boxApprovalProgram := fmt.Sprintf(boxTestProgram, 8) + boxAppID := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + ApprovalProgram: boxApprovalProgram, + ClearStateProgram: `#pragma version 8 int 1`, }) - checkStartingBalanceTxn := env.TxnInfo.NewTxn(txntest.Txn{ - Type: protocol.ApplicationCallTx, - Sender: sender.Addr, - ApplicationID: futureAppID, - Accounts: []basics.Address{receiver.Addr}, - ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance)}, - }) - paymentTxn := env.TxnInfo.NewTxn(txntest.Txn{ - Type: protocol.PaymentTx, - Sender: sender.Addr, - Receiver: receiver.Addr, - Amount: sendAmount, - }) - checkEndingBalanceTxn := env.TxnInfo.NewTxn(txntest.Txn{ - Type: protocol.ApplicationCallTx, - Sender: sender.Addr, - ApplicationID: futureAppID, - Accounts: []basics.Address{receiver.Addr}, - // Receiver's balance should have increased by sendAmount - ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance + sendAmount)}, - }) - txntest.Group(&createTxn, &checkStartingBalanceTxn, &paymentTxn, &checkEndingBalanceTxn) + op, err := logic.AssembleString(boxApprovalProgram) + require.NoError(t, err) + progHash := crypto.Hash(op.Program) - signedCreateTxn := createTxn.Txn().Sign(sender.Sk) - signedCheckStartingBalanceTxn := checkStartingBalanceTxn.Txn().Sign(sender.Sk) - signedPaymentTxn := paymentTxn.Txn().Sign(sender.Sk) - signedCheckEndingBalanceTxn := checkEndingBalanceTxn.Txn().Sign(sender.Sk) + transferable := env.Accounts[1].AcctData.MicroAlgos.Raw - proto.MinBalance - proto.MinTxnFee + env.TransferAlgos(env.Accounts[1].Addr, boxAppID.Address(), transferable) + + for _, boxOp := range testcase.boxOpsForPrepare { + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: boxAppID, + ApplicationArgs: boxOp.appArgs(), + Boxes: boxOp.boxRefs(), + }).SignedTxn()) + } + + boxOpToSimResult := func(boxOp boxOperation) simulation.TxnResult { + var res simulation.TxnResult + switch boxOp.op { + case logic.BoxReadOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 14, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 14}, + {PC: 22}, + {PC: 28}, + {PC: 35}, + {PC: 38}, + {PC: 69}, + {PC: 72}, + {PC: 73}, + {PC: 74}, + {PC: 75}, + {PC: 87}, + }, + ApprovalProgramHash: progHash, + }, + } + case logic.BoxWriteOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 13, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 14}, + {PC: 22}, + {PC: 28}, + {PC: 35}, + {PC: 38}, + {PC: 78}, + {PC: 81}, + {PC: 83}, + { + PC: 86, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateWrite, + AppState: logic.BoxState, + AppID: boxAppID, + Key: boxOp.name, + NewValue: basics.TealValue{ + Type: basics.TealBytesType, + Bytes: string(boxOp.contents), + }, + }, + }, + }, + {PC: 87}, + }, + ApprovalProgramHash: progHash, + }, + } + case logic.BoxCreateOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 15, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 14}, + {PC: 22}, + {PC: 28}, + {PC: 35}, + {PC: 38}, + {PC: 49}, + {PC: 52}, + {PC: 55}, + { + PC: 56, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateWrite, + AppState: logic.BoxState, + AppID: boxAppID, + Key: boxOp.name, + NewValue: basics.TealValue{ + Type: basics.TealBytesType, + Bytes: string(make([]byte, boxOp.createSize)), + }, + }, + }, + }, + {PC: 57}, + {PC: 58}, + {PC: 87}, + }, + ApprovalProgramHash: progHash, + }, + } + case logic.BoxDeleteOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 13, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 14}, + {PC: 22}, + {PC: 28}, + {PC: 35}, + {PC: 38}, + {PC: 61}, + { + PC: 64, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateDelete, + AppState: logic.BoxState, + AppID: boxAppID, + Key: boxOp.name, + }, + }, + }, + {PC: 65}, + {PC: 66}, + {PC: 87}, + }, + ApprovalProgramHash: progHash, + }, + } + } + return res + } + + txnPtrs := make([]*txntest.Txn, len(testcase.boxOpsForSimulate)) + for i, boxOp := range testcase.boxOpsForSimulate { + tempTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: boxAppID, + ApplicationArgs: boxOp.appArgs(), + Boxes: boxOp.boxRefs(), + }) + txnPtrs[i] = &tempTxn + } + + txntest.Group(txnPtrs...) + signedTxns := make([]transactions.SignedTxn, len(testcase.boxOpsForSimulate)) + for i, txn := range txnPtrs { + signedTxns[i] = txn.Txn().Sign(appCreator.Sk) + } + + txnResults := make([]simulation.TxnResult, len(testcase.boxOpsForSimulate)) + for i, boxOp := range testcase.boxOpsForSimulate { + txnResults[i] = boxOpToSimResult(boxOp) + } + totalConsumed := uint64(0) + for _, txnResult := range txnResults { + totalConsumed += txnResult.AppBudgetConsumed + } + + prepareKeys := make(util.Set[string]) + for _, instruction := range testcase.boxOpsForPrepare { + if instruction.op != logic.BoxWriteOperation { + continue + } + prepareKeys.Add(instruction.name) + } + newlyCreatedGlobalKeySet := make(util.Set[string]) + for _, instruction := range testcase.boxOpsForSimulate { + if instruction.op != logic.BoxWriteOperation { + continue + } + if prepareKeys.Contains(instruction.name) { + continue + } + newlyCreatedGlobalKeySet.Add(instruction.name) + } return simulationTestCase{ input: simulation.Request{ TxnGroups: [][]transactions.SignedTxn{ - { - signedCreateTxn, - signedCheckStartingBalanceTxn, - signedPaymentTxn, - signedCheckEndingBalanceTxn, - }, + signedTxns, + }, + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, }, }, + developerAPI: true, expected: simulation.Result{ Version: simulation.ResultLatestVersion, LastRound: env.TxnInfo.LatestRound(), + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, TxnGroups: []simulation.TxnGroupResult{ { - Txns: []simulation.TxnResult{ - { - Txn: transactions.SignedTxnWithAD{ - ApplyData: transactions.ApplyData{ - ApplicationID: futureAppID, - }, - }, - AppBudgetConsumed: 4, - }, - { - AppBudgetConsumed: 10, - }, - {}, - { - AppBudgetConsumed: 10, - }, + Txns: txnResults, + AppBudgetAdded: 700 * uint64(len(txnResults)), + AppBudgetConsumed: totalConsumed, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + boxAppID: simulation.SingleAppInitialStates{ + AppGlobals: make(simulation.AppKVPairs), + AppLocals: map[basics.Address]simulation.AppKVPairs{}, + AppBoxes: testcase.initialBoxStates, + CreatedGlobals: make(util.Set[string]), + CreatedBoxes: newlyCreatedGlobalKeySet, + CreatedLocals: map[basics.Address]util.Set[string]{}, }, - AppBudgetAdded: 2100, - AppBudgetConsumed: 24, }, + CreatedApp: util.Set[basics.AppIndex]{}, }, }, } }) } -// TestOptionalSignatures tests that transactions with signatures and without signatures are both -// properly handled when AllowEmptySignatures is enabled. -func TestOptionalSignatures(t *testing.T) { +func TestAppInitialBoxStates(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - for _, signed := range []bool{true, false} { - signed := signed - t.Run(fmt.Sprintf("signed=%t", signed), func(t *testing.T) { - simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + + testBoxInitialStatesHelper(t, BoxInitialStatesTestCase{ + boxOpsForPrepare: []boxOperation{ + { + op: logic.BoxCreateOperation, + name: "A", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("initial box A content"), + }, + }, + boxOpsForSimulate: []boxOperation{ + { + op: logic.BoxReadOperation, + name: "A", + }, + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("box A get overwritten"), + }, + }, + initialBoxStates: simulation.AppKVPairs{ + "A": { + Type: basics.TealBytesType, + Bytes: "initial box A content", + }, + }, + }) + + testBoxInitialStatesHelper(t, BoxInitialStatesTestCase{ + boxOpsForPrepare: []boxOperation{ + { + op: logic.BoxCreateOperation, + name: "A", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("initial box A content"), + }, + { + op: logic.BoxCreateOperation, + name: "B", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "B", + contents: []byte("initial box B content"), + }, + { + op: logic.BoxCreateOperation, + name: "C", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "C", + contents: []byte("initial box C content"), + }, + }, + boxOpsForSimulate: []boxOperation{ + { + op: logic.BoxDeleteOperation, + name: "C", + }, + { + op: logic.BoxReadOperation, + name: "A", + }, + }, + initialBoxStates: simulation.AppKVPairs{ + "A": { + Type: basics.TealBytesType, + Bytes: "initial box A content", + }, + "C": { + Type: basics.TealBytesType, + Bytes: "initial box C content", + }, + }, + }) + + testBoxInitialStatesHelper(t, BoxInitialStatesTestCase{ + boxOpsForPrepare: []boxOperation{ + { + op: logic.BoxCreateOperation, + name: "A", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("initial box A content"), + }, + { + op: logic.BoxCreateOperation, + name: "C", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "C", + contents: []byte("initial box C content"), + }, + }, + boxOpsForSimulate: []boxOperation{ + { + op: logic.BoxCreateOperation, + name: "B", + createSize: 21, + }, + { + op: logic.BoxWriteOperation, + name: "B", + contents: []byte("initial box B content"), + }, + }, + initialBoxStates: simulation.AppKVPairs{}, + }) +} + +func testBoxPutInitialStatesHelper(t *testing.T, testcase BoxInitialStatesTestCase) { + t.Helper() + + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + proto := env.TxnInfo.CurrentProtocolParams() + appCreator := env.Accounts[0] + + boxApprovalProgram := `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +byte "write" +byte "delete" +txn ApplicationArgs 0 +match put del +err // Unknown command + +put: +txn ApplicationArgs 1 +txn ApplicationArgs 2 +box_put +b end + +del: +txn ApplicationArgs 1 +box_del +assert +b end + +end: +int 1 +` + boxAppID := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + ApprovalProgram: boxApprovalProgram, + ClearStateProgram: `#pragma version 8 + int 1`, + }) + + op, err := logic.AssembleString(boxApprovalProgram) + require.NoError(t, err) + progHash := crypto.Hash(op.Program) + + transferable := env.Accounts[1].AcctData.MicroAlgos.Raw - proto.MinBalance - proto.MinTxnFee + env.TransferAlgos(env.Accounts[1].Addr, boxAppID.Address(), transferable) + + for _, boxOp := range testcase.boxOpsForPrepare { + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: boxAppID, + ApplicationArgs: boxOp.appArgs(), + Boxes: boxOp.boxRefs(), + }).SignedTxn()) + } + + boxOpToSimResult := func(boxOp boxOperation) simulation.TxnResult { + var res simulation.TxnResult + switch boxOp.op { + case logic.BoxWriteOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 11, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 13}, + {PC: 21}, + {PC: 24}, + {PC: 31}, + {PC: 34}, + { + PC: 37, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateWrite, + AppState: logic.BoxState, + AppID: boxAppID, + Key: boxOp.name, + NewValue: basics.TealValue{ + Type: basics.TealBytesType, + Bytes: string(boxOp.contents), + }, + }, + }, + }, + {PC: 38}, + {PC: 49}, + }, + ApprovalProgramHash: progHash, + }, + } + case logic.BoxDeleteOperation: + res = simulation.TxnResult{ + AppBudgetConsumed: 11, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 13}, + {PC: 21}, + {PC: 24}, + {PC: 31}, + {PC: 34}, + {PC: 37}, + {PC: 61}, + { + PC: 64, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateDelete, + AppState: logic.BoxState, + AppID: boxAppID, + Key: boxOp.name, + }, + }, + }, + {PC: 65}, + {PC: 66}, + {PC: 87}, + }, + ApprovalProgramHash: progHash, + }, + } + } + return res + } + + txnPtrs := make([]*txntest.Txn, len(testcase.boxOpsForSimulate)) + for i, boxOp := range testcase.boxOpsForSimulate { + tempTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: boxAppID, + ApplicationArgs: boxOp.appArgs(), + Boxes: boxOp.boxRefs(), + }) + txnPtrs[i] = &tempTxn + } + + txntest.Group(txnPtrs...) + signedTxns := make([]transactions.SignedTxn, len(testcase.boxOpsForSimulate)) + for i, txn := range txnPtrs { + signedTxns[i] = txn.Txn().Sign(appCreator.Sk) + } + + txnResults := make([]simulation.TxnResult, len(testcase.boxOpsForSimulate)) + for i, boxOp := range testcase.boxOpsForSimulate { + txnResults[i] = boxOpToSimResult(boxOp) + } + totalConsumed := uint64(0) + for _, txnResult := range txnResults { + totalConsumed += txnResult.AppBudgetConsumed + } + + prepareKeys := make(util.Set[string]) + for _, instruction := range testcase.boxOpsForPrepare { + if instruction.op != logic.BoxWriteOperation { + continue + } + prepareKeys.Add(instruction.name) + } + newlyCreatedGlobalKeySet := make(util.Set[string]) + for _, instruction := range testcase.boxOpsForSimulate { + if instruction.op != logic.BoxWriteOperation { + continue + } + if prepareKeys.Contains(instruction.name) { + continue + } + newlyCreatedGlobalKeySet.Add(instruction.name) + } + + return simulationTestCase{ + input: simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{ + signedTxns, + }, + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + }, + developerAPI: true, + expected: simulation.Result{ + Version: simulation.ResultLatestVersion, + LastRound: env.TxnInfo.LatestRound(), + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + TxnGroups: []simulation.TxnGroupResult{ + { + Txns: txnResults, + AppBudgetAdded: 700 * uint64(len(txnResults)), + AppBudgetConsumed: totalConsumed, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + boxAppID: simulation.SingleAppInitialStates{ + AppGlobals: make(simulation.AppKVPairs), + AppLocals: map[basics.Address]simulation.AppKVPairs{}, + AppBoxes: testcase.initialBoxStates, + CreatedGlobals: make(util.Set[string]), + CreatedBoxes: newlyCreatedGlobalKeySet, + CreatedLocals: map[basics.Address]util.Set[string]{}, + }, + }, + CreatedApp: util.Set[basics.AppIndex]{}, + }, + }, + } + }) +} + +func TestAppInitialBoxStatesAboutBoxPut(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testBoxPutInitialStatesHelper(t, BoxInitialStatesTestCase{ + boxOpsForPrepare: []boxOperation{ + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("initial box A content"), + }, + }, + boxOpsForSimulate: []boxOperation{ + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("box A get overwritten"), + }, + }, + initialBoxStates: simulation.AppKVPairs{ + "A": { + Type: basics.TealBytesType, + Bytes: "initial box A content", + }, + }, + }) + + testBoxPutInitialStatesHelper(t, BoxInitialStatesTestCase{ + boxOpsForSimulate: []boxOperation{ + { + op: logic.BoxWriteOperation, + name: "A", + contents: []byte("box A get overwritten"), + }, + }, + initialBoxStates: simulation.AppKVPairs{}, + }) +} + +type GlobalInitialStatesTestCase struct { + prepareInstruction [][][]byte + txnsArgs [][][]byte + initialGlobalStates simulation.AppKVPairs +} + +func (l GlobalInitialStatesTestCase) toSignedTxns(env simulationtesting.Environment, addr simulationtesting.Account, appID basics.AppIndex) []transactions.SignedTxn { + txns := make([]*txntest.Txn, len(l.txnsArgs)) + for i, txnArgs := range l.txnsArgs { + tempTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: addr.Addr, + ApplicationID: appID, + ApplicationArgs: txnArgs, + }) + txns[i] = &tempTxn + } + txntest.Group(txns...) + signedTxns := make([]transactions.SignedTxn, len(l.txnsArgs)) + for i, txn := range txns { + signedTxns[i] = txn.Txn().Sign(addr.Sk) + } + return signedTxns +} + +func testGlobalInitialStatesHelper(t *testing.T, testcase GlobalInitialStatesTestCase) { + t.Helper() + + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + appCreator := env.Accounts[0] + + approvalProgramSrc := `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +byte "put" +byte "del" +txn ApplicationArgs 0 +match put del +err // Unknown command + +put: + txn ApplicationArgs 1 + txn ApplicationArgs 2 + app_global_put + b end + +del: + txn ApplicationArgs 1 + app_global_del + b end + +end: + int 1 +` + + appID := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + GlobalStateSchema: basics.StateSchema{NumByteSlice: 8}, + ApprovalProgram: approvalProgramSrc, + ClearStateProgram: `#pragma version 8 +int 1`, + }) + + op, err := logic.AssembleString(approvalProgramSrc) + require.NoError(t, err) + progHash := crypto.Hash(op.Program) + + for _, instruction := range testcase.prepareInstruction { + txnArgs := [][]byte{[]byte("put")} + txnArgs = append(txnArgs, instruction...) + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Sender: appCreator.Addr, + Type: protocol.ApplicationCallTx, + ApplicationID: appID, + ApplicationArgs: txnArgs, + }).SignedTxn()) + } + + signedTxns := testcase.toSignedTxns(env, appCreator, appID) + + txnArgsToResult := func(txnAppArgs [][]byte) simulation.TxnResult { + var res simulation.TxnResult + switch string(txnAppArgs[0]) { + case "put": + res = simulation.TxnResult{ + Txn: transactions.SignedTxnWithAD{ + ApplyData: transactions.ApplyData{ + EvalDelta: transactions.EvalDelta{ + GlobalDelta: basics.StateDelta{ + string(txnAppArgs[1]): basics.ValueDelta{ + Bytes: string(txnAppArgs[2]), + Action: basics.SetBytesAction, + }, + }, + }, + }, + }, + AppBudgetConsumed: 11, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 11}, + {PC: 16}, + {PC: 19}, + {PC: 26}, + {PC: 29}, + { + PC: 32, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateWrite, + AppState: logic.GlobalState, + AppID: appID, + Key: string(txnAppArgs[1]), + NewValue: basics.TealValue{ + Type: basics.TealBytesType, + Bytes: string(txnAppArgs[2]), + }, + }, + }, + }, + {PC: 33}, + {PC: 43}, + }, + ApprovalProgramHash: progHash, + }, + } + case "del": + res = simulation.TxnResult{ + Txn: transactions.SignedTxnWithAD{ + ApplyData: transactions.ApplyData{ + EvalDelta: transactions.EvalDelta{ + GlobalDelta: basics.StateDelta{ + string(txnAppArgs[1]): basics.ValueDelta{ + Action: basics.DeleteAction, + }, + }, + }, + }, + }, + AppBudgetConsumed: 10, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 11}, + {PC: 16}, + {PC: 19}, + {PC: 36}, + { + PC: 39, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateDelete, + AppState: logic.GlobalState, + AppID: appID, + Key: string(txnAppArgs[1]), + }, + }, + }, + {PC: 40}, + {PC: 43}, + }, + ApprovalProgramHash: progHash, + }, + } + default: + } + return res + } + txnResults := make([]simulation.TxnResult, len(testcase.txnsArgs)) + for i, txnArgs := range testcase.txnsArgs { + txnResults[i] = txnArgsToResult(txnArgs) + } + + prepareKeys := make(util.Set[string]) + for _, instruction := range testcase.prepareInstruction { + prepareKeys.Add(string(instruction[0])) + } + newlyCreatedGlobalKeySet := make(util.Set[string]) + for _, txnArgs := range testcase.txnsArgs { + if string(txnArgs[0]) != "put" { + continue + } + if prepareKeys.Contains(string(txnArgs[1])) { + continue + } + newlyCreatedGlobalKeySet.Add(string(txnArgs[1])) + } + + totalConsumed := uint64(0) + for _, txnResult := range txnResults { + totalConsumed += txnResult.AppBudgetConsumed + } + + return simulationTestCase{ + input: simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{ + signedTxns, + }, + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + }, + developerAPI: true, + expected: simulation.Result{ + Version: simulation.ResultLatestVersion, + LastRound: env.TxnInfo.LatestRound(), + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + TxnGroups: []simulation.TxnGroupResult{ + { + Txns: txnResults, + AppBudgetAdded: 700 * uint64(len(txnResults)), + AppBudgetConsumed: totalConsumed, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + appID: simulation.SingleAppInitialStates{ + AppGlobals: testcase.initialGlobalStates, + AppLocals: map[basics.Address]simulation.AppKVPairs{}, + AppBoxes: make(simulation.AppKVPairs), + CreatedGlobals: newlyCreatedGlobalKeySet, + CreatedBoxes: make(util.Set[string]), + CreatedLocals: map[basics.Address]util.Set[string]{}, + }, + }, + CreatedApp: make(util.Set[basics.AppIndex]), + }, + }, + } + }) +} + +func TestAppInitialGlobalStates(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testGlobalInitialStatesHelper(t, + GlobalInitialStatesTestCase{ + txnsArgs: [][][]byte{ + { + []byte("put"), []byte("A"), []byte("content A"), + }, + { + []byte("del"), []byte("A"), + }, + }, + initialGlobalStates: simulation.AppKVPairs{}, + }, + ) + + testGlobalInitialStatesHelper(t, + GlobalInitialStatesTestCase{ + prepareInstruction: [][][]byte{ + { + []byte("A"), []byte("initial content A"), + }, + }, + txnsArgs: [][][]byte{ + { + []byte("put"), []byte("A"), []byte("content A"), + }, + { + []byte("del"), []byte("A"), + }, + }, + initialGlobalStates: simulation.AppKVPairs{ + "A": basics.TealValue{ + Type: basics.TealBytesType, + Bytes: "initial content A", + }, + }, + }, + ) +} + +type LocalStateOperation struct { + addressIndex uint64 + appArgs [][]byte +} + +type LocalInitialStatesTestCase struct { + prepareInstructions []LocalStateOperation + simulateInstructions []LocalStateOperation + initialLocalStates map[uint64]simulation.AppKVPairs +} + +func (testcase LocalInitialStatesTestCase) toSignedTxns(env simulationtesting.Environment, appID basics.AppIndex) []transactions.SignedTxn { + txns := make([]*txntest.Txn, len(testcase.simulateInstructions)) + for i, instruction := range testcase.simulateInstructions { + tempTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: env.Accounts[instruction.addressIndex].Addr, + ApplicationID: appID, + ApplicationArgs: instruction.appArgs, + }) + txns[i] = &tempTxn + } + txntest.Group(txns...) + signedTxns := make([]transactions.SignedTxn, len(testcase.simulateInstructions)) + for i, txn := range txns { + signedTxns[i] = txn.Txn().Sign(env.Accounts[testcase.simulateInstructions[i].addressIndex].Sk) + } + return signedTxns +} + +func testLocalInitialStatesHelper(t *testing.T, testcase LocalInitialStatesTestCase) { + t.Helper() + + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + appCreator := env.Accounts[0] + + approvalProgramSrc := `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +txn OnCompletion +int OptIn +== +bnz end // Always allow optin + +byte "put" +byte "get" +byte "del" + +txn ApplicationArgs 0 +match put get del +err // Unknown command + +put: + txn Sender // account + txn ApplicationArgs 1 // key + txn ApplicationArgs 2 // local state content + app_local_put + b end + +get: + txn Sender // account + txn ApplicationArgs 1 // key + app_local_get + pop + b end + +del: + txn Sender // account + txn ApplicationArgs 1 // key + app_local_del + b end + +end: + int 1 +` + + appID := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + LocalStateSchema: basics.StateSchema{NumByteSlice: 8}, + ApprovalProgram: approvalProgramSrc, + ClearStateProgram: `#pragma version 8 +int 1`, + }) + + op, err := logic.AssembleString(approvalProgramSrc) + require.NoError(t, err) + progHash := crypto.Hash(op.Program) + + for _, acct := range env.Accounts[2:] { + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Sender: acct.Addr, + Type: protocol.ApplicationCallTx, + ApplicationID: appID, + OnCompletion: transactions.OptInOC, + }).SignedTxn()) + } + + for _, instruction := range testcase.prepareInstructions { + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Sender: env.Accounts[instruction.addressIndex].Addr, + Type: protocol.ApplicationCallTx, + ApplicationID: appID, + ApplicationArgs: instruction.appArgs, + }).SignedTxn()) + } + + signedTxns := testcase.toSignedTxns(env, appID) + + txnArgsToResult := func(instruction LocalStateOperation) simulation.TxnResult { + var res simulation.TxnResult + switch string(instruction.appArgs[0]) { + case "put": + res = simulation.TxnResult{ + Txn: transactions.SignedTxnWithAD{ + ApplyData: transactions.ApplyData{ + EvalDelta: transactions.EvalDelta{ + LocalDeltas: map[uint64]basics.StateDelta{ + 0: { + string(instruction.appArgs[1]): basics.ValueDelta{ + Bytes: string(instruction.appArgs[2]), + Action: basics.SetBytesAction, + }, + }, + }, + }, + }, + }, + AppBudgetConsumed: 18, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 4}, + {PC: 6}, + {PC: 9}, + {PC: 11}, + {PC: 12}, + {PC: 13}, + {PC: 16}, + {PC: 21}, + {PC: 26}, + {PC: 31}, + {PC: 34}, + {PC: 43}, + {PC: 45}, + {PC: 48}, + { + PC: 51, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateWrite, + AppState: logic.LocalState, + AppID: appID, + Key: string(instruction.appArgs[1]), + NewValue: basics.TealValue{ + Type: basics.TealBytesType, + Bytes: string(instruction.appArgs[2]), + }, + Account: env.Accounts[instruction.addressIndex].Addr, + }, + }, + }, + {PC: 52}, + {PC: 74}, + }, + ApprovalProgramHash: progHash, + }, + } + case "del": + res = simulation.TxnResult{ + Txn: transactions.SignedTxnWithAD{ + ApplyData: transactions.ApplyData{ + EvalDelta: transactions.EvalDelta{ + LocalDeltas: map[uint64]basics.StateDelta{ + 0: { + string(instruction.appArgs[1]): basics.ValueDelta{ + Action: basics.DeleteAction, + }, + }, + }, + }, + }, + }, + AppBudgetConsumed: 17, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 4}, + {PC: 6}, + {PC: 9}, + {PC: 11}, + {PC: 12}, + {PC: 13}, + {PC: 16}, + {PC: 21}, + {PC: 26}, + {PC: 31}, + {PC: 34}, + {PC: 65}, + {PC: 67}, + { + PC: 70, + StateChanges: []simulation.StateOperation{ + { + AppStateOp: logic.AppStateDelete, + AppState: logic.LocalState, + AppID: appID, + Key: string(instruction.appArgs[1]), + Account: env.Accounts[instruction.addressIndex].Addr, + }, + }, + }, + {PC: 71}, + {PC: 74}, + }, + ApprovalProgramHash: progHash, + }, + } + case "get": + res = simulation.TxnResult{ + AppBudgetConsumed: 18, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 4}, + {PC: 6}, + {PC: 9}, + {PC: 11}, + {PC: 12}, + {PC: 13}, + {PC: 16}, + {PC: 21}, + {PC: 26}, + {PC: 31}, + {PC: 34}, + {PC: 55}, + {PC: 57}, + {PC: 60}, + {PC: 61}, + {PC: 62}, + {PC: 74}, + }, + ApprovalProgramHash: progHash, + }, + } + default: + } + return res + } + txnResults := make([]simulation.TxnResult, len(testcase.simulateInstructions)) + for i, txnArgs := range testcase.simulateInstructions { + txnResults[i] = txnArgsToResult(txnArgs) + } + + prepareInitialStates := make(map[basics.Address]util.Set[string]) + for _, instruction := range testcase.prepareInstructions { + if prepareInitialStates[env.Accounts[instruction.addressIndex].Addr] == nil { + prepareInitialStates[env.Accounts[instruction.addressIndex].Addr] = make(util.Set[string]) + } + prepareInitialStates[env.Accounts[instruction.addressIndex].Addr].Add(string(instruction.appArgs[1])) + } + + newlyCreatedLocalStates := make(map[basics.Address]util.Set[string]) + for _, instruction := range testcase.simulateInstructions { + if string(instruction.appArgs[0]) != "put" { + continue + } + acctAddress := env.Accounts[instruction.addressIndex].Addr + if prepareInitialStates[acctAddress] != nil && prepareInitialStates[acctAddress].Contains(string(instruction.appArgs[1])) { + continue + } + if newlyCreatedLocalStates[acctAddress] == nil { + newlyCreatedLocalStates[acctAddress] = make(util.Set[string]) + } + newlyCreatedLocalStates[acctAddress].Add(string(instruction.appArgs[1])) + } + + totalConsumed := uint64(0) + for _, txnResult := range txnResults { + totalConsumed += txnResult.AppBudgetConsumed + } + + expectedInitialLocalStates := make(map[basics.Address]simulation.AppKVPairs) + for addrID, kvPair := range testcase.initialLocalStates { + expectedInitialLocalStates[env.Accounts[addrID].Addr] = kvPair + } + + return simulationTestCase{ + input: simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{ + signedTxns, + }, + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + }, + developerAPI: true, + expected: simulation.Result{ + Version: simulation.ResultLatestVersion, + LastRound: env.TxnInfo.LatestRound(), + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + TxnGroups: []simulation.TxnGroupResult{ + { + Txns: txnResults, + AppBudgetAdded: 700 * uint64(len(txnResults)), + AppBudgetConsumed: totalConsumed, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + appID: simulation.SingleAppInitialStates{ + AppGlobals: make(simulation.AppKVPairs), + AppLocals: expectedInitialLocalStates, + AppBoxes: make(simulation.AppKVPairs), + CreatedGlobals: make(util.Set[string]), + CreatedLocals: newlyCreatedLocalStates, + CreatedBoxes: make(util.Set[string]), + }, + }, + CreatedApp: make(util.Set[basics.AppIndex]), + }, + }, + } + }) +} + +func TestLocalInitialStates(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + testLocalInitialStatesHelper(t, LocalInitialStatesTestCase{ + prepareInstructions: []LocalStateOperation{}, + simulateInstructions: []LocalStateOperation{ + { + addressIndex: 2, + appArgs: [][]byte{ + []byte("put"), []byte("key"), []byte("value"), + }, + }, + }, + initialLocalStates: map[uint64]simulation.AppKVPairs{}, + }) + + testLocalInitialStatesHelper(t, LocalInitialStatesTestCase{ + prepareInstructions: []LocalStateOperation{ + { + addressIndex: 2, + appArgs: [][]byte{ + []byte("put"), []byte("key"), []byte("value"), + }, + }, + }, + simulateInstructions: []LocalStateOperation{ + { + addressIndex: 2, + appArgs: [][]byte{ + []byte("put"), []byte("key"), []byte("new-value"), + }, + }, + { + addressIndex: 2, + appArgs: [][]byte{ + []byte("get"), []byte("key"), + }, + }, + { + addressIndex: 2, + appArgs: [][]byte{ + []byte("del"), []byte("key"), + }, + }, + }, + initialLocalStates: map[uint64]simulation.AppKVPairs{ + 2: { + "key": basics.TealValue{ + Type: basics.TealBytesType, + Bytes: "value", + }, + }, + }, + }) +} + +func TestInitialStatesGetEx(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + appCreator := env.Accounts[0] + + approvalProgramSrc := `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +txn OnCompletion +int OptIn +== +bnz end // Always allow optin + +byte "put" +byte "local_put" +byte "del" +txn ApplicationArgs 0 +match put local_put del +err // Unknown command + +put: + txn ApplicationArgs 1 + txn ApplicationArgs 2 + app_global_put + b end + +local_put: + txn Sender + txn ApplicationArgs 1 + txn ApplicationArgs 2 + app_local_put + b end + +del: + txn ApplicationArgs 1 + app_global_del + b end + +end: + int 1 +` + + appIDWithStates := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + GlobalStateSchema: basics.StateSchema{NumByteSlice: 8}, + LocalStateSchema: basics.StateSchema{NumByteSlice: 8}, + ApprovalProgram: approvalProgramSrc, + ClearStateProgram: `#pragma version 8 +int 1`, + }) + + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Sender: appCreator.Addr, + Type: protocol.ApplicationCallTx, + ApplicationID: appIDWithStates, + OnCompletion: transactions.OptInOC, + }).SignedTxn()) + + prepareSteps := [][][]byte{ + { + []byte("put"), []byte("A"), []byte("initial content A"), + }, + { + []byte("local_put"), []byte("B"), []byte("initial content B"), + }, + } + + for _, txnArgs := range prepareSteps { + env.Txn(env.TxnInfo.NewTxn(txntest.Txn{ + Sender: appCreator.Addr, + Type: protocol.ApplicationCallTx, + ApplicationID: appIDWithStates, + ApplicationArgs: txnArgs, + }).SignedTxn()) + } + + // The application to read another app + approvalProgramSrc = `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +byte "read_global" +byte "read_local" +txn ApplicationArgs 0 +match read_global read_local +err // Unknown command + +read_global: + txn ApplicationArgs 1 // AppID + btoi + txn ApplicationArgs 2 // GlobalKey + app_global_get_ex + assert + pop + b end + +read_local: + txn Sender + txn ApplicationArgs 1 // AppID + btoi + txn ApplicationArgs 2 // LocalKey + app_local_get_ex + assert + pop + b end + +end: +int 1 +` + appIDReadingStates := env.CreateApp(appCreator.Addr, simulationtesting.AppParams{ + ApprovalProgram: approvalProgramSrc, + ClearStateProgram: `#pragma version 8 +int 1`, + }) + + op, err := logic.AssembleString(approvalProgramSrc) + require.NoError(t, err) + progHash := crypto.Hash(op.Program) + + txns := make([]*txntest.Txn, 2) + tmpTxn0 := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: appIDReadingStates, + ApplicationArgs: [][]byte{ + []byte("read_global"), + uint64ToBytes(uint64(appIDWithStates)), + []byte("A"), + }, + ForeignApps: []basics.AppIndex{appIDWithStates}, + }) + txns[0] = &tmpTxn0 + tmpTxn1 := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: appCreator.Addr, + ApplicationID: appIDReadingStates, + ApplicationArgs: [][]byte{ + []byte("read_local"), + uint64ToBytes(uint64(appIDWithStates)), + []byte("B"), + }, + ForeignApps: []basics.AppIndex{appIDWithStates}, + Note: []byte("bla"), + }) + txns[1] = &tmpTxn1 + txntest.Group(txns...) + signedTxns := make([]transactions.SignedTxn, len(txns)) + for i, txn := range txns { + signedTxns[i] = txn.Txn().Sign(appCreator.Sk) + } + + // now construct app calls for global local get ex + txnResults := []simulation.TxnResult{ + { + AppBudgetConsumed: 14, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 19}, + {PC: 31}, + {PC: 34}, + {PC: 41}, + {PC: 44}, + {PC: 45}, + {PC: 48}, + {PC: 49}, + {PC: 50}, + {PC: 51}, + {PC: 69}, + }, + ApprovalProgramHash: progHash, + }, + }, + { + AppBudgetConsumed: 15, + Trace: &simulation.TransactionTrace{ + ApprovalProgramTrace: []simulation.OpcodeTraceUnit{ + {PC: 1}, + {PC: 3}, + {PC: 6}, + {PC: 19}, + {PC: 31}, + {PC: 34}, + {PC: 54}, + {PC: 56}, + {PC: 59}, + {PC: 60}, + {PC: 63}, + {PC: 64}, + {PC: 65}, + {PC: 66}, + {PC: 69}, + }, + ApprovalProgramHash: progHash, + }, + }, + } + + totalConsumed := uint64(0) + for _, txnResult := range txnResults { + totalConsumed += txnResult.AppBudgetConsumed + } + + return simulationTestCase{ + input: simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{ + signedTxns, + }, + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + }, + developerAPI: true, + expected: simulation.Result{ + Version: simulation.ResultLatestVersion, + LastRound: env.TxnInfo.LatestRound(), + TraceConfig: simulation.ExecTraceConfig{ + Enable: true, + State: true, + }, + TxnGroups: []simulation.TxnGroupResult{ + { + Txns: txnResults, + AppBudgetAdded: 700 * uint64(len(txnResults)), + AppBudgetConsumed: totalConsumed, + }, + }, + InitialStates: &simulation.ResourcesInitialStates{ + AllAppsInitialStates: simulation.AppsInitialStates{ + appIDWithStates: simulation.SingleAppInitialStates{ + AppGlobals: simulation.AppKVPairs{ + "A": basics.TealValue{ + Type: basics.TealBytesType, + Bytes: "initial content A", + }, + }, + AppLocals: map[basics.Address]simulation.AppKVPairs{ + appCreator.Addr: { + "B": basics.TealValue{ + Type: basics.TealBytesType, + Bytes: "initial content B", + }, + }, + }, + AppBoxes: make(simulation.AppKVPairs), + CreatedGlobals: make(util.Set[string]), + CreatedBoxes: make(util.Set[string]), + CreatedLocals: map[basics.Address]util.Set[string]{}, + }, + }, + CreatedApp: make(util.Set[basics.AppIndex]), + }, + }, + } + }) +} + +// TestBalanceChangesWithApp sends a payment transaction to a new account and confirms its balance +// within a subsequent app call +func TestBalanceChangesWithApp(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { + sender := env.Accounts[0] + senderBalance := sender.AcctData.MicroAlgos.Raw + sendAmount := senderBalance - 500_000 // Leave 0.5 Algos in the sender account + receiver := env.Accounts[1] + receiverBalance := receiver.AcctData.MicroAlgos.Raw + + futureAppID := basics.AppIndex(1001) + createTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: sender.Addr, + ApprovalProgram: `#pragma version 6 +txn ApplicationID // [appId] +bz end // [] +int 1 // [1] +balance // [bal[1]] +itob // [itob(bal[1])] +txn ApplicationArgs 0 // [itob(bal[1]), args[0]] +== // [itob(bal[1])=?=args[0]] +assert +end: +int 1 // [1] +`, + ClearStateProgram: `#pragma version 6 +int 1`, + }) + checkStartingBalanceTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: sender.Addr, + ApplicationID: futureAppID, + Accounts: []basics.Address{receiver.Addr}, + ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance)}, + }) + paymentTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.PaymentTx, + Sender: sender.Addr, + Receiver: receiver.Addr, + Amount: sendAmount, + }) + checkEndingBalanceTxn := env.TxnInfo.NewTxn(txntest.Txn{ + Type: protocol.ApplicationCallTx, + Sender: sender.Addr, + ApplicationID: futureAppID, + Accounts: []basics.Address{receiver.Addr}, + // Receiver's balance should have increased by sendAmount + ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance + sendAmount)}, + }) + + txntest.Group(&createTxn, &checkStartingBalanceTxn, &paymentTxn, &checkEndingBalanceTxn) + + signedCreateTxn := createTxn.Txn().Sign(sender.Sk) + signedCheckStartingBalanceTxn := checkStartingBalanceTxn.Txn().Sign(sender.Sk) + signedPaymentTxn := paymentTxn.Txn().Sign(sender.Sk) + signedCheckEndingBalanceTxn := checkEndingBalanceTxn.Txn().Sign(sender.Sk) + + return simulationTestCase{ + input: simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{ + { + signedCreateTxn, + signedCheckStartingBalanceTxn, + signedPaymentTxn, + signedCheckEndingBalanceTxn, + }, + }, + }, + expected: simulation.Result{ + Version: simulation.ResultLatestVersion, + LastRound: env.TxnInfo.LatestRound(), + TxnGroups: []simulation.TxnGroupResult{ + { + Txns: []simulation.TxnResult{ + { + Txn: transactions.SignedTxnWithAD{ + ApplyData: transactions.ApplyData{ + ApplicationID: futureAppID, + }, + }, + AppBudgetConsumed: 4, + }, + { + AppBudgetConsumed: 10, + }, + {}, + { + AppBudgetConsumed: 10, + }, + }, + AppBudgetAdded: 2100, + AppBudgetConsumed: 24, + }, + }, + }, + } + }) +} + +// TestOptionalSignatures tests that transactions with signatures and without signatures are both +// properly handled when AllowEmptySignatures is enabled. +func TestOptionalSignatures(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + for _, signed := range []bool{true, false} { + signed := signed + t.Run(fmt.Sprintf("signed=%t", signed), func(t *testing.T) { + simulationTest(t, func(env simulationtesting.Environment) simulationTestCase { sender := env.Accounts[0] txn := env.TxnInfo.NewTxn(txntest.Txn{ @@ -5738,6 +7373,10 @@ func (o boxOperation) appArgs() [][]byte { } } +func (o boxOperation) boxRefs() []transactions.BoxRef { + return []transactions.BoxRef{{Name: []byte(o.name)}} +} + type boxTestResult struct { Boxes map[logic.BoxRef]uint64 NumEmptyBoxRefs int diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go index 9283eb83d7..be9f29556b 100644 --- a/ledger/simulation/simulator.go +++ b/ledger/simulation/simulator.go @@ -27,18 +27,14 @@ import ( "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/transactions/verify" + "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/protocol" ) -// simulatorLedger patches the ledger interface to use a constant latest round. -type simulatorLedger struct { - *data.Ledger - start basics.Round -} - // Request packs simulation related txn-group(s), and configurations that are overlapping the ones in real transactions. type Request struct { + Round basics.Round TxnGroups [][]transactions.SignedTxn AllowEmptySignatures bool AllowMoreLogging bool @@ -47,12 +43,24 @@ type Request struct { TraceConfig ExecTraceConfig } -// Latest is part of the LedgerForSimulator interface. +// simulatorLedger patches the ledger interface to use a constant latest round. +type simulatorLedger struct { + *data.Ledger + start basics.Round +} + +// Latest is part of the ledger.Ledger interface. // We override this to use the set latest to prevent racing with the network func (l simulatorLedger) Latest() basics.Round { return l.start } +// LatestTotals is part of the ledger.Ledger interface. +func (l simulatorLedger) LatestTotals() (basics.Round, ledgercore.AccountTotals, error) { + totals, err := l.Totals(l.start) + return l.start, totals, err +} + // LookupLatest would implicitly use the latest round in the _underlying_ // Ledger, it would give wrong results if that ledger has moved forward. But it // should never be called, as the REST API is the only code using this function, @@ -62,6 +70,23 @@ func (l simulatorLedger) LookupLatest(addr basics.Address) (basics.AccountData, return basics.AccountData{}, 0, basics.MicroAlgos{}, err } +// StartEvaluator is part of the ledger.Ledger interface. We override this so that +// the eval.LedgerForEvaluator value passed into eval.StartEvaluator is a simulatorLedger, +// not a data.Ledger. This ensures our overridden LookupLatest method will be used. +func (l simulatorLedger) StartEvaluator(hdr bookkeeping.BlockHeader, paysetHint, maxTxnBytesPerBlock int, tracer logic.EvalTracer) (*eval.BlockEvaluator, error) { + if tracer == nil { + return nil, errors.New("tracer is nil") + } + return eval.StartEvaluator(&l, hdr, + eval.EvaluatorOptions{ + PaysetHint: paysetHint, + Generate: true, + Validate: true, + MaxTxnBytesPerBlock: maxTxnBytesPerBlock, + Tracer: tracer, + }) +} + // SimulatorError is the base error type for all simulator errors. type SimulatorError struct { err error @@ -94,7 +119,7 @@ type Simulator struct { // MakeSimulator creates a new simulator from a ledger. func MakeSimulator(ledger *data.Ledger, developerAPI bool) *Simulator { return &Simulator{ - ledger: simulatorLedger{ledger, ledger.Latest()}, + ledger: simulatorLedger{ledger, 0}, // start round to be specified in Simulate method developerAPI: developerAPI, } } @@ -211,6 +236,13 @@ func (s Simulator) simulateWithTracer(txgroup []transactions.SignedTxn, tracer l // Simulate simulates a transaction group using the simulator. Will error if the transaction group is not well-formed. func (s Simulator) Simulate(simulateRequest Request) (Result, error) { + if simulateRequest.Round != 0 { + s.ledger.start = simulateRequest.Round + } else { + // Access underlying data.Ledger to get the real latest round + s.ledger.start = s.ledger.Ledger.Latest() + } + simulatorTracer, err := makeEvalTracer(s.ledger.start, simulateRequest, s.developerAPI) if err != nil { return Result{}, err diff --git a/ledger/simulation/simulator_test.go b/ledger/simulation/simulator_test.go index 7a28d951f3..a53d2f8ec3 100644 --- a/ledger/simulation/simulator_test.go +++ b/ledger/simulation/simulator_test.go @@ -48,13 +48,13 @@ func TestNonOverridenDataLedgerMethodsUseRoundParameter(t *testing.T) { overridenMethods := []string{ "Latest", "LookupLatest", + "LatestTotals", } // methods that don't use a round number excludedMethods := []string{ "GenesisHash", "GenesisProto", - "LatestTotals", "FlushCaches", } @@ -141,6 +141,7 @@ int 1`, txgroup := []transactions.SignedTxn{signedPayTxn, signedAppCallTxn} mockTracer := &mocktracer.Tracer{} + s.ledger.start = s.ledger.Ledger.Latest() // Set starting round for simulation block, err := s.simulateWithTracer(txgroup, mockTracer, ResultEvalOverrides{}) require.NoError(t, err) diff --git a/ledger/simulation/testing/utils.go b/ledger/simulation/testing/utils.go index 0d02ce8415..d141058106 100644 --- a/ledger/simulation/testing/utils.go +++ b/ledger/simulation/testing/utils.go @@ -75,6 +75,7 @@ func (info TxnInfo) NewTxn(txn txntest.Txn) txntest.Txn { type Environment struct { t *testing.T Ledger *data.Ledger + Config config.Local // Accounts is a list of all accounts in the ledger, excluding the fee sink and rewards pool Accounts []Account FeeSinkAccount Account @@ -296,6 +297,7 @@ func PrepareSimulatorTest(t *testing.T) Environment { return Environment{ t: t, Ledger: ledger, + Config: cfg, Accounts: accounts, FeeSinkAccount: feeSinkAccount, RewardsPoolAccount: rewardsPoolAccount, diff --git a/ledger/simulation/trace.go b/ledger/simulation/trace.go index 7861292830..afc6a2124e 100644 --- a/ledger/simulation/trace.go +++ b/ledger/simulation/trace.go @@ -141,6 +141,7 @@ type Result struct { EvalOverrides ResultEvalOverrides Block *ledgercore.ValidatedBlock TraceConfig ExecTraceConfig + InitialStates *ResourcesInitialStates } // ReturnTrace reads from Result object and decides if simulation returns PC. @@ -217,6 +218,7 @@ func makeSimulationResult(lastRound basics.Round, request Request, developerAPI TxnGroups: groups, EvalOverrides: resultEvalConstants, TraceConfig: request.TraceConfig, + InitialStates: newResourcesInitialStates(request), }, nil } diff --git a/ledger/simulation/tracer.go b/ledger/simulation/tracer.go index 0d5f2eb5c1..2bc600ee38 100644 --- a/ledger/simulation/tracer.go +++ b/ledger/simulation/tracer.go @@ -270,7 +270,7 @@ func (tracer *evalTracer) makeOpcodeTraceUnit(cx *logic.EvalContext) OpcodeTrace } func (o *OpcodeTraceUnit) computeStackValueDeletions(cx *logic.EvalContext, tracer *evalTracer) { - tracer.popCount, tracer.addCount = cx.GetOpSpec().Explain(cx) + tracer.popCount, tracer.addCount = cx.GetOpSpec().StackExplain(cx) o.StackPopCount = uint64(tracer.popCount) stackHeight := len(cx.Stack) @@ -309,6 +309,7 @@ func (tracer *evalTracer) BeforeOpcode(cx *logic.EvalContext) { } if tracer.result.ReturnStateChange() { latestOpcodeTraceUnit.appendStateOperations(cx) + tracer.result.InitialStates.increment(cx) } } } @@ -325,10 +326,14 @@ func (o *OpcodeTraceUnit) appendAddedStackValue(cx *logic.EvalContext, tracer *e } func (o *OpcodeTraceUnit) appendStateOperations(cx *logic.EvalContext) { - if cx.GetOpSpec().StateExplain == nil { + if cx.GetOpSpec().AppStateExplain == nil { + return + } + appState, stateOp, appID, acctAddr, stateKey := cx.GetOpSpec().AppStateExplain(cx) + // If the operation is not write or delete, return without + if stateOp == logic.AppStateRead { return } - appState, stateOp, appID, acctAddr, stateKey := cx.GetOpSpec().StateExplain(cx) o.StateChanges = append(o.StateChanges, StateOperation{ AppStateOp: stateOp, AppState: appState, @@ -376,7 +381,7 @@ func (tracer *evalTracer) recordUpdatedScratchVars(cx *logic.EvalContext) []Scra func (o *OpcodeTraceUnit) updateNewStateValues(cx *logic.EvalContext) { for i, sc := range o.StateChanges { - o.StateChanges[i].NewValue = logic.AppNewStateQuerying( + o.StateChanges[i].NewValue = logic.AppStateQuerying( cx, sc.AppState, sc.AppStateOp, sc.AppID, sc.Account, sc.Key) } } @@ -447,6 +452,13 @@ func (tracer *evalTracer) BeforeProgram(cx *logic.EvalContext) { txnTraceStackElem.ApprovalProgramHash = programHash } } + if tracer.result.ReturnStateChange() { + // If we are recording state changes, including initial states, + // then we should exclude initial states of created app during simulation. + if cx.TxnGroup[groupIndex].SignedTxn.Txn.ApplicationID == 0 { + tracer.result.InitialStates.CreatedApp.Add(cx.AppID()) + } + } if tracer.unnamedResourcePolicy != nil { globalSharing := false diff --git a/ledger/spverificationtracker_test.go b/ledger/spverificationtracker_test.go index a88c05f0ed..47d85bab31 100644 --- a/ledger/spverificationtracker_test.go +++ b/ledger/spverificationtracker_test.go @@ -38,7 +38,7 @@ const unusedByStateProofTracker = basics.Round(0) type StateProofTrackingLocation uint64 const ( - any StateProofTrackingLocation = iota + spverDBLoc StateProofTrackingLocation = iota trackerDB trackerMemory ) @@ -157,7 +157,7 @@ func verifyStateProofVerificationTracking(t *testing.T, spt *spVerificationTrack for lastAttestedRound := startRound; lastAttestedRound <= finalLastAttestedRound; lastAttestedRound += basics.Round(stateProofInterval) { var err error switch trackingLocation { - case any: + case spverDBLoc: _, err = spt.LookupVerificationContext(lastAttestedRound) case trackerDB: _, err = spt.lookupContextInDB(lastAttestedRound) @@ -190,7 +190,7 @@ func TestStateProofVerificationTracker_StateProofsDisabled(t *testing.T) { mockCommit(t, spt, ml, 0, roundsAmount) - verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, uint64(roundsAmount)/defaultStateProofInterval, defaultStateProofInterval, false, any) + verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, uint64(roundsAmount)/defaultStateProofInterval, defaultStateProofInterval, false, spverDBLoc) } func TestStateProofVerificationTracker_StateProofsNotStuck(t *testing.T) { @@ -208,12 +208,12 @@ func TestStateProofVerificationTracker_StateProofsNotStuck(t *testing.T) { mockCommit(t, spt, ml, 0, lastBlock.block.Round()) expectedRemainingContextNum := expectedContextNum - 1 - verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, expectedRemainingContextNum, defaultStateProofInterval, false, any) + verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, expectedRemainingContextNum, defaultStateProofInterval, false, spverDBLoc) finalLastAttestedRound := defaultFirstStateProofContextRound + basics.Round(expectedRemainingContextNum*defaultStateProofInterval) // The last verification context should still be tracked since the round with the state proof transaction it is used // to verify has not yet been committed. - verifyStateProofVerificationTracking(t, spt, finalLastAttestedRound, 1, defaultStateProofInterval, true, any) + verifyStateProofVerificationTracking(t, spt, finalLastAttestedRound, 1, defaultStateProofInterval, true, spverDBLoc) } func TestStateProofVerificationTracker_CommitFUllDbFlush(t *testing.T) { @@ -295,12 +295,12 @@ func TestStateProofVerificationTracker_CommitFullDbPruning(t *testing.T) { mockCommit(t, spt, ml, 0, lastBlock.block.Round()) - verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, maxStateProofsToGenerate, defaultStateProofInterval, false, any) + verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, maxStateProofsToGenerate, defaultStateProofInterval, false, spverDBLoc) finalLastAttestedRound := defaultFirstStateProofContextRound + basics.Round(maxStateProofsToGenerate*defaultStateProofInterval) // The last verification context should still be tracked since the round with the state proof transaction it is used // to verify has not yet been committed. - verifyStateProofVerificationTracking(t, spt, finalLastAttestedRound, 1, defaultStateProofInterval, true, any) + verifyStateProofVerificationTracking(t, spt, finalLastAttestedRound, 1, defaultStateProofInterval, true, spverDBLoc) } func TestStateProofVerificationTracker_CommitPartialDbPruning(t *testing.T) { @@ -324,7 +324,7 @@ func TestStateProofVerificationTracker_CommitPartialDbPruning(t *testing.T) { mockCommit(t, spt, ml, 0, lastStuckBlock.block.Round()+basics.Round(contextToRemove)) - verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, contextToRemove, defaultStateProofInterval, false, any) + verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, contextToRemove, defaultStateProofInterval, false, spverDBLoc) verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound+basics.Round(contextToRemove*defaultStateProofInterval), contextToAdd-contextToRemove, defaultStateProofInterval, true, trackerDB) } @@ -380,10 +380,10 @@ func TestStateProofVerificationTracker_StateProofIntervalChange(t *testing.T) { newStateProofInterval, true) verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, oldIntervalContext, defaultStateProofInterval, - true, any) + true, spverDBLoc) firstNewIntervalLastAttestedRound := lastOldIntervalBlock.block.Round() + basics.Round(defaultStateProofInterval) verifyStateProofVerificationTracking(t, spt, firstNewIntervalLastAttestedRound, newIntervalContext, - newStateProofInterval, true, any) + newStateProofInterval, true, spverDBLoc) newIntervalRemovedStateProofs := newIntervalContext - (newIntervalContext / 2) // State Proofs for old blocks should be generated using the old interval. @@ -399,11 +399,11 @@ func TestStateProofVerificationTracker_StateProofIntervalChange(t *testing.T) { firstRemainingLastAttestedRound := firstNewIntervalLastAttestedRound + basics.Round(newIntervalRemovedStateProofs*newStateProofInterval) verifyStateProofVerificationTracking(t, spt, defaultFirstStateProofContextRound, oldIntervalContext, defaultStateProofInterval, - false, any) + false, spverDBLoc) verifyStateProofVerificationTracking(t, spt, firstNewIntervalLastAttestedRound, - newIntervalRemovedStateProofs, newStateProofInterval, false, any) + newIntervalRemovedStateProofs, newStateProofInterval, false, spverDBLoc) verifyStateProofVerificationTracking(t, spt, firstRemainingLastAttestedRound, newIntervalContext-newIntervalRemovedStateProofs, - newStateProofInterval, true, any) + newStateProofInterval, true, spverDBLoc) } func TestStateProofVerificationTracker_LookupVerificationContext(t *testing.T) { diff --git a/ledger/store/trackerdb/generickv/accounts_ext_reader.go b/ledger/store/trackerdb/generickv/accounts_ext_reader.go index 553d143a45..132149da6f 100644 --- a/ledger/store/trackerdb/generickv/accounts_ext_reader.go +++ b/ledger/store/trackerdb/generickv/accounts_ext_reader.go @@ -36,7 +36,8 @@ func (r *accountsReader) AccountsRound() (rnd basics.Round, err error) { // "SELECT rnd FROM acctrounds WHERE id='acctbase'" // read round entry - value, closer, err := r.kvr.Get(roundKey()) + key := roundKey() + value, closer, err := r.kvr.Get(key[:]) if err != nil { return } @@ -50,7 +51,8 @@ func (r *accountsReader) AccountsRound() (rnd basics.Round, err error) { func (r *accountsReader) AccountsTotals(ctx context.Context, catchpointStaging bool) (totals ledgercore.AccountTotals, err error) { // read round entry - value, closer, err := r.kvr.Get(totalsKey(catchpointStaging)) + key := totalsKey(catchpointStaging) + value, closer, err := r.kvr.Get(key[:]) if err != nil { return } @@ -75,17 +77,18 @@ func (r *accountsReader) LookupAccountAddressFromAddressID(ctx context.Context, } func (r *accountsReader) LookupAccountRowID(addr basics.Address) (ref trackerdb.AccountRef, err error) { - // Note: [perf] this is not a very cheap operation since we have to pull up the entire record - acc, err := r.LookupAccount(addr) + // TODO: [Review] technically we could just return the address here + // return accountRef{addr}, nil + // the problem is that this would have a different behaviour than the SQL which hits the db + // thus potentially returning notfound + key := accountKey(addr) + _, closer, err := r.kvr.Get(key[:]) if err != nil { return } + defer closer.Close() - if acc.Ref == nil { - return nil, trackerdb.ErrNotFound - } - - return acc.Ref, nil + return accountRef{addr}, nil } func (r *accountsReader) LookupResourceDataByAddrID(accRef trackerdb.AccountRef, aidx basics.CreatableIndex) (data []byte, err error) { @@ -97,7 +100,8 @@ func (r *accountsReader) LookupResourceDataByAddrID(accRef trackerdb.AccountRef, } xref := accRef.(accountRef) - value, closer, err := r.kvr.Get(resourceKey(xref.addr, aidx)) + key := resourceKey(xref.addr, aidx) + value, closer, err := r.kvr.Get(key[:]) if err != nil { return } @@ -123,22 +127,16 @@ func (r *accountsReader) TotalKVs(ctx context.Context) (total uint64, err error) // TODO: this replicates some functionality from LookupOnlineHistory, implemented for onlineAccountsReader func (r *accountsReader) LookupOnlineAccountDataByAddress(addr basics.Address) (ref trackerdb.OnlineAccountRef, data []byte, err error) { - low := onlineAccountOnlyPartialKey(addr) - high := onlineAccountOnlyPartialKey(addr) - high[len(high)-1]++ - iter := r.kvr.NewIter(low, high, true) + low, high := onlineAccountAddressRangePrefix(addr) + iter := r.kvr.NewIter(low[:], high[:], true) defer iter.Close() if iter.Next() { - // key is -- = key := iter.Key() - rndOffset := len(kvPrefixOnlineAccount) + 1 + 32 + 1 - u64Rnd := binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - - addrOffset := len(kvPrefixOnlineAccount) + 1 - var addr basics.Address - copy(addr[:], key[addrOffset:addrOffset+32]) + // extract the round and address from the key + addr := extractOnlineAccountAddress(key) + rnd := extractOnlineAccountRound(key) data, err = iter.Value() if err != nil { @@ -153,7 +151,7 @@ func (r *accountsReader) LookupOnlineAccountDataByAddress(addr basics.Address) ( ref = onlineAccountRef{ addr: addr, - round: basics.Round(u64Rnd), + round: rnd, normBalance: oa.NormalizedOnlineBalance(r.proto), } } else { @@ -187,12 +185,9 @@ func (r *accountsReader) AccountsOnlineTop(rnd basics.Round, offset uint64, n ui data = make(map[basics.Address]*ledgercore.OnlineAccount) // prepare iter over online accounts (by balance) - low := []byte(kvPrefixOnlineAccountBalance) - low = append(low, "-"...) - high := onlineAccountBalanceOnlyPartialKey(rnd) - high[len(high)-1]++ + low, high := onlineAccountBalanceForRoundRangePrefix(rnd) // reverse order iterator to get high-to-low - iter := r.kvr.NewIter(low, high, true) + iter := r.kvr.NewIter(low[:], high[:], true) defer iter.Close() var value []byte @@ -209,16 +204,10 @@ func (r *accountsReader) AccountsOnlineTop(rnd basics.Round, offset uint64, n ui return } - // key is --- = key := iter.Key() - // TODO: make this cleaner - // get the offset where the address starts - offset := len(kvPrefixOnlineAccountBalance) + 1 + 8 + 1 + 8 + 1 - // extract address - var addr basics.Address - copy(addr[:], key[offset:]) + addr := extractOnlineAccountBalanceAddress(key) // skip if already in map if _, ok := data[addr]; ok { @@ -254,23 +243,17 @@ func (r *accountsReader) AccountsOnlineRoundParams() (onlineRoundParamsData []le // // SELECT rnd, data FROM onlineroundparamstail ORDER BY rnd ASC - start := []byte(kvOnlineAccountRoundParams + "-") - end := []byte(kvOnlineAccountRoundParams + ".") - iter := r.kvr.NewIter(start, end, false) + start, end := onlineAccountRoundParamsFullRangePrefix() + iter := r.kvr.NewIter(start[:], end[:], false) defer iter.Close() var value []byte for iter.Next() { - // read the key - // schema: - key := iter.Key() - // extract the round from the key - rndOffset := len(kvOnlineAccountRoundParams) + 1 - u64Rnd := binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - // assign current item round as endRound - endRound = basics.Round(u64Rnd) + // extract the round from the key & assign current item round as endRound + endRound = extractOnlineAccountRoundParamsRoundPart(key) // get value for current item in the iterator value, err = iter.Value() @@ -312,15 +295,13 @@ func (r *accountsReader) OnlineAccountsAll(maxAccounts uint64) ([]trackerdb.Pers return nil, err } - low := []byte(kvPrefixOnlineAccount + "-") - high := []byte(kvPrefixOnlineAccount + ".") - iter := r.kvr.NewIter(low, high, false) + low, high := onlineAccountFullRangePrefix() + iter := r.kvr.NewIter(low[:], high[:], false) defer iter.Close() result := make([]trackerdb.PersistedOnlineAccountData, 0, maxAccounts) var value []byte - var updround uint64 // keep track of the most recently seen account so we can tally up the total number seen lastAddr := basics.Address{} @@ -329,20 +310,15 @@ func (r *accountsReader) OnlineAccountsAll(maxAccounts uint64) ([]trackerdb.Pers for iter.Next() { pitem := trackerdb.PersistedOnlineAccountData{Round: round} - // schema: -- key := iter.Key() - addrOffset := len(kvPrefixOnlineAccount) + 1 - var addr basics.Address - copy(addr[:], key[addrOffset:addrOffset+32]) - // extract updround, it's the last section after the "-" - - rndOffset := addrOffset + 32 + 1 - updround = binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) + // extract addr & round + addr := extractOnlineAccountAddress(key) + updRound := extractOnlineAccountRound(key) // load addr, round and data into the persisted item pitem.Addr = addr - pitem.UpdRound = basics.Round(updround) + pitem.UpdRound = updRound // get value for current item in the iterator value, err = iter.Value() if err != nil { @@ -392,27 +368,19 @@ func (r *accountsReader) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd expired := make(map[basics.Address]struct{}) // prepare iter over online accounts (by balance) - low := []byte(kvPrefixOnlineAccountBalance) - low = append(low, "-"...) - high := onlineAccountBalanceOnlyPartialKey(rnd) - high[len(high)-1]++ + low, high := onlineAccountBalanceForRoundRangePrefix(rnd) // reverse order iterator to get high-to-low - iter := r.kvr.NewIter(low, high, true) + iter := r.kvr.NewIter(low[:], high[:], true) defer iter.Close() var value []byte // add the other results to the map for iter.Next() { - // key is --- = key := iter.Key() - // get the addrOffset where the address starts - addrOffset := len(kvPrefixOnlineAccountBalance) + 1 + 8 + 1 + 8 + 1 - // extract address - var addr basics.Address - copy(addr[:], key[addrOffset:]) + addr := extractOnlineAccountBalanceAddress(key) // skip if already in map // we keep only the one with `max(updround)` @@ -420,7 +388,7 @@ func (r *accountsReader) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd if _, ok := data[addr]; ok { continue } - // when the a ccount is expired we do not add it to the data + // when the account is expired we do not add it to the data // but we might have an older version that is not expired show up // this would be wrong, so we skip those accounts if the latest version is expired if _, ok := expired[addr]; ok { @@ -459,22 +427,19 @@ func (r *accountsReader) LoadTxTail(ctx context.Context, dbRound basics.Round) ( // // "SELECT rnd, data FROM txtail ORDER BY rnd DESC" - start := []byte(kvTxTail + "-") - end := []byte(kvTxTail + ".") - iter := r.kvr.NewIter(start, end, true) + start, end := txTailFullRangePrefix() + iter := r.kvr.NewIter(start[:], end[:], true) defer iter.Close() var value []byte expectedRound := dbRound for iter.Next() { - // read the key key := iter.Key() // extract the txTail round from the key - rndOffset := len(kvTxTail) + 1 - u64Rnd := binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - round := basics.Round(u64Rnd) + round := extractTxTailRoundPart(key) + // check that we are on the right round if round != expectedRound { return nil, nil, 0, fmt.Errorf("txtail table contain unexpected round %d; round %d was expected", round, expectedRound) diff --git a/ledger/store/trackerdb/generickv/accounts_ext_writer.go b/ledger/store/trackerdb/generickv/accounts_ext_writer.go index bdb05b9138..c135f73c59 100644 --- a/ledger/store/trackerdb/generickv/accounts_ext_writer.go +++ b/ledger/store/trackerdb/generickv/accounts_ext_writer.go @@ -18,7 +18,6 @@ package generickv import ( "context" - "encoding/binary" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" @@ -51,16 +50,16 @@ func (w *accountsWriter) TxtailNewRound(ctx context.Context, baseRound basics.Ro // insert the new txTail's for i, data := range roundData { rnd := basics.Round(int(baseRound) + i) - err := w.kvw.Set(txTailKey(rnd), data) + key := txTailKey(rnd) + err := w.kvw.Set(key[:], data) if err != nil { return err } } // delete old ones - start := []byte(kvTxTail + "-") - end := txTailKey(forgetBeforeRound) - err := w.kvw.DeleteRange(start, end) + start, end := txTailRoundRangePrefix(forgetBeforeRound) + err := w.kvw.DeleteRange(start[:], end[:]) if err != nil { return err } @@ -78,7 +77,8 @@ func (w *accountsWriter) UpdateAccountsRound(rnd basics.Round) (err error) { // write round entry raw := bigEndianUint64(uint64(rnd)) - err = w.kvw.Set(roundKey(), raw) + key := roundKey() + err = w.kvw.Set(key[:], raw[:]) if err != nil { return err } @@ -104,7 +104,8 @@ func (w *accountsWriter) AccountsPutTotals(totals ledgercore.AccountTotals, catc // write totals entry raw := protocol.Encode(&totals) - err = w.kvw.Set(totalsKey(catchpointStaging), raw) + key := totalsKey(catchpointStaging) + err = w.kvw.Set(key[:], raw) if err != nil { return err } @@ -131,13 +132,13 @@ func (w *accountsWriter) OnlineAccountsDelete(forgetBefore basics.Round) (err er // - the `onlineAccountKey(address, round)` -> "-".join(kvPrefixOnlineAccount, addr, round) // - and the `onlineAccountBalanceKey(round, normBalance, addr) -> "-".join(kvPrefixOnlineAccountBalance, round, normBalance, addr) - // 1. read from the `onlineAccountBalanceKey` range since we can the addr's that will need to be deleted - start := []byte(kvPrefixOnlineAccountBalance + "-") - end := []byte(kvPrefixOnlineAccountBalance + "-") - end = append(end, bigEndianUint64(uint64(forgetBefore))...) - iter := w.kvr.NewIter(start, end, true) + // 1. read from the `onlineAccountBalanceKey` range since we need the addresses that will need to be deleted + start, end := onlineAccountBalanceForRoundRangePrefix(forgetBefore) + iter := w.kvr.NewIter(start[:], end[:], true) defer iter.Close() + seenAddrs := make(map[basics.Address]struct{}) + toDeletePrimaryIndex := make([]struct { basics.Address basics.Round @@ -145,33 +146,22 @@ func (w *accountsWriter) OnlineAccountsDelete(forgetBefore basics.Round) (err er toDeleteSecondaryIndex := make([][]byte, 0) - var prevAddr basics.Address - + // loop through the rounds in reverse order (latest first) for iter.Next() { - // read the key - // schema: --- key := iter.Key() - // extract the round from the key (offset: 1) - rndOffset := len(kvPrefixOnlineAccountBalance) + 1 - u64Rnd := binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - round := basics.Round(u64Rnd) - - // get the offset where the address starts - addrOffset := len(kvPrefixOnlineAccountBalance) + 1 + 8 + 1 + 8 + 1 - var addr basics.Address - copy(addr[:], key[addrOffset:addrOffset+32]) + // extract address & round from the key + addr := extractOnlineAccountBalanceAddress(key) + round := extractOnlineAccountBalanceRound(key) - if addr != prevAddr { + // check that we have NOT seen this address before + if _, ok := seenAddrs[addr]; !ok { // new address - // if the first (latest) entry is - // - offline then delete all - // - online then safe to delete all previous except this first (latest) + // if the first time (latest in rnd, order reversed) we see it the entry is: + // - offline -> then delete all + // - online -> then safe to delete all previous except this first (latest) - // reset the state - prevAddr = addr - - // delete on voting empty + // check if voting data is empty (it means the account is offline) var oad trackerdb.BaseOnlineAccountData var data []byte data, err = iter.Value() @@ -183,7 +173,7 @@ func (w *accountsWriter) OnlineAccountsDelete(forgetBefore basics.Round) (err er return err } if oad.IsVotingEmpty() { - // delete this and all subsequent + // delete this entry (all subsequent will be deleted too outside the if) toDeletePrimaryIndex = append(toDeletePrimaryIndex, struct { basics.Address basics.Round @@ -191,6 +181,9 @@ func (w *accountsWriter) OnlineAccountsDelete(forgetBefore basics.Round) (err er toDeleteSecondaryIndex = append(toDeleteSecondaryIndex, key) } + // mark addr as seen + seenAddrs[addr] = struct{}{} + // restart the loop // if there are some subsequent entries, they will deleted on the next iteration // if no subsequent entries, the loop will reset the state and the latest entry does not get deleted @@ -208,7 +201,8 @@ func (w *accountsWriter) OnlineAccountsDelete(forgetBefore basics.Round) (err er // 2. delete the individual addr+round entries for _, item := range toDeletePrimaryIndex { // TODO: [perf] we might be able to optimize this with a SingleDelete call - err = w.kvw.Delete(onlineAccountKey(item.Address, item.Round)) + key := onlineAccountKey(item.Address, item.Round) + err = w.kvw.Delete(key[:]) if err != nil { return } @@ -239,7 +233,8 @@ func (w *accountsWriter) AccountsPutOnlineRoundParams(onlineRoundParamsData []le for i := range onlineRoundParamsData { rnd := basics.Round(int(startRound) + i) raw := protocol.Encode(&onlineRoundParamsData[i]) - err := w.kvw.Set(onlineAccountRoundParamsKey(rnd), raw) + key := onlineAccountRoundParamsKey(rnd) + err := w.kvw.Set(key[:], raw) if err != nil { return err } @@ -254,9 +249,8 @@ func (w *accountsWriter) AccountsPruneOnlineRoundParams(deleteBeforeRound basics // DELETE FROM onlineroundparamstail WHERE rnd-- = key := iter.Key() - aidxOffset := len(kvPrefixResource) + 1 + 32 + 1 - aidx = binary.BigEndian.Uint64(key[aidxOffset : aidxOffset+8]) - pitem.Aidx = basics.CreatableIndex(aidx) + // extract aidx from key + pitem.Aidx = extractResourceAidx(key) // get value for current item in the iterator value, err = iter.Value() @@ -284,7 +279,8 @@ func (r *accountsReader) LookupCreator(cidx basics.CreatableIndex, ctype basics. return } - value, closer, err := r.kvr.Get(creatableKey(cidx)) + key := creatableKey(cidx) + value, closer, err := r.kvr.Get(key[:]) if err == trackerdb.ErrNotFound { // the record does not exist // clean up the error and just return ok=false diff --git a/ledger/store/trackerdb/generickv/accounts_writer.go b/ledger/store/trackerdb/generickv/accounts_writer.go index 0a4db7f3cf..42530e7681 100644 --- a/ledger/store/trackerdb/generickv/accounts_writer.go +++ b/ledger/store/trackerdb/generickv/accounts_writer.go @@ -70,7 +70,8 @@ func MakeAccountsWriter(kvw KvWrite, kvr KvRead) *accountsWriter { func (w *accountsWriter) InsertAccount(addr basics.Address, normBalance uint64, data trackerdb.BaseAccountData) (ref trackerdb.AccountRef, err error) { // write account entry raw := protocol.Encode(&data) - err = w.kvw.Set(accountKey(addr), raw) + key := accountKey(addr) + err = w.kvw.Set(key[:], raw) if err != nil { return nil, err } @@ -82,7 +83,8 @@ func (w *accountsWriter) DeleteAccount(ref trackerdb.AccountRef) (rowsAffected i xref := ref.(accountRef) // delete account entry - err = w.kvw.Delete(accountKey(xref.addr)) + key := accountKey(xref.addr) + err = w.kvw.Delete(key[:]) if err != nil { return 0, err } @@ -95,7 +97,8 @@ func (w *accountsWriter) UpdateAccount(ref trackerdb.AccountRef, normBalance uin // overwrite account entry raw := protocol.Encode(&data) - err = w.kvw.Set(accountKey(xref.addr), raw) + key := accountKey(xref.addr) + err = w.kvw.Set(key[:], raw) if err != nil { return 0, err } @@ -108,7 +111,8 @@ func (w *accountsWriter) InsertResource(acctRef trackerdb.AccountRef, aidx basic // write resource entry raw := protocol.Encode(&data) - err = w.kvw.Set(resourceKey(xref.addr, aidx), raw) + key := resourceKey(xref.addr, aidx) + err = w.kvw.Set(key[:], raw) if err != nil { return nil, err } @@ -120,7 +124,8 @@ func (w *accountsWriter) DeleteResource(acctRef trackerdb.AccountRef, aidx basic xref := acctRef.(accountRef) // delete resource entry - err = w.kvw.Delete(resourceKey(xref.addr, aidx)) + key := resourceKey(xref.addr, aidx) + err = w.kvw.Delete(key[:]) if err != nil { return 0, err } @@ -133,7 +138,8 @@ func (w *accountsWriter) UpdateResource(acctRef trackerdb.AccountRef, aidx basic // update resource entry raw := protocol.Encode(&data) - err = w.kvw.Set(resourceKey(xref.addr, aidx), raw) + key := resourceKey(xref.addr, aidx) + err = w.kvw.Set(key[:], raw) if err != nil { return 0, err } @@ -170,7 +176,8 @@ type creatableEntry struct { func (w *accountsWriter) InsertCreatable(cidx basics.CreatableIndex, ctype basics.CreatableType, creator []byte) (ref trackerdb.CreatableRef, err error) { // insert creatable entry raw := protocol.Encode(&creatableEntry{Ctype: ctype, CreatorAddr: creator}) - err = w.kvw.Set(creatableKey(cidx), raw) + key := creatableKey(cidx) + err = w.kvw.Set(key[:], raw) if err != nil { return } @@ -180,7 +187,8 @@ func (w *accountsWriter) InsertCreatable(cidx basics.CreatableIndex, ctype basic func (w *accountsWriter) DeleteCreatable(cidx basics.CreatableIndex, ctype basics.CreatableType) (rowsAffected int64, err error) { // delete creatable entry - err = w.kvw.Delete(creatableKey(cidx)) + key := creatableKey(cidx) + err = w.kvw.Delete(key[:]) if err != nil { return 0, err } diff --git a/ledger/store/trackerdb/generickv/migrations.go b/ledger/store/trackerdb/generickv/migrations.go index 9c8e942e39..e3d04ba5cf 100644 --- a/ledger/store/trackerdb/generickv/migrations.go +++ b/ledger/store/trackerdb/generickv/migrations.go @@ -29,7 +29,8 @@ import ( func getSchemaVersion(ctx context.Context, kvr KvRead) (int32, error) { // read version entry - value, closer, err := kvr.Get(schemaVersionKey()) + key := schemaVersionKey() + value, closer, err := kvr.Get(key[:]) if err == trackerdb.ErrNotFound { // ignore the error, return version 0 return 0, nil @@ -47,7 +48,8 @@ func getSchemaVersion(ctx context.Context, kvr KvRead) (int32, error) { func setSchemaVersion(ctx context.Context, kvw KvWrite, version int32) error { // write version entry raw := bigEndianUint32(uint32(version)) - err := kvw.Set(schemaVersionKey(), raw) + key := schemaVersionKey() + err := kvw.Set(key[:], raw[:]) if err != nil { return err } diff --git a/ledger/store/trackerdb/generickv/msgp_gen.go b/ledger/store/trackerdb/generickv/msgp_gen.go index 214b08500c..026677c1bc 100644 --- a/ledger/store/trackerdb/generickv/msgp_gen.go +++ b/ledger/store/trackerdb/generickv/msgp_gen.go @@ -58,11 +58,11 @@ func (_ *creatableEntry) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *creatableEntry) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/ledger/store/trackerdb/generickv/onlineaccounts_reader.go b/ledger/store/trackerdb/generickv/onlineaccounts_reader.go index fb85ef89f8..573bf478f5 100644 --- a/ledger/store/trackerdb/generickv/onlineaccounts_reader.go +++ b/ledger/store/trackerdb/generickv/onlineaccounts_reader.go @@ -17,8 +17,6 @@ package generickv import ( - "encoding/binary" - "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store/trackerdb" @@ -44,29 +42,23 @@ func (r *accountsReader) LookupOnline(addr basics.Address, rnd basics.Round) (da return } + // addr is set in the sqlite impl even if we dont find the account + data.Addr = addr + // read latest account up to `rnd`` - low := onlineAccountOnlyPartialKey(addr) - high := onlineAccountKey(addr, rnd) - // inc the last byte to make it inclusive - high[len(high)-1]++ - iter := r.kvr.NewIter(low, high, true) + low, high := onlineAccountLatestRangePrefix(addr, rnd) + iter := r.kvr.NewIter(low[:], high[:], true) defer iter.Close() var value []byte - var updRound uint64 if iter.Next() { - // schema: -- key := iter.Key() - // extract updround, its the last section after the "-" - rndOffset := len(kvPrefixOnlineAccount) + 1 + 32 + 1 - updRound = binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - if err != nil { - return - } - data.Addr = addr - data.UpdRound = basics.Round(updRound) + // extract round + updRound := extractOnlineAccountRound(key) + + data.UpdRound = updRound // get value for current item in the iterator value, err = iter.Value() @@ -93,14 +85,11 @@ func (r *accountsReader) LookupOnline(addr basics.Address, rnd basics.Round) (da } func (r *accountsReader) LookupOnlineHistory(addr basics.Address) (result []trackerdb.PersistedOnlineAccountData, rnd basics.Round, err error) { - low := onlineAccountOnlyPartialKey(addr) - high := onlineAccountOnlyPartialKey(addr) - high[len(high)-1]++ - iter := r.kvr.NewIter(low, high, false) + low, high := onlineAccountAddressRangePrefix(addr) + iter := r.kvr.NewIter(low[:], high[:], false) defer iter.Close() var value []byte - var updround uint64 // read the current db round rnd, err = r.AccountsRound() @@ -111,16 +100,13 @@ func (r *accountsReader) LookupOnlineHistory(addr basics.Address) (result []trac for iter.Next() { pitem := trackerdb.PersistedOnlineAccountData{} - // schema: -- key := iter.Key() - // extract updround, its the last section after the "-" - rndOffset := len(kvPrefixOnlineAccount) + 1 + 32 + 1 - updround = binary.BigEndian.Uint64(key[rndOffset : rndOffset+8]) - if err != nil { - return - } + + // extract round + updRound := extractOnlineAccountRound(key) + pitem.Addr = addr - pitem.UpdRound = basics.Round(updround) + pitem.UpdRound = updRound // Note: for compatibility with the SQL impl, this is not included on each item // pitem.Round = rnd @@ -152,7 +138,8 @@ func (r *accountsReader) LookupOnlineRoundParams(rnd basics.Round) (onlineRoundP // FROM onlineroundparamstail // WHERE rnd=? - value, closer, err := r.kvr.Get(onlineAccountRoundParamsKey(rnd)) + key := onlineAccountRoundParamsKey(rnd) + value, closer, err := r.kvr.Get(key[:]) if err != nil { return } diff --git a/ledger/store/trackerdb/generickv/onlineaccounts_writer.go b/ledger/store/trackerdb/generickv/onlineaccounts_writer.go index cca847c608..054d75e87f 100644 --- a/ledger/store/trackerdb/generickv/onlineaccounts_writer.go +++ b/ledger/store/trackerdb/generickv/onlineaccounts_writer.go @@ -44,7 +44,8 @@ func (w *onlineAccountsWriter) InsertOnlineAccount(addr basics.Address, normBala rnd := basics.Round(updRound) // write to the online account key - err = w.kvw.Set(onlineAccountKey(addr, rnd), raw) + key := onlineAccountKey(addr, rnd) + err = w.kvw.Set(key[:], raw) if err != nil { return nil, err } @@ -52,7 +53,8 @@ func (w *onlineAccountsWriter) InsertOnlineAccount(addr basics.Address, normBala // write to the secondary account balance key // TODO: this is not the most efficient use of space, but its a tradeoff with a second lookup per object. // this impacts `AccountsOnlineTop`, and some experiments will be needed to determine if we do extra lookups or duplicate the values. - err = w.kvw.Set(onlineAccountBalanceKey(updRound, normBalance, addr), raw) + bKey := onlineAccountBalanceKey(rnd, normBalance, addr) + err = w.kvw.Set(bKey[:], raw) if err != nil { return nil, err } diff --git a/ledger/store/trackerdb/generickv/schema.go b/ledger/store/trackerdb/generickv/schema.go index 8afb41e9e2..0c3abf8943 100644 --- a/ledger/store/trackerdb/generickv/schema.go +++ b/ledger/store/trackerdb/generickv/schema.go @@ -23,149 +23,378 @@ import ( ) const ( - kvPrefixAccount = "account" - kvPrefixResource = "resource" - kvPrefixAppKv = "appkv" - kvPrefixCreatorIndex = "creator" - kvPrefixOnlineAccount = "online_account_base" - kvPrefixOnlineAccountBalance = "online_account_balance" - kvRoundKey = "global_round" - kvSchemaVersionKey = "global_schema_version" - kvTotalsKey = "global_total" - kvTxTail = "txtail" - kvOnlineAccountRoundParams = "online_account_round_params" - kvPrefixStateproof = "stateproofs" + prefixLength = 2 + separatorLength = 1 + addressLength = 32 + roundLength = 8 +) + +const ( + kvPrefixAccount = "xa" + kvPrefixResource = "xb" + kvPrefixAppKv = "xc" + kvPrefixCreatorIndex = "xd" + kvPrefixOnlineAccount = "xe" + kvPrefixOnlineAccountBalance = "xf" + kvRoundKey = "xg" + kvSchemaVersionKey = "xh" + kvTotalsKey = "xi" + kvTxTail = "xj" + kvOnlineAccountRoundParams = "xk" + kvPrefixStateproof = "xl" +) + +const ( + // this is the true separator used in the keys + separator = '-' + // this is used as a value greather than the `separator` to get all the keys with a given prefix + endRangeSeparator = '.' ) // return the big-endian binary encoding of a uint64 -func bigEndianUint64(v uint64) []byte { - ret := make([]byte, 8) - binary.BigEndian.PutUint64(ret, v) +func bigEndianUint64(v uint64) [8]byte { + var ret [8]byte + binary.BigEndian.PutUint64(ret[:], v) return ret } // return the big-endian binary encoding of a uint32 -func bigEndianUint32(v uint32) []byte { - ret := make([]byte, 4) - binary.BigEndian.PutUint32(ret, v) +func bigEndianUint32(v uint32) [4]byte { + var ret [4]byte + binary.BigEndian.PutUint32(ret[:], v) return ret } -// accountKey: 4-byte prefix + 32-byte address -func accountKey(address basics.Address) []byte { - ret := []byte(kvPrefixAccount) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - return ret +func accountKey(address basics.Address) [35]byte { + var key [prefixLength + separatorLength + addressLength]byte + + copy(key[0:], kvPrefixAccount) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], address[:]) + + return key } -// resourceKey: 4-byte prefix + 32-byte address + 8-byte big-endian uint64 -func resourceKey(address basics.Address, aidx basics.CreatableIndex) []byte { - ret := []byte(kvPrefixResource) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(aidx))...) - return ret +func extractResourceAidx(key []byte) basics.CreatableIndex { + const offset int = prefixLength + separatorLength + addressLength + separatorLength + aidx64 := binary.BigEndian.Uint64(key[offset : offset+8]) + return basics.CreatableIndex(aidx64) } -func resourceAddrOnlyPartialKey(address basics.Address) []byte { - ret := []byte(kvPrefixResource) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - ret = append(ret, "-"...) - return ret +// TODO: [Review] discuss if we want/need to have the address as part of the key +func resourceKey(address basics.Address, aidx basics.CreatableIndex) [44]byte { + var key [prefixLength + separatorLength + addressLength + separatorLength + 8]byte + + copy(key[0:], kvPrefixResource) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], address[:]) + key[prefixLength+separatorLength+addressLength] = separator + + aidx8 := bigEndianUint64(uint64(aidx)) + copy(key[prefixLength+separatorLength+addressLength+separatorLength:], aidx8[:]) + + return key } -func appKvKey(key string) []byte { - ret := []byte(kvPrefixAppKv) - ret = append(ret, "-"...) - ret = append(ret, key...) - return ret +func resourceAddrOnlyRangePrefix(address basics.Address) ([36]byte, [36]byte) { + var low, high [prefixLength + separatorLength + addressLength + separatorLength]byte + + // low + copy(low[0:], kvPrefixResource) + low[prefixLength] = separator + copy(low[prefixLength+separatorLength:], address[:]) + low[prefixLength+separatorLength+addressLength] = separator + // high + copy(high[:], low[:]) + high[prefixLength+separatorLength+addressLength] = endRangeSeparator + + return low, high } -func creatableKey(cidx basics.CreatableIndex) []byte { - ret := []byte(kvPrefixCreatorIndex) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(cidx))...) - return ret +func appKvKey(kvKey string) []byte { + key := make([]byte, 0, prefixLength+separatorLength+len(kvKey)) + + key = append(key, kvPrefixAppKv...) + key = append(key, separator) + key = append(key, kvKey...) + + return key } -func onlineAccountKey(address basics.Address, round basics.Round) []byte { - ret := []byte(kvPrefixOnlineAccount) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(round))...) - return ret +func creatableKey(cidx basics.CreatableIndex) [11]byte { + var key [prefixLength + separatorLength + 8]byte + + copy(key[0:], kvPrefixCreatorIndex) + key[prefixLength] = separator + + cidx8 := bigEndianUint64(uint64(cidx)) + copy(key[prefixLength+separatorLength:], cidx8[:]) + + return key } -func onlineAccountOnlyPartialKey(address basics.Address) []byte { - ret := []byte(kvPrefixOnlineAccount) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - ret = append(ret, "-"...) - return ret +func creatableMaxRangePrefix(maxIdx basics.CreatableIndex) ([3]byte, [11]byte) { + var low [prefixLength + separatorLength]byte + + copy(low[0:], kvPrefixCreatorIndex) + low[prefixLength] = separator + + high := creatableKey(basics.CreatableIndex(uint64(maxIdx) + 1)) + + return low, high } -// TODO: use basics.Round -func onlineAccountBalanceKey(round uint64, normBalance uint64, address basics.Address) []byte { - ret := []byte(kvPrefixOnlineAccountBalance) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(round)...) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(normBalance)...) - ret = append(ret, "-"...) - ret = append(ret, address[:]...) - return ret +func extractOnlineAccountAddress(key []byte) (addr basics.Address) { + const offset int = prefixLength + separatorLength + copy(addr[:], key[offset:]) + return } -func onlineAccountBalanceOnlyPartialKey(round basics.Round) []byte { - ret := []byte(kvPrefixOnlineAccountBalance) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(round))...) - ret = append(ret, "-"...) - return ret +func extractOnlineAccountRound(key []byte) basics.Round { + const offset int = prefixLength + separatorLength + addressLength + separatorLength + u64Rnd := binary.BigEndian.Uint64(key[offset : offset+roundLength]) + return basics.Round(u64Rnd) } -func roundKey() []byte { - ret := []byte(kvRoundKey) - return ret +func onlineAccountKey(address basics.Address, round basics.Round) [44]byte { + var key [prefixLength + separatorLength + addressLength + separatorLength + 8]byte + + copy(key[0:], kvPrefixOnlineAccount) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], address[:]) + key[prefixLength+separatorLength+addressLength] = separator + + round8 := bigEndianUint64(uint64(round)) + copy(key[prefixLength+separatorLength+addressLength+separatorLength:], round8[:]) + + return key } -func schemaVersionKey() []byte { - ret := []byte(kvSchemaVersionKey) - return ret +func onlineAccountLatestRangePrefix(address basics.Address, round basics.Round) ([36]byte, [44]byte) { + low := onlineAccountOnlyPartialKey(address) + high := onlineAccountKey(address, round) + // inc the last byte to make it inclusive + high[len(high)-1]++ + + return low, high +} + +func onlineAccountAddressRangePrefix(address basics.Address) ([36]byte, [36]byte) { + low := onlineAccountOnlyPartialKey(address) + high := onlineAccountOnlyPartialKey(address) + high[prefixLength+separatorLength+addressLength] = endRangeSeparator + + return low, high +} + +func onlineAccountFullRangePrefix() ([3]byte, [3]byte) { + var low, high [prefixLength + separatorLength]byte + + copy(low[0:], kvPrefixOnlineAccount) + low[prefixLength] = separator + + copy(high[0:], kvPrefixOnlineAccount) + high[prefixLength] = endRangeSeparator + + return low, high +} + +func onlineAccountOnlyPartialKey(address basics.Address) [36]byte { + var key [prefixLength + separatorLength + addressLength + separatorLength]byte + + copy(key[0:], kvPrefixOnlineAccount) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], address[:]) + key[prefixLength+separatorLength+addressLength] = separator + + return key +} + +func extractOnlineAccountBalanceAddress(key []byte) (addr basics.Address) { + const offset int = prefixLength + separatorLength + 8 + separatorLength + 8 + separatorLength + copy(addr[:], key[offset:]) + return +} + +func extractOnlineAccountBalanceRound(key []byte) basics.Round { + const offset int = prefixLength + separatorLength + u64Rnd := binary.BigEndian.Uint64(key[offset : offset+roundLength]) + return basics.Round(u64Rnd) +} + +func onlineAccountBalanceKey(round basics.Round, normBalance uint64, address basics.Address) [53]byte { + var key [prefixLength + separatorLength + 8 + separatorLength + 8 + separatorLength + addressLength]byte + + round8 := bigEndianUint64(uint64(round)) + normBalance8 := bigEndianUint64(normBalance) + + copy(key[0:], kvPrefixOnlineAccountBalance) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], round8[:]) + key[prefixLength+separatorLength+8] = separator + copy(key[prefixLength+separatorLength+8+separatorLength:], normBalance8[:]) + key[prefixLength+separatorLength+8+separatorLength+8] = separator + copy(key[prefixLength+separatorLength+8+separatorLength+8+separatorLength:], address[:]) + + return key +} + +func onlineAccountBalanceForRoundRangePrefix(round basics.Round) ([3]byte, [12]byte) { + var low [prefixLength + separatorLength]byte + copy(low[0:], kvPrefixOnlineAccountBalance) + low[prefixLength] = separator + + var high [prefixLength + separatorLength + 8 + separatorLength]byte + + round8 := bigEndianUint64(uint64(round)) + + copy(high[0:], kvPrefixOnlineAccountBalance) + high[prefixLength] = separator + copy(high[prefixLength+separatorLength:], round8[:]) + high[prefixLength+separatorLength+8] = endRangeSeparator + + return low, high +} + +func roundKey() [2]byte { + var key [prefixLength]byte + copy(key[0:], kvRoundKey) + return key } -func totalsKey(catchpointStaging bool) []byte { - ret := []byte(kvTotalsKey) - ret = append(ret, "-"...) +func schemaVersionKey() [2]byte { + var key [prefixLength]byte + copy(key[0:], kvSchemaVersionKey) + return key +} + +func totalsKey(catchpointStaging bool) [4]byte { + var key [prefixLength + separatorLength + 1]byte + + copy(key[0:], kvTotalsKey) + key[prefixLength] = separator if catchpointStaging { - ret = append(ret, "staging"...) + key[prefixLength+separatorLength] = 's' } else { - ret = append(ret, "live"...) + key[prefixLength+separatorLength] = 'l' } - return ret + + return key } -func txTailKey(rnd basics.Round) []byte { - ret := []byte(kvTxTail) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(rnd))...) - return ret +func extractTxTailRoundPart(key []byte) basics.Round { + const offset int = prefixLength + separatorLength + u64Rnd := binary.BigEndian.Uint64(key[offset : offset+roundLength]) + return basics.Round(u64Rnd) } -func onlineAccountRoundParamsKey(rnd basics.Round) []byte { - ret := []byte(kvOnlineAccountRoundParams) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(rnd))...) - return ret +func txTailKey(rnd basics.Round) [11]byte { + var key [prefixLength + separatorLength + 8]byte + + rnd8 := bigEndianUint64(uint64(rnd)) + + copy(key[0:], kvTxTail) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], rnd8[:]) + + return key } -func stateproofKey(rnd basics.Round) []byte { - ret := []byte(kvPrefixStateproof) - ret = append(ret, "-"...) - ret = append(ret, bigEndianUint64(uint64(rnd))...) - return ret +func txTailRoundRangePrefix(rnd basics.Round) ([3]byte, [11]byte) { + var low [prefixLength + separatorLength]byte + + copy(low[0:], kvTxTail) + low[prefixLength] = separator + + high := txTailKey(rnd) + + return low, high +} + +func txTailFullRangePrefix() ([3]byte, [3]byte) { + var low, high [prefixLength + separatorLength]byte + + copy(low[0:], kvTxTail) + low[prefixLength] = separator + + copy(high[0:], kvTxTail) + high[prefixLength] = endRangeSeparator + + return low, high +} + +func extractOnlineAccountRoundParamsRoundPart(key []byte) basics.Round { + const offset int = prefixLength + separatorLength + u64Rnd := binary.BigEndian.Uint64(key[offset : offset+roundLength]) + return basics.Round(u64Rnd) +} + +func onlineAccountRoundParamsKey(rnd basics.Round) [11]byte { + var key [prefixLength + separatorLength + 8]byte + + rnd8 := bigEndianUint64(uint64(rnd)) + + copy(key[0:], kvOnlineAccountRoundParams) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], rnd8[:]) + + return key +} + +func onlineAccountRoundParamsRoundRangePrefix(rnd basics.Round) ([3]byte, [11]byte) { + var low [prefixLength + separatorLength]byte + + copy(low[0:], kvOnlineAccountRoundParams) + low[prefixLength] = separator + + high := onlineAccountRoundParamsKey(rnd) + + return low, high +} + +func onlineAccountRoundParamsFullRangePrefix() ([3]byte, [3]byte) { + var low, high [prefixLength + separatorLength]byte + + copy(low[0:], kvOnlineAccountRoundParams) + low[prefixLength] = separator + + copy(high[0:], kvOnlineAccountRoundParams) + high[prefixLength] = endRangeSeparator + + return low, high +} + +func stateproofKey(rnd basics.Round) [11]byte { + var key [prefixLength + separatorLength + 8]byte + + rnd8 := bigEndianUint64(uint64(rnd)) + + copy(key[0:], kvPrefixStateproof) + key[prefixLength] = separator + copy(key[prefixLength+separatorLength:], rnd8[:]) + + return key +} + +func stateproofRoundRangePrefix(rnd basics.Round) ([3]byte, [11]byte) { + var low [prefixLength + separatorLength]byte + + copy(low[0:], kvPrefixStateproof) + low[prefixLength] = separator + + high := stateproofKey(rnd) + + return low, high +} + +func stateproofFullRangePrefix() ([3]byte, [3]byte) { + var low, high [prefixLength + separatorLength]byte + + copy(low[0:], kvPrefixStateproof) + low[prefixLength] = separator + + copy(high[0:], kvPrefixStateproof) + high[prefixLength] = endRangeSeparator + + return low, high } diff --git a/ledger/store/trackerdb/generickv/stateproof_reader.go b/ledger/store/trackerdb/generickv/stateproof_reader.go index 0c3ed62b58..18bce01c4a 100644 --- a/ledger/store/trackerdb/generickv/stateproof_reader.go +++ b/ledger/store/trackerdb/generickv/stateproof_reader.go @@ -43,7 +43,8 @@ func (r *stateproofReader) LookupSPContext(stateProofLastAttestedRound basics.Ro // FROM stateproofverification // WHERE lastattestedround=? - value, closer, err := r.kvr.Get(stateproofKey(stateProofLastAttestedRound)) + key := stateproofKey(stateProofLastAttestedRound) + value, closer, err := r.kvr.Get(key[:]) if err != nil { return nil, err } @@ -67,9 +68,8 @@ func (r *stateproofReader) GetAllSPContexts(ctx context.Context) ([]ledgercore.S // FROM stateProofVerification // ORDER BY lastattestedround - low := []byte(kvPrefixStateproof + "-") - high := []byte(kvPrefixStateproof + ".") - iter := r.kvr.NewIter(low, high, false) + low, high := stateproofFullRangePrefix() + iter := r.kvr.NewIter(low[:], high[:], false) defer iter.Close() results := make([]ledgercore.StateProofVerificationContext, 0) diff --git a/ledger/store/trackerdb/generickv/stateproof_writer.go b/ledger/store/trackerdb/generickv/stateproof_writer.go index 42565d9edc..9aa7145185 100644 --- a/ledger/store/trackerdb/generickv/stateproof_writer.go +++ b/ledger/store/trackerdb/generickv/stateproof_writer.go @@ -47,7 +47,8 @@ func (w *stateproofWriter) StoreSPContexts(ctx context.Context, verificationCont // write stateproof entry vc := verificationContext[i] raw := protocol.Encode(vc) - err := w.kvw.Set(stateproofKey(vc.LastAttestedRound), raw) + key := stateproofKey(vc.LastAttestedRound) + err := w.kvw.Set(key[:], raw) if err != nil { return err } @@ -63,10 +64,8 @@ func (w *stateproofWriter) DeleteOldSPContexts(ctx context.Context, earliestLast // DELETE FROM stateproofverification // WHERE lastattestedround < ? - start := []byte(kvPrefixStateproof + "-") - end := stateproofKey(earliestLastAttestedRound) - - return w.kvw.DeleteRange(start, end) + start, end := stateproofRoundRangePrefix(earliestLastAttestedRound) + return w.kvw.DeleteRange(start[:], end[:]) } // StoreSPContextsToCatchpointTbl implements trackerdb.SpVerificationCtxWriter diff --git a/ledger/store/trackerdb/msgp_gen.go b/ledger/store/trackerdb/msgp_gen.go index fe2f6cd4a9..a13469c0ab 100644 --- a/ledger/store/trackerdb/msgp_gen.go +++ b/ledger/store/trackerdb/msgp_gen.go @@ -305,11 +305,11 @@ func (_ *BaseAccountData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BaseAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -768,11 +768,11 @@ func (_ *BaseOnlineAccountData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BaseOnlineAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1031,11 +1031,11 @@ func (_ *BaseVotingData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *BaseVotingData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1275,11 +1275,11 @@ func (_ *CatchpointFirstStageInfo) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *CatchpointFirstStageInfo) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -1467,11 +1467,11 @@ func (_ ResourceFlags) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ResourceFlags) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint8 zb0001, bts, err = msgp.ReadUint8Bytes(bts) @@ -1764,11 +1764,11 @@ func (_ *ResourcesData) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ResourcesData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -2339,11 +2339,11 @@ func (_ *TxTailRound) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TxTailRound) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0004 int @@ -2636,11 +2636,11 @@ func (_ *TxTailRoundLease) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TxTailRoundLease) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int diff --git a/ledger/store/trackerdb/sqlitedriver/merkle_commiter.go b/ledger/store/trackerdb/sqlitedriver/merkle_committer.go similarity index 100% rename from ledger/store/trackerdb/sqlitedriver/merkle_commiter.go rename to ledger/store/trackerdb/sqlitedriver/merkle_committer.go diff --git a/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go b/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go index 9062587b99..f4064658c3 100644 --- a/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go +++ b/ledger/store/trackerdb/testsuite/onlineaccounts_kv_test.go @@ -335,104 +335,114 @@ func CustomTestOnlineAccountsDelete(t *customT) { aw, err := t.db.MakeAccountsWriter() require.NoError(t, err) - // set round to 3 + // generate some test data + + // timeline + // round 0: A touched [0], B touched [0] + // round 1: A touched [1,0] + // round 2: A touched [2,1,0] + B offline [0] + C touched [2] + + // set round // Note: this will be used to check that we read the round expectedRound := basics.Round(3) err = aw.UpdateAccountsRound(expectedRound) require.NoError(t, err) - // generate some test data + // rnd 0 + addrA := RandomAddress() - dataA1 := trackerdb.BaseOnlineAccountData{ + dataA0 := trackerdb.BaseOnlineAccountData{ // some value so its NOT empty BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1}, MicroAlgos: basics.MicroAlgos{Raw: uint64(20)}, RewardsBase: uint64(200), } - normalizedBalA1 := dataA1.NormalizedOnlineBalance(t.proto) + normalizedBalA0 := dataA0.NormalizedOnlineBalance(t.proto) - _, err = oaw.InsertOnlineAccount(addrA, normalizedBalA1, dataA1, uint64(0), uint64(2)) + _, err = oaw.InsertOnlineAccount(addrA, normalizedBalA0, dataA0, uint64(0), uint64(21)) require.NoError(t, err) - // generate some test data - dataA2 := trackerdb.BaseOnlineAccountData{ + addrB := RandomAddress() + dataB0 := trackerdb.BaseOnlineAccountData{ // some value so its NOT empty - BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1}, - MicroAlgos: basics.MicroAlgos{Raw: uint64(100)}, + BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1, VoteLastValid: basics.Round(2)}, + MicroAlgos: basics.MicroAlgos{Raw: uint64(75)}, RewardsBase: uint64(200), } - normalizedBalA2 := dataA2.NormalizedOnlineBalance(t.proto) + normalizedBalB2 := dataB0.NormalizedOnlineBalance(t.proto) - _, err = oaw.InsertOnlineAccount(addrA, normalizedBalA2, dataA2, uint64(1), uint64(3)) + _, err = oaw.InsertOnlineAccount(addrB, normalizedBalB2, dataB0, uint64(0), uint64(2)) require.NoError(t, err) - // generate some test data - addrB := RandomAddress() - dataB1 := trackerdb.BaseOnlineAccountData{ + // rnd 1 + + dataA1 := trackerdb.BaseOnlineAccountData{ // some value so its NOT empty BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1}, - MicroAlgos: basics.MicroAlgos{Raw: uint64(75)}, + MicroAlgos: basics.MicroAlgos{Raw: uint64(100)}, RewardsBase: uint64(200), } - normalizedBalB1 := dataB1.NormalizedOnlineBalance(t.proto) + normalizedBalA1 := dataA1.NormalizedOnlineBalance(t.proto) - _, err = oaw.InsertOnlineAccount(addrB, normalizedBalB1, dataB1, uint64(2), uint64(3)) + _, err = oaw.InsertOnlineAccount(addrA, normalizedBalA1, dataA1, uint64(1), uint64(21)) require.NoError(t, err) - // timeline - // round 0: A touched [0] - // round 1: A touched [1,0] - // round 2: B touched [2] + A remains [1,0] + // rnd 2 - // - // the test - // + dataA2 := trackerdb.BaseOnlineAccountData{ + // some value so its NOT empty + BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1}, + MicroAlgos: basics.MicroAlgos{Raw: uint64(187)}, + RewardsBase: uint64(200), + } + normalizedBalA2 := dataA1.NormalizedOnlineBalance(t.proto) - // delete before 0 (no changes) - err = aw.OnlineAccountsDelete(basics.Round(0)) + _, err = oaw.InsertOnlineAccount(addrA, normalizedBalA2, dataA2, uint64(2), uint64(21)) require.NoError(t, err) - // check they are all there - oas, err := ar.AccountsOnlineTop(basics.Round(0), 0, 10, t.proto) - require.NoError(t, err) - require.Len(t, oas, 1) - require.Equal(t, oas[addrA].MicroAlgos, basics.MicroAlgos{Raw: uint64(20)}) // check item - // read the accounts directly - poaA, err := oar.LookupOnline(addrA, basics.Round(0)) - require.NoError(t, err) - require.NotNil(t, poaA.Ref) // A was found + addrC := RandomAddress() + dataC2 := trackerdb.BaseOnlineAccountData{ + // some value so its NOT empty + BaseVotingData: trackerdb.BaseVotingData{VoteKeyDilution: 1}, + MicroAlgos: basics.MicroAlgos{Raw: uint64(721)}, + RewardsBase: uint64(200), + } + normalizedBalC2 := dataC2.NormalizedOnlineBalance(t.proto) - // delete before round 1 - err = aw.OnlineAccountsDelete(basics.Round(1)) + _, err = oaw.InsertOnlineAccount(addrC, normalizedBalC2, dataC2, uint64(2), uint64(21)) require.NoError(t, err) - // check they are all there - oas, err = ar.AccountsOnlineTop(basics.Round(1), 0, 10, t.proto) - require.NoError(t, err) - require.Len(t, oas, 1) - require.Equal(t, oas[addrA].MicroAlgos, basics.MicroAlgos{Raw: uint64(100)}) // check item - // read the accounts directly - poaA, err = oar.LookupOnline(addrA, basics.Round(1)) - require.NoError(t, err) - require.NotNil(t, poaA.Ref) // A was found + // + // the test + // - // delete before round 2 - err = aw.OnlineAccountsDelete(basics.Round(2)) + // delete before round 3 + err = aw.OnlineAccountsDelete(basics.Round(4)) require.NoError(t, err) - // check they are all there - oas, err = ar.AccountsOnlineTop(basics.Round(2), 0, 10, t.proto) + // check accounts + // expected: A touched [2], C touched [2] + oas, err := ar.AccountsOnlineTop(basics.Round(4), 0, 99, t.proto) + require.NoError(t, err) + require.Len(t, oas, 3) + require.Equal(t, oas[addrA].MicroAlgos, dataA2.MicroAlgos) // check item + require.Equal(t, oas[addrB].MicroAlgos, dataB0.MicroAlgos) // check item + require.Equal(t, oas[addrC].MicroAlgos, dataC2.MicroAlgos) // check item + // make sure A[0] was deleted + poa, err := oar.LookupOnline(addrA, basics.Round(0)) require.NoError(t, err) - require.Len(t, oas, 2) - require.Equal(t, oas[addrB].MicroAlgos, basics.MicroAlgos{Raw: uint64(75)}) // check item - // read the accounts directly - poaA, err = oar.LookupOnline(addrA, basics.Round(2)) + require.Nil(t, poa.Ref) // means "not found" + // make sure A[1] was deleted + poa, err = oar.LookupOnline(addrA, basics.Round(1)) require.NoError(t, err) - require.NotNil(t, poaA.Ref) // A is still found, the latest record is kept - require.Equal(t, basics.Round(1), poaA.UpdRound) // the latest we find is at 1 - poaB, err := oar.LookupOnline(addrB, basics.Round(2)) + require.Nil(t, poa.Ref) // means "not found" + // make sure B[0] was deleted + // Note: we actually dont check if it deleted, becase it is not + // the legacy code on SQL checks if VoteInfo is empty, not that the last valid has expired. + // therefore, this is not really deleted. + poa, err = oar.LookupOnline(addrB, basics.Round(0)) require.NoError(t, err) - require.NotNil(t, poaB.Ref) // B was found + require.NotNil(t, poa.Ref) // means we still have it } func CustomTestAccountsOnlineExpired(t *customT) { diff --git a/ledger/tracker_test.go b/ledger/tracker_test.go index 709d123d9e..730c315e80 100644 --- a/ledger/tracker_test.go +++ b/ledger/tracker_test.go @@ -69,7 +69,13 @@ func TestTrackerScheduleCommit(t *testing.T) { ct := &catchpointTracker{} ao := &onlineAccounts{} au.initialize(conf) - ct.initialize(conf, ".") + paths := DirsAndPrefix{ + ResolvedGenesisDirs: config.ResolvedGenesisDirs{ + CatchpointGenesisDir: ".", + HotGenesisDir: ".", + }, + } + ct.initialize(conf, paths) ao.initialize(conf) _, err := trackerDBInitialize(ml, false, ".") diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go index 2bc924bac9..ad98582387 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -1131,16 +1131,12 @@ func (c *Client) AbortCatchup() error { } // Catchup start catching up to the give catchpoint label. -func (c *Client) Catchup(catchpointLabel string) error { +func (c *Client) Catchup(catchpointLabel string, min uint64) (model.CatchpointStartResponse, error) { algod, err := c.ensureAlgodClient() if err != nil { - return err + return model.CatchpointStartResponse{}, err } - _, err = algod.Catchup(catchpointLabel) - if err != nil { - return err - } - return nil + return algod.Catchup(catchpointLabel, min) } const defaultAppIdx = 1380011588 diff --git a/logging/telemetryspec/event.go b/logging/telemetryspec/event.go index 4a8280591c..1617b0339c 100644 --- a/logging/telemetryspec/event.go +++ b/logging/telemetryspec/event.go @@ -89,14 +89,16 @@ const BlockAcceptedEvent Event = "BlockAccepted" // BlockAcceptedEventDetails contains details for the BlockAcceptedEvent type BlockAcceptedEventDetails struct { - Address string - Hash string - Round uint64 - ValidatedAt time.Duration - ReceivedAt time.Duration - PreValidated bool - PropBufLen uint64 - VoteBufLen uint64 + Address string + Hash string + Round uint64 + ValidatedAt time.Duration + ReceivedAt time.Duration + VoteValidatedAt time.Duration + DynamicFilterTimeout time.Duration + PreValidated bool + PropBufLen uint64 + VoteBufLen uint64 } // AccountRegisteredEvent event diff --git a/msgp/.gitignore b/msgp/.gitignore deleted file mode 100644 index 2d19289705..0000000000 --- a/msgp/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -_generated/generated.go -_generated/generated_test.go -_generated/*_gen.go -_generated/*_gen_test.go -_generated/embeddedStruct/*_gen.go -_generated/embeddedStruct/*_gen_test.go -msgp/defgen_test.go -msgp/cover.out -*~ -*.coverprofile -.idea/ -.vscode/ -cover.out diff --git a/msgp/.travis.yml b/msgp/.travis.yml deleted file mode 100644 index 5f574c093b..0000000000 --- a/msgp/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go - -go: - - 1.12.x - - tip - -env: - - GIMME_ARCH=amd64 - - GIMME_ARCH=386 - -script: "make travis" diff --git a/msgp/LICENSE b/msgp/LICENSE deleted file mode 100644 index 14d60424e8..0000000000 --- a/msgp/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright (c) 2014 Philip Hofer -Portions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/msgp/Makefile b/msgp/Makefile deleted file mode 100644 index a77d20d5ce..0000000000 --- a/msgp/Makefile +++ /dev/null @@ -1,47 +0,0 @@ - -# NOTE: This Makefile is only necessary if you -# plan on developing the msgp tool and library. -# Installation can still be performed with a -# normal `go install`. - -# generated unit test files -MGEN = ./msgp/defgen_test.go - -SHELL := /bin/bash - -BIN = $(GOBIN)/msgp - -.PHONY: clean wipe install get-deps bench all - -$(BIN): */*.go - @go install ./... - -install: $(BIN) - -$(MGEN): ./msgp/defs_test.go - go generate ./msgp - -test: all - go test -covermode=atomic -coverprofile=cover.out ./... - -bench: all - go test -bench ./... - -clean: - $(RM) $(MGEN) - -wipe: clean - $(RM) $(BIN) - -get-deps: - go get -d -t ./... - -all: install $(MGEN) - -# travis CI enters here -travis: - go get -d -t ./... - go build -o "$${GOPATH%%:*}/bin/msgp" . - go generate ./msgp - go generate ./_generated - go test -v ./... ./_generated diff --git a/msgp/README.md b/msgp/README.md deleted file mode 100644 index f5d3528a03..0000000000 --- a/msgp/README.md +++ /dev/null @@ -1,4 +0,0 @@ -MessagePack Code Generator -======= - -Based on [github.com/tinylib/msgp](https://github.com/tinylib/msgp) diff --git a/msgp/gen/elem.go b/msgp/gen/elem.go deleted file mode 100644 index 707b56bd94..0000000000 --- a/msgp/gen/elem.go +++ /dev/null @@ -1,992 +0,0 @@ -package gen - -import ( - "fmt" - "go/ast" - "strconv" - "strings" -) - -var ( - identNext = 0 - identPrefix = "za" -) - -func resetIdent(prefix string) { - identPrefix = prefix - identNext = 0 -} - -// generate a random identifier name -func randIdent() string { - identNext++ - return fmt.Sprintf("%s%04d", identPrefix, identNext) -} - -// This code defines the type declaration tree. -// -// Consider the following: -// -// type Marshaler struct { -// Thing1 *float64 `msg:"thing1"` -// Body []byte `msg:"body"` -// } -// -// A parser using this generator as a backend -// should parse the above into: -// -// var val Elem = &Ptr{ -// name: "z", -// Value: &Struct{ -// Name: "Marshaler", -// Fields: []StructField{ -// { -// FieldTag: "thing1", -// FieldElem: &Ptr{ -// name: "z.Thing1", -// Value: &BaseElem{ -// name: "*z.Thing1", -// Value: Float64, -// Convert: false, -// }, -// }, -// }, -// { -// FieldTag: "body", -// FieldElem: &BaseElem{ -// name: "z.Body", -// Value: Bytes, -// Convert: false, -// }, -// }, -// }, -// }, -// } - -// Base is one of the -// base types -type Primitive uint8 - -// this is effectively the -// list of currently available -// ReadXxxx / WriteXxxx methods. -const ( - Invalid Primitive = iota - Bytes - String - Float32 - Float64 - Complex64 - Complex128 - Uint - Uint8 - Uint16 - Uint32 - Uint64 - Uintptr - Byte - Int - Int8 - Int16 - Int32 - Int64 - Bool - Intf // interface{} - Time // time.Time - Ext // extension - Error // error - Duration // time.Duration - - IDENT // IDENT means an unrecognized identifier -) - -// all of the recognized identities -// that map to primitive types -var primitives = map[string]Primitive{ - "[]byte": Bytes, - "string": String, - "float32": Float32, - "float64": Float64, - "complex64": Complex64, - "complex128": Complex128, - "uint": Uint, - "uint8": Uint8, - "uint16": Uint16, - "uint32": Uint32, - "uint64": Uint64, - "uintptr": Uintptr, - "byte": Byte, - "rune": Int32, - "int": Int, - "int8": Int8, - "int16": Int16, - "int32": Int32, - "int64": Int64, - "bool": Bool, - "interface{}": Intf, - "time.Time": Time, - "msgp.Extension": Ext, - "error": Error, - "time.Duration": Duration, -} - -// types built into the library -// that satisfy all of the -// interfaces. -var builtins = map[string]struct{}{ - "msgp.Raw": struct{}{}, - "msgp.Number": struct{}{}, -} - -// Callback represents a function that can is expected to be printed into the generated code. -// for example, at the end of a successful unmarshalling. -type Callback struct { - Fname string - CallbackType CallbackType -} - -type CallbackType uint64 - -// UnmarshalCallBack represents a type callback that should run over the generated code. -const UnmarshalCallBack CallbackType = 1 - -func (c Callback) IsUnmarshallCallback() bool { return c.CallbackType == UnmarshalCallBack } -func (c Callback) GetName() string { return c.Fname } - -// common data/methods for every Elem -type common struct { - vname, alias string - allocbound string - maxtotalbytes string - callbacks []Callback -} - -func (c *common) SetVarname(s string) { c.vname = s } -func (c *common) Varname() string { return c.vname } -func (c *common) Alias(typ string) { c.alias = typ } -func (c *common) SortInterface() string { return "" } -func (c *common) SetAllocBound(s string) { c.allocbound = s } -func (c *common) AllocBound() string { return c.allocbound } -func (c *common) SetMaxTotalBytes(s string) { c.maxtotalbytes = s } -func (c *common) MaxTotalBytes() string { return c.maxtotalbytes } -func (c *common) GetCallbacks() []Callback { return c.callbacks } -func (c *common) AddCallback(cb Callback) { c.callbacks = append(c.callbacks, cb) } -func (c *common) hidden() {} - -func IsDangling(e Elem) bool { - if be, ok := e.(*BaseElem); ok && be.Dangling() { - return true - } - return false -} - -// Elem is a go type capable of being -// serialized into MessagePack. It is -// implemented by *Ptr, *Struct, *Array, -// *Slice, *Map, and *BaseElem. -type Elem interface { - // SetVarname sets this nodes - // variable name and recursively - // sets the names of all its children. - // In general, this should only be - // called on the parent of the tree. - SetVarname(s string) - - // Varname returns the variable - // name of the element. - Varname() string - - // TypeName is the canonical - // go type name of the node - // e.g. "string", "int", "map[string]float64" - // OR the alias name, if it has been set. - TypeName() string - - // Alias sets a type (alias) name - Alias(typ string) - - // Copy should perform a deep copy of the object - Copy() Elem - - // Complexity returns a measure of the - // complexity of element (greater than - // or equal to 1.) - Complexity() int - - // ZeroExpr returns the expression for the correct zero/empty - // value. Can be used for assignment. - // Returns "" if zero/empty not supported for this Elem. - ZeroExpr() string - - // IfZeroExpr returns the expression to compare to zero/empty - // for this type. It is meant to be used in an if statement - // and may include the simple statement form followed by - // semicolon and then the expression. - // Returns "" if zero/empty not supported for this Elem. - IfZeroExpr() string - - // SortInterface returns the sort.Interface for sorting a - // slice of this type. - SortInterface() string - - // Comparable returns whether the type is comparable, along the lines - // of the Go spec (https://golang.org/ref/spec#Comparison_operators), - // used to determine whether we can compare to a zero value to determine - // zeroness. - Comparable() bool - - // SetAllocBound specifies the maximum number of elements to allocate - // when decoding this type. Meaningful for slices and maps. - // Blank means unspecified bound. "-" means no bound. - SetAllocBound(bound string) - - // AllocBound returns the maximum number of elements to allocate - // when decoding this type. Meaningful for slices and maps. - AllocBound() string - - // SetMaxTotalBytes specifies the maximum number of bytes to allocate when - // decoding this type. - // Blank means unspecified bound. "-" means no bound. - SetMaxTotalBytes(bound string) - - // MaxTotalBytes specifies the maximum number of bytes to allocate when - // decoding this type. Meaningful for slices of strings or byteslices. - MaxTotalBytes() string - - // AddCallback adds to the elem a Callback it should call at the end of marshaling - AddCallback(Callback) - - // GetCallbacks fetches all callbacks this Elem stored. - GetCallbacks() []Callback - - hidden() -} - -// Ident returns the *BaseElem that corresponds -// to the provided identity. -func Ident(importPrefix string, id string) *BaseElem { - p, ok := primitives[id] - if ok { - return &BaseElem{Value: p} - } - id = importPrefix + id - be := &BaseElem{Value: IDENT, IdentName: id} - be.Alias(id) - return be -} - -type Array struct { - common - Index string // index variable name - Size string // array size - SizeHint string // const object referred to by Size - Els Elem // child -} - -func (a *Array) SetVarname(s string) { - a.common.SetVarname(s) -ridx: - a.Index = randIdent() - - // try to avoid using the same - // index as a parent slice - if strings.Contains(a.Varname(), a.Index) { - goto ridx - } - - a.Els.SetVarname(fmt.Sprintf("%s[%s]", a.Varname(), a.Index)) -} - -func (a *Array) TypeName() string { - if a.common.alias != "" { - return a.common.alias - } - a.common.Alias(fmt.Sprintf("[%s]%s", a.Size, a.Els.TypeName())) - return a.common.alias -} - -func (a *Array) Copy() Elem { - b := *a - b.Els = a.Els.Copy() - return &b -} - -func (a *Array) Complexity() int { return 1 + a.Els.Complexity() } - -func (a *Array) sz() int { - szString := a.SizeHint - if szString == "" { - szString = a.Size - } - - s, err := strconv.Atoi(szString) - if err != nil { - panic(err) - } - - return s -} - -// ZeroExpr returns the zero/empty expression or empty string if not supported. -func (a *Array) ZeroExpr() string { - zeroElem := a.Els.ZeroExpr() - if zeroElem == "" { - return "" - } - - sz := a.sz() - - res := fmt.Sprintf("%s{", a.TypeName()) - for i := 0; i < sz; i++ { - res += fmt.Sprintf("%s, ", zeroElem) - } - res += "}" - return res -} - -// IfZeroExpr returns the expression to compare to zero/empty. -func (a *Array) IfZeroExpr() string { - // Special case for arrays of comparable elements: Go generates - // faster code if we just compare to a zero value. - if a.Els.Comparable() { - return fmt.Sprintf("%s == (%s{})", a.Varname(), a.TypeName()) - } - - sz := a.sz() - - var res string - for i := 0; i < sz; i++ { - el := a.Els.Copy() - el.SetVarname(fmt.Sprintf("%s[%d]", a.Varname(), i)) - if res != "" { - res += " && " - } - res += "(" + el.IfZeroExpr() + ")" - } - return res -} - -// Comparable returns whether this elem's type is comparable. -func (a *Array) Comparable() bool { - return a.Els.Comparable() -} - -// Map is a map[string]Elem -type Map struct { - common - Keyidx string // key variable name - Key Elem // type of map key - Validx string // value variable name - Value Elem // value element -} - -func (m *Map) SetVarname(s string) { - m.common.SetVarname(s) -ridx: - m.Keyidx = randIdent() - m.Validx = randIdent() - - // just in case - if m.Keyidx == m.Validx { - goto ridx - } - - m.Key.SetVarname(m.Keyidx) - m.Value.SetVarname(m.Validx) -} - -func (m *Map) TypeName() string { - if m.common.alias != "" { - return m.common.alias - } - m.common.Alias("map[" + m.Key.TypeName() + "]" + m.Value.TypeName()) - return m.common.alias -} - -func (m *Map) Copy() Elem { - g := *m - g.Key = m.Key.Copy() - g.Value = m.Value.Copy() - return &g -} - -func (m *Map) Complexity() int { return 2 + m.Value.Complexity() } - -// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case. -func (m *Map) ZeroExpr() string { return "nil" } - -// IfZeroExpr returns the expression to compare to zero/empty. -func (m *Map) IfZeroExpr() string { return "len(" + m.Varname() + ") == 0" } - -// Comparable returns whether this elem's type is comparable. -func (m *Map) Comparable() bool { - return false -} - -type Slice struct { - common - Index string - Els Elem // The type of each element -} - -func (s *Slice) SetVarname(a string) { - s.common.SetVarname(a) - s.Index = randIdent() - varName := s.Varname() - if varName[0] == '*' { - // Pointer-to-slice requires parenthesis for slicing. - varName = "(" + varName + ")" - } - s.Els.SetVarname(fmt.Sprintf("%s[%s]", varName, s.Index)) -} - -func (s *Slice) TypeName() string { - if s.common.alias != "" { - return s.common.alias - } - s.common.Alias("[]" + s.Els.TypeName()) - return s.common.alias -} - -func (s *Slice) Copy() Elem { - z := *s - z.Els = s.Els.Copy() - return &z -} - -func (s *Slice) Complexity() int { - return 1 + s.Els.Complexity() -} - -// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case. -func (s *Slice) ZeroExpr() string { return "nil" } - -// IfZeroExpr returns the expression to compare to zero/empty. -func (s *Slice) IfZeroExpr() string { return "len(" + s.Varname() + ") == 0" } - -// Comparable returns whether this elem's type is comparable. -func (s *Slice) Comparable() bool { - return false -} - -type Ptr struct { - common - Value Elem -} - -func (s *Ptr) SetVarname(a string) { - s.common.SetVarname(a) - - // struct fields are dereferenced - // automatically... - switch x := s.Value.(type) { - case *Struct: - // struct fields are automatically dereferenced - x.SetVarname(a) - return - - case *BaseElem: - // identities have pointer receivers - if x.Value == IDENT { - x.SetVarname(a) - } else { - x.SetVarname("*" + a) - } - return - - default: - s.Value.SetVarname("*" + a) - return - } -} - -func (s *Ptr) TypeName() string { - if s.common.alias != "" { - return s.common.alias - } - s.common.Alias("*" + s.Value.TypeName()) - return s.common.alias -} - -func (s *Ptr) Copy() Elem { - v := *s - v.Value = s.Value.Copy() - return &v -} - -func (s *Ptr) Complexity() int { return 1 + s.Value.Complexity() } - -func (s *Ptr) Needsinit() bool { - if be, ok := s.Value.(*BaseElem); ok && be.needsref { - return false - } - return true -} - -// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case. -func (s *Ptr) ZeroExpr() string { return "nil" } - -// IfZeroExpr returns the expression to compare to zero/empty. -func (s *Ptr) IfZeroExpr() string { return s.Varname() + " == nil" } - -// Comparable returns whether this elem's type is comparable. -func (s *Ptr) Comparable() bool { - return false -} - -type Struct struct { - common - Fields []StructField // field list - AsTuple bool // write as an array instead of a map -} - -func (s *Struct) TypeName() string { - if s.common.alias != "" { - return s.common.alias - } - str := "struct{\n" - for i := range s.Fields { - str += s.Fields[i].FieldName + - " " + s.Fields[i].FieldElem.TypeName() + - " " + s.Fields[i].RawTag + ";\n" - } - str += "}" - s.common.Alias(str) - return s.common.alias -} - -func (s *Struct) SetVarname(a string) { - s.common.SetVarname(a) - writeStructFields(s.Fields, a) -} - -func (s *Struct) Copy() Elem { - g := *s - g.Fields = make([]StructField, len(s.Fields)) - copy(g.Fields, s.Fields) - for i := range s.Fields { - g.Fields[i].FieldElem = s.Fields[i].FieldElem.Copy() - } - return &g -} - -func (s *Struct) Complexity() int { - c := 1 - for i := range s.Fields { - c += s.Fields[i].FieldElem.Complexity() - } - return c -} - -// ZeroExpr returns the zero/empty expression or empty string if not supported. -func (s *Struct) ZeroExpr() string { - if s.alias == "" { - return "" // structs with no names not supported (for now) - } - return "(" + s.TypeName() + "{})" -} - -// IfZeroExpr returns the expression to compare to zero/empty. -func (s *Struct) IfZeroExpr() string { - if s.alias == "" { - return "" // structs with no names not supported (for now) - } - - var res string - for i := range s.Fields { - if !ast.IsExported(s.Fields[i].FieldName) { - continue - } - - fieldZero := s.Fields[i].FieldElem.IfZeroExpr() - if fieldZero != "" { - if res != "" { - res += " && " - } - res += "(" + fieldZero + ")" - } - } - return res -} - -// Comparable returns whether this elem's type is comparable. -func (s *Struct) Comparable() bool { - for _, sf := range s.Fields { - if !sf.FieldElem.Comparable() { - return false - } - } - return true -} - -// AnyHasTagPart returns true if HasTagPart(p) is true for any field. -func (s *Struct) AnyHasTagPart(pname string) bool { - for _, sf := range s.Fields { - if sf.HasTagPart(pname) { - return true - } - } - return false -} - -// UnderscoreStructHasTagPart returns true if HasTagPart(p) is true for the _struct field. -func (s *Struct) UnderscoreStructHasTagPart(pname string) bool { - for _, sf := range s.Fields { - if sf.FieldName == "_struct" && sf.HasTagPart(pname) { - return true - } - } - return false -} - -// HasAnyStructTag returns true if any of the fields in the struct have -// a codec: tag. This is used to determine which structs we can skip -// because they are not intended for encoding/decoding. -func (s *Struct) HasAnyStructTag() bool { - for _, sf := range s.Fields { - if sf.HasCodecTag { - return true - } - } - return false -} - -// HasUnderscoreStructTag returns true if there is a field named _struct -// with a codec: tag. This is used to ensure developers don't forget to -// annotate their structs with omitempty (unless explicitly opted out). -func (s *Struct) HasUnderscoreStructTag() bool { - for _, sf := range s.Fields { - if sf.FieldName == "_struct" && sf.HasCodecTag { - return true - } - } - return false -} - -type StructField struct { - FieldTag string // the string inside the `codec:""` tag up to the first comma - FieldTagParts []string // the string inside the `codec:""` tag split by commas - RawTag string // the full struct tag - HasCodecTag bool // has a `codec:` tag - FieldName string // the name of the struct field - FieldElem Elem // the field type - FieldPath []string // set of embedded struct names for accessing FieldName -} - -type byFieldTag []StructField - -func (a byFieldTag) Len() int { return len(a) } -func (a byFieldTag) Less(i, j int) bool { return a[i].FieldTag < a[j].FieldTag } -func (a byFieldTag) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -// HasTagPart returns true if the specified tag part (option) is present. -func (sf *StructField) HasTagPart(pname string) bool { - if len(sf.FieldTagParts) < 2 { - return false - } - for _, p := range sf.FieldTagParts[1:] { - if p == pname { - return true - } - } - return false -} - -type ShimMode int - -const ( - Cast ShimMode = iota - Convert -) - -// BaseElem is an element that -// can be represented by a primitive -// MessagePack type. -type BaseElem struct { - common - ShimMode ShimMode // Method used to shim - ShimToBase string // shim to base type, or empty - ShimFromBase string // shim from base type, or empty - Value Primitive // Type of element - IdentName string // name, for Value == IDENT - Convert bool // should we do an explicit conversion? - mustinline bool // must inline; not printable - needsref bool // needs reference for shim -} - -func (s *BaseElem) Dangling() bool { return s.mustinline } - -func (s *BaseElem) Alias(typ string) { - s.common.Alias(typ) - if s.Value != IDENT { - s.Convert = true - } - if strings.Contains(typ, ".") { - s.mustinline = true - } -} - -func (s *BaseElem) SetVarname(a string) { - // extensions whose parents - // are not pointers need to - // be explicitly referenced - if s.Value == Ext || s.needsref { - if strings.HasPrefix(a, "*") { - s.common.SetVarname(a[1:]) - return - } - s.common.SetVarname("&" + a) - return - } - - s.common.SetVarname(a) -} - -// TypeName returns the syntactically correct Go -// type name for the base element. -func (s *BaseElem) TypeName() string { - if s.common.alias != "" { - return s.common.alias - } - s.common.Alias(s.BaseType()) - return s.common.alias -} - -// ToBase, used if Convert==true, is used as tmp = {{ToBase}}({{Varname}}) -func (s *BaseElem) ToBase() string { - if s.ShimToBase != "" { - return s.ShimToBase - } - return s.BaseType() -} - -// FromBase, used if Convert==true, is used as {{Varname}} = {{FromBase}}(tmp) -func (s *BaseElem) FromBase() string { - if s.ShimFromBase != "" { - return s.ShimFromBase - } - return s.TypeName() -} - -// BaseName returns the string form of the -// base type (e.g. Float64, Ident, etc) -func (s *BaseElem) BaseName() string { - // time and duration are special cases; - // we strip the package prefix - if s.Value == Time { - return "Time" - } - if s.Value == Duration { - return "Duration" - } - return s.Value.String() -} - -func (s *BaseElem) BaseType() string { - switch s.Value { - case IDENT: - return s.TypeName() - - // exceptions to the naming/capitalization - // rule: - case Intf: - return "interface{}" - case Bytes: - return "[]byte" - case Time: - return "time.Time" - case Ext: - return "msgp.Extension" - - // everything else is base.String() with - // the first letter as lowercase - default: - return strings.ToLower(s.BaseName()) - } -} - -func (s *BaseElem) Needsref(b bool) { - s.needsref = b -} - -func (s *BaseElem) Copy() Elem { - g := *s - return &g -} - -func (s *BaseElem) Complexity() int { - if s.Convert && !s.mustinline { - return 2 - } - // we need to return 1 if !printable(), - // in order to make sure that stuff gets - // inlined appropriately - return 1 -} - -// Resolved returns whether or not -// the type of the element is -// a primitive or a builtin provided -// by the package. -func (s *BaseElem) Resolved() bool { - if s.Value == IDENT { - _, ok := builtins[s.TypeName()] - return ok - } - return true -} - -// ZeroExpr returns the zero/empty expression or empty string if not supported. -func (s *BaseElem) ZeroExpr() string { - - switch s.Value { - case Bytes: - return "nil" - case String: - return "\"\"" - case Complex64, Complex128: - return "complex(0,0)" - case Float32, - Float64, - Uint, - Uint8, - Uint16, - Uint32, - Uint64, - Byte, - Int, - Int8, - Int16, - Int32, - Int64, - Duration: - return "0" - case Bool: - return "false" - - case Time: - return "(time.Time{})" - } - - return "" -} - -// IfZeroExpr returns the expression to compare to zero/empty. -func (s *BaseElem) IfZeroExpr() string { - // Byte slices are special: we treat both nil and empty as - // zero for encoding purposes. - if s.Value == Bytes { - return "len(" + s.Varname() + ") == 0" - } - - z := s.ZeroExpr() - if z == "" { - // Assume this is an identifier from another package, - // and that it has generated code for MsgIsZero. - return s.Varname() + ".MsgIsZero()" - } - return s.Varname() + " == " + z -} - -// Comparable returns whether this elem's type is comparable. -func (s *BaseElem) Comparable() bool { - switch s.Value { - case String, Float32, Float64, Complex64, Complex128, - Uint, Uint8, Uint16, Uint32, Uint64, Byte, - Int, Int8, Int16, Int32, Int64, Bool, Time: - return true - default: - return false - } -} - -// SortInterface returns a sort.Interface for sorting a slice of this type. -func (s *BaseElem) SortInterface() string { - sortIntf, ok := sortInterface[s.TypeName()] - if ok { - return sortIntf - } - return "" -} - -func (k Primitive) String() string { - switch k { - case String: - return "String" - case Bytes: - return "Bytes" - case Float32: - return "Float32" - case Float64: - return "Float64" - case Complex64: - return "Complex64" - case Complex128: - return "Complex128" - case Uint: - return "Uint" - case Uint8: - return "Uint8" - case Uint16: - return "Uint16" - case Uint32: - return "Uint32" - case Uint64: - return "Uint64" - case Byte: - return "Byte" - case Int: - return "Int" - case Int8: - return "Int8" - case Int16: - return "Int16" - case Int32: - return "Int32" - case Int64: - return "Int64" - case Bool: - return "Bool" - case Intf: - return "Intf" - case Time: - return "time.Time" - case Duration: - return "time.Duration" - case Ext: - return "Extension" - case IDENT: - return "Ident" - default: - return "INVALID" - } -} - -// writeStructFields is a trampoline for writeBase for -// all of the fields in a struct -func writeStructFields(s []StructField, name string) { - for i := range s { - var path string - for _, pathelem := range s[i].FieldPath { - path += "." + pathelem - } - s[i].FieldElem.SetVarname(fmt.Sprintf("%s%s.%s", name, path, s[i].FieldName)) - } -} - -// SetSortInterface registers sort.Interface types from -// the msgp:sort directive. It would have been nice to -// register it inside the Elem, but unfortunately that -// only affects the type definition; call sites that -// refer to that type (e.g., map keys) have a different -// Elem that does not inherit (get copied) from the type -// definition in f.Identities. -var sortInterface map[string]string - -func SetSortInterface(sorttype string, sortintf string) { - if sortInterface == nil { - sortInterface = make(map[string]string) - } - - sortInterface[sorttype] = sortintf -} diff --git a/msgp/gen/iszero.go b/msgp/gen/iszero.go deleted file mode 100644 index 2f7ed2c306..0000000000 --- a/msgp/gen/iszero.go +++ /dev/null @@ -1,68 +0,0 @@ -package gen - -import ( - "io" -) - -func isZeros(w io.Writer, topics *Topics) *isZeroGen { - return &isZeroGen{ - p: printer{w: w}, - topics: topics, - } -} - -type isZeroGen struct { - passes - p printer - ctx *Context - topics *Topics -} - -func (s *isZeroGen) Method() Method { return IsZero } - -func (s *isZeroGen) Apply(dirs []string) error { - return nil -} - -func (s *isZeroGen) Execute(p Elem) ([]string, error) { - if !s.p.ok() { - return nil, s.p.err - } - p = s.applyall(p) - if p == nil { - return nil, nil - } - - // We might change p.Varname in methodReceiver(); make a copy - // to not affect other code that will use p. - p = p.Copy() - - s.ctx = &Context{} - s.ctx.PushString(p.TypeName()) - - s.p.comment("MsgIsZero returns whether this is a zero value") - - if IsDangling(p) { - baseType := p.(*BaseElem).IdentName - ptrName := p.Varname() - receiver := methodReceiver(p) - s.p.printf("\nfunc (%s %s) MsgIsZero() bool {", ptrName, receiver) - s.p.printf("\n return ((*(%s))(%s)).MsgIsZero()", baseType, ptrName) - s.p.printf("\n}") - s.topics.Add(receiver, "MsgIsZero") - return nil, s.p.err - } - - ptrName := p.Varname() - receiver := imutMethodReceiver(p) - s.p.printf("\nfunc (%s %s) MsgIsZero() bool {", ptrName, receiver) - ize := p.IfZeroExpr() - if ize == "" { - ize = "true" - } - s.p.printf("\nreturn %s", ize) - s.p.printf("\n}") - - s.topics.Add(receiver, "MsgIsZero") - return nil, s.p.err -} diff --git a/msgp/gen/marshal.go b/msgp/gen/marshal.go deleted file mode 100644 index 56340d66fa..0000000000 --- a/msgp/gen/marshal.go +++ /dev/null @@ -1,393 +0,0 @@ -package gen - -import ( - "fmt" - "go/ast" - "io" - "sort" - "strings" - - "github.com/algorand/msgp/msgp" -) - -func marshal(w io.Writer, topics *Topics) *marshalGen { - return &marshalGen{ - p: printer{w: w}, - topics: topics, - } -} - -type marshalGen struct { - passes - p printer - fuse []byte - ctx *Context - msgs []string - topics *Topics -} - -func (m *marshalGen) Method() Method { return Marshal } - -func (m *marshalGen) Apply(dirs []string) error { - return nil -} - -func (m *marshalGen) Execute(p Elem) ([]string, error) { - m.msgs = nil - if !m.p.ok() { - return m.msgs, m.p.err - } - p = m.applyall(p) - if p == nil { - return m.msgs, nil - } - - // We might change p.Varname in methodReceiver(); make a copy - // to not affect other code that will use p. - p = p.Copy() - - m.ctx = &Context{} - - m.p.comment("MarshalMsg implements msgp.Marshaler") - - if IsDangling(p) { - baseType := p.(*BaseElem).IdentName - c := p.Varname() - methodRecv := methodReceiver(p) - m.p.printf("\nfunc (%s %s) MarshalMsg(b []byte) []byte {", c, methodRecv) - m.p.printf("\n return ((*(%s))(%s)).MarshalMsg(b)", baseType, c) - m.p.printf("\n}") - - m.p.printf("\nfunc (_ %[2]s) CanMarshalMsg(%[1]s interface{}) bool {", c, methodRecv) - m.p.printf("\n _, ok := (%s).(%s)", c, methodRecv) - m.p.printf("\n return ok") - m.p.printf("\n}") - - m.topics.Add(methodRecv, "MarshalMsg") - m.topics.Add(methodRecv, "CanMarshalMsg") - - return m.msgs, m.p.err - } - - // save the vname before - // calling methodReceiver so - // that z.Msgsize() is printed correctly - c := p.Varname() - methodRecv := imutMethodReceiver(p) - - m.p.printf("\nfunc (%s %s) MarshalMsg(b []byte) (o []byte) {", c, methodRecv) - m.p.printf("\no = msgp.Require(b, %s.Msgsize())", c) - next(m, p) - m.p.nakedReturn() - - m.p.printf("\nfunc (_ %[2]s) CanMarshalMsg(%[1]s interface{}) bool {", c, methodRecv) - m.p.printf("\n _, ok := (%s).(%s)", c, methodRecv) - - // If this is a value receiver, check for a pointer type too - if methodRecv == p.TypeName() { - m.p.printf("\n if !ok {") - m.p.printf("\n _, ok = (%s).(*%s)", c, methodRecv) - m.p.printf("\n }") - } - - m.p.printf("\n return ok") - m.p.printf("\n}") - - m.topics.Add(methodRecv, "MarshalMsg") - m.topics.Add(methodRecv, "CanMarshalMsg") - - return m.msgs, m.p.err -} - -func (m *marshalGen) rawAppend(typ string, argfmt string, arg interface{}) { - m.p.printf("\no = msgp.Append%s(o, %s)", typ, fmt.Sprintf(argfmt, arg)) -} - -func (m *marshalGen) fuseHook() { - if len(m.fuse) > 0 { - m.rawbytes(m.fuse) - m.fuse = m.fuse[:0] - } -} - -func (m *marshalGen) Fuse(b []byte) { - if len(m.fuse) == 0 { - m.fuse = b - } else { - m.fuse = append(m.fuse, b...) - } -} - -func (m *marshalGen) gStruct(s *Struct) { - if !m.p.ok() { - return - } - - if s.AsTuple { - m.tuple(s) - } else { - m.mapstruct(s) - } - return -} - -func (m *marshalGen) tuple(s *Struct) { - data := make([]byte, 0, 5) - data = msgp.AppendArrayHeader(data, uint32(len(s.Fields))) - m.p.printf("\n// array header, size %d", len(s.Fields)) - m.Fuse(data) - if len(s.Fields) == 0 { - m.fuseHook() - } - for i := range s.Fields { - if !m.p.ok() { - return - } - m.ctx.PushString(s.Fields[i].FieldName) - next(m, s.Fields[i].FieldElem) - m.ctx.Pop() - } -} - -func isFieldOmitEmpty(sf StructField, s *Struct) bool { - tagName := "omitempty" - - // go-codec distinguished between omitempty and omitemptyarray - e := sf.FieldElem - _, isArray := e.(*Array) - if isArray { - tagName = "omitemptyarray" - } - - return sf.HasTagPart(tagName) || s.UnderscoreStructHasTagPart(tagName) -} - -func (m *marshalGen) mapstruct(s *Struct) { - - // Every struct must have a _struct annotation with a codec: tag. - // In the common case, the tag would contain omitempty, but it could - // also be blank, if for some reason omitempty is not desired. This - // check guards against developers forgetting to specify omitempty. - if !s.HasUnderscoreStructTag() { - m.msgs = append(m.msgs, fmt.Sprintf("Missing _struct annotation on struct %v", s)) - return - } - - sortedFields := append([]StructField(nil), s.Fields...) - sort.Sort(byFieldTag(sortedFields)) - - oeIdentPrefix := randIdent() - - var data []byte - nfields := len(sortedFields) - bm := bmask{ - bitlen: nfields, - varname: oeIdentPrefix + "Mask", - } - - exportedFields := 0 - for _, sf := range sortedFields { - if !ast.IsExported(sf.FieldName) { - continue - } - exportedFields++ - } - - omitempty := s.AnyHasTagPart("omitempty") - var fieldNVar string - needCloseBrace := false - needBmDecl := true - if omitempty { - - fieldNVar = oeIdentPrefix + "Len" - - m.p.printf("\n// omitempty: check for empty values") - m.p.printf("\n%s := uint32(%d)", fieldNVar, exportedFields) - for i, sf := range sortedFields { - if !m.p.ok() { - return - } - - if !ast.IsExported(sf.FieldName) { - continue - } - - fieldOmitEmpty := isFieldOmitEmpty(sf, s) - if ize := sf.FieldElem.IfZeroExpr(); ize != "" && fieldOmitEmpty { - if needBmDecl { - m.p.printf("\n%s", bm.typeDecl()) - needBmDecl = false - } - - m.p.printf("\nif %s {", ize) - m.p.printf("\n%s--", fieldNVar) - m.p.printf("\n%s", bm.setStmt(i)) - m.p.printf("\n}") - } - } - - m.p.printf("\n// variable map header, size %s", fieldNVar) - m.p.varAppendMapHeader("o", fieldNVar, exportedFields) - if !m.p.ok() { - return - } - - // quick check for the case where the entire thing is empty, but only at the top level - if !strings.Contains(s.Varname(), ".") { - m.p.printf("\nif %s != 0 {", fieldNVar) - needCloseBrace = true - } - - } else { - - // non-omitempty version - data = make([]byte, 0, 64) - data = msgp.AppendMapHeader(data, uint32(exportedFields)) - m.p.printf("\n// map header, size %d", exportedFields) - m.Fuse(data) - if exportedFields == 0 { - m.fuseHook() - } - - } - - for i, sf := range sortedFields { - if !ast.IsExported(sf.FieldName) { - continue - } - - if !m.p.ok() { - return - } - - fieldOmitEmpty := isFieldOmitEmpty(sf, s) - - // if field is omitempty, wrap with if statement based on the emptymask - oeField := fieldOmitEmpty && sf.FieldElem.IfZeroExpr() != "" - if oeField { - m.p.printf("\nif %s == 0 { // if not empty", bm.readExpr(i)) - } - - data = msgp.AppendString(nil, sf.FieldTag) - - m.p.printf("\n// string %q", sf.FieldTag) - m.Fuse(data) - m.fuseHook() - - m.ctx.PushString(sf.FieldName) - next(m, sf.FieldElem) - m.ctx.Pop() - - if oeField { - m.p.printf("\n}") // close if statement - } - - } - - if needCloseBrace { - m.p.printf("\n}") - } -} - -// append raw data -func (m *marshalGen) rawbytes(bts []byte) { - m.p.print("\no = append(o, ") - for _, b := range bts { - m.p.printf("0x%x,", b) - } - m.p.print(")") -} - -func (m *marshalGen) gMap(s *Map) { - if !m.p.ok() { - return - } - m.fuseHook() - vname := s.Varname() - m.p.printf("\nif %s == nil {", vname) - m.p.printf("\n o = msgp.AppendNil(o)") - m.p.printf("\n} else {") - m.rawAppend(mapHeader, lenAsUint32, vname) - m.p.printf("\n}") - - m.p.printf("\n%s_keys := make([]%s, 0, len(%s))", s.Keyidx, s.Key.TypeName(), vname) - m.p.printf("\nfor %s := range %s {", s.Keyidx, vname) - m.p.printf("\n%s_keys = append(%s_keys, %s)", s.Keyidx, s.Keyidx, s.Keyidx) - m.p.closeblock() - - m.p.printf("\nsort.Sort(%s(%s_keys))", s.Key.SortInterface(), s.Keyidx) - - m.p.printf("\nfor _, %s := range %s_keys {", s.Keyidx, s.Keyidx) - m.p.printf("\n%s := %s[%s]", s.Validx, vname, s.Keyidx) - m.p.printf("\n_ = %s", s.Validx) // we may not use the value, if it's a struct{} - m.ctx.PushVar(s.Keyidx) - next(m, s.Key) - next(m, s.Value) - m.ctx.Pop() - m.p.closeblock() -} - -func (m *marshalGen) gSlice(s *Slice) { - if !m.p.ok() { - return - } - m.fuseHook() - vname := s.Varname() - m.p.printf("\nif %s == nil {", vname) - m.p.printf("\n o = msgp.AppendNil(o)") - m.p.printf("\n} else {") - m.rawAppend(arrayHeader, lenAsUint32, vname) - m.p.printf("\n}") - m.p.rangeBlock(m.ctx, s.Index, vname, m, s.Els) -} - -func (m *marshalGen) gArray(a *Array) { - if !m.p.ok() { - return - } - m.fuseHook() - if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte { - m.rawAppend("Bytes", "(%s)[:]", a.Varname()) - return - } - - m.rawAppend(arrayHeader, literalFmt, a.Size) - m.p.rangeBlock(m.ctx, a.Index, a.Varname(), m, a.Els) -} - -func (m *marshalGen) gPtr(p *Ptr) { - if !m.p.ok() { - return - } - m.fuseHook() - m.p.printf("\nif %s == nil {\no = msgp.AppendNil(o)\n} else {", p.Varname()) - next(m, p.Value) - m.p.closeblock() -} - -func (m *marshalGen) gBase(b *BaseElem) { - if !m.p.ok() { - return - } - m.fuseHook() - vname := b.Varname() - - if b.Convert { - if b.ShimMode == Cast { - vname = tobaseConvert(b) - } else { - vname = randIdent() - m.p.printf("\nvar %s %s", vname, b.BaseType()) - m.p.printf("\n%s = %s", vname, tobaseConvert(b)) - } - } - - switch b.Value { - case IDENT: - m.p.printf("\no = %s.MarshalMsg(o)", vname) - case Intf, Ext: - m.p.printf("\no = msgp.Append%s(o, %s)", b.BaseName(), vname) - default: - m.rawAppend(b.BaseName(), literalFmt, vname) - } -} diff --git a/msgp/gen/maxsize.go b/msgp/gen/maxsize.go deleted file mode 100644 index 50522492cc..0000000000 --- a/msgp/gen/maxsize.go +++ /dev/null @@ -1,379 +0,0 @@ -package gen - -import ( - "bytes" - "fmt" - "go/ast" - "io" - "reflect" - "strconv" - "strings" - - "github.com/algorand/msgp/msgp" -) - -type maxSizeState uint8 - -const ( - // need to write "s = ..." - assignM maxSizeState = iota - - // need to write "s += ..." - addM - - // can just append "+ ..." - exprM - - multM - // the result is multiplied by whatever is preceeding it -) - -func maxSizes(w io.Writer, topics *Topics) *maxSizeGen { - return &maxSizeGen{ - p: printer{w: w}, - state: assignM, - topics: topics, - } -} - -type maxSizeGen struct { - passes - p printer - state maxSizeState - ctx *Context - topics *Topics -} - -func (s *maxSizeGen) Method() Method { return MaxSize } - -func (s *maxSizeGen) Apply(dirs []string) error { - return nil -} - -// this lets us chain together addition -// operations where possible -func (s *maxSizeGen) addConstant(sz string) { - if !s.p.ok() { - return - } - - switch s.state { - case assignM: - s.p.print("\ns = " + sz) - s.state = exprM - return - case addM: - s.p.print("\ns += " + sz) - s.state = exprM - return - case exprM: - s.p.print(" + " + sz) - return - case multM: - s.p.print(" * ( " + sz + ")") - s.state = addM - return - } - - panic("unknown size state") -} - -func (s *maxSizeGen) Execute(p Elem) ([]string, error) { - if !s.p.ok() { - return nil, s.p.err - } - p = s.applyall(p) - if p == nil { - return nil, nil - } - - // We might change p.Varname in methodReceiver(); make a copy - // to not affect other code that will use p. - p = p.Copy() - - s.p.comment("MaxSize returns a maximum valid message size for this message type") - - if IsDangling(p) { - baseType := p.(*BaseElem).IdentName - s.p.printf("\nfunc %s int{", getMaxSizeMethod(p.TypeName())) - s.p.printf("\n return %s", getMaxSizeMethod(baseType)) - s.p.printf("\n}") - s.topics.Add(baseType, getMaxSizeMethod(baseType)) - return nil, s.p.err - } - - s.ctx = &Context{} - s.ctx.PushString(p.TypeName()) - - // receiver := imutMethodReceiver(p) - s.p.printf("\nfunc %s (s int) {", getMaxSizeMethod(p.TypeName())) - s.state = assignM - next(s, p) - s.p.nakedReturn() - s.topics.Add(p.TypeName(), getMaxSizeMethod(p.TypeName())) - return nil, s.p.err -} - -func (s *maxSizeGen) gStruct(st *Struct) { - if !s.p.ok() { - return - } - - nfields := uint32(0) - for i := range st.Fields { - if ast.IsExported(st.Fields[i].FieldName) { - nfields += 1 - } - } - - if st.AsTuple { - data := msgp.AppendArrayHeader(nil, nfields) - s.addConstant(strconv.Itoa(len(data))) - for i := range st.Fields { - if !ast.IsExported(st.Fields[i].FieldName) { - continue - } - - if !s.p.ok() { - return - } - next(s, st.Fields[i].FieldElem) - } - } else { - data := msgp.AppendMapHeader(nil, nfields) - s.addConstant(strconv.Itoa(len(data))) - for i := range st.Fields { - if !ast.IsExported(st.Fields[i].FieldName) { - continue - } - - data = data[:0] - data = msgp.AppendString(data, st.Fields[i].FieldTag) - s.addConstant(strconv.Itoa(len(data))) - next(s, st.Fields[i].FieldElem) - } - } -} - -func (s *maxSizeGen) gPtr(p *Ptr) { - s.state = addM // inner must use add - next(s, p.Value) - s.state = addM // closing block; reset to add -} - -func (s *maxSizeGen) gSlice(sl *Slice) { - if !s.p.ok() { - return - } - s.state = addM - s.p.comment("Calculating size of slice: " + sl.Varname()) - if (sl.AllocBound() == "" || sl.AllocBound() == "-") && (sl.MaxTotalBytes() == "" || sl.MaxTotalBytes() == "-") { - s.p.printf("\npanic(\"Slice %s is unbounded\")", sl.Varname()) - s.state = addM // reset the add to prevent further + expressions from being added to the end the panic statement - return - } - - s.addConstant(builtinSize(arrayHeader)) - - // use maxtotalbytes if it's available - if sl.common.MaxTotalBytes() != "" && sl.common.MaxTotalBytes() != "-" { - s.addConstant(sl.common.MaxTotalBytes()) - return - } - - topLevelAllocBound := sl.AllocBound() - childElement := sl.Els - if sl.Els.AllocBound() == "" && len(strings.Split(sl.AllocBound(), ",")) > 1 { - splitIndex := strings.Index(sl.AllocBound(), ",") - childElement = sl.Els.Copy() - childElement.SetAllocBound(sl.AllocBound()[splitIndex+1:]) - topLevelAllocBound = sl.AllocBound()[:splitIndex] - } - - if str, err := maxSizeExpr(childElement); err == nil { - s.addConstant(fmt.Sprintf("((%s) * (%s))", topLevelAllocBound, str)) - } else { - s.p.printf("\npanic(\"Unable to determine max size: %s\")", err) - } - s.state = addM - return -} - -func (s *maxSizeGen) gArray(a *Array) { - if !s.p.ok() { - return - } - // If this is not the first line where we define s = ... then we need to reset the state - // to addM so that the comment is printed correctly on a newline - if s.state != assignM { - s.state = addM - } - s.p.comment("Calculating size of array: " + a.Varname()) - - s.addConstant(builtinSize(arrayHeader)) - - if str, err := maxSizeExpr(a.Els); err == nil { - s.addConstant(fmt.Sprintf("((%s) * (%s))", a.Size, str)) - } else { - s.p.printf("\npanic(\"Unable to determine max size: %s\")", err) - - } - s.state = addM - return -} - -func (s *maxSizeGen) gMap(m *Map) { - vn := m.Varname() - s.state = addM - s.addConstant(builtinSize(mapHeader)) - topLevelAllocBound := m.AllocBound() - if topLevelAllocBound != "" && topLevelAllocBound == "-" { - s.p.printf("\npanic(\"Map %s is unbounded\")", m.Varname()) - s.state = addM // reset the add to prevent further + expressions from being added to the end the panic statement - return - } - splitBounds := strings.Split(m.AllocBound(), ",") - if len(splitBounds) > 1 { - topLevelAllocBound = splitBounds[0] - m.Key.SetAllocBound(splitBounds[1]) - if len(splitBounds) > 2 { - m.Value.SetAllocBound(splitBounds[2]) - } - } - - s.p.comment("Adding size of map keys for " + vn) - s.p.printf("\ns += %s", topLevelAllocBound) - s.state = multM - next(s, m.Key) - - s.p.comment("Adding size of map values for " + vn) - s.p.printf("\ns += %s", topLevelAllocBound) - s.state = multM - next(s, m.Value) - - s.state = addM -} - -func (s *maxSizeGen) gBase(b *BaseElem) { - if !s.p.ok() { - return - } - if b.MaxTotalBytes() != "" { - s.p.comment("Using maxtotalbytes for: " + b.Varname()) - s.state = addM - s.addConstant(b.MaxTotalBytes()) - s.state = addM - return - } - if b.Convert && b.ShimMode == Convert { - s.state = addM - vname := randIdent() - s.p.printf("\nvar %s %s", vname, b.BaseType()) - - // ensure we don't get "unused variable" warnings from outer slice iterations - s.p.printf("\n_ = %s", b.Varname()) - - value, err := baseMaxSizeExpr(b.Value, vname, b.BaseName(), b.TypeName(), b.common.AllocBound()) - if err != nil { - s.p.printf("\npanic(\"Unable to determine max size: %s\")", err) - s.state = addM // reset the add to prevent further + expressions from being added to the end the panic statement - return - } - s.p.printf("\ns += %s", value) - s.state = exprM - - } else { - vname := b.Varname() - if b.Convert { - vname = tobaseConvert(b) - } - value, err := baseMaxSizeExpr(b.Value, vname, b.BaseName(), b.TypeName(), b.common.AllocBound()) - if err != nil { - s.p.printf("\npanic(\"Unable to determine max size: %s\")", err) - s.state = addM // reset the add to prevent further + expressions from being added to the end the panic statement - return - } - s.addConstant(value) - } -} - -func baseMaxSizeExpr(value Primitive, vname, basename, typename string, allocbound string) (string, error) { - if typename == "msgp.Raw" { - return "", fmt.Errorf("MaxSize() not implemented for Raw type") - } - switch value { - case Ext: - return "", fmt.Errorf("MaxSize() not implemented for Ext type") - case Intf: - return "", fmt.Errorf("MaxSize() not implemented for Interfaces") - case IDENT: - return getMaxSizeMethod(typename), nil - case Bytes: - if allocbound == "" || allocbound == "-" { - return "", fmt.Errorf("Byteslice type %s is unbounded", vname) - } - return "msgp.BytesPrefixSize + " + allocbound, nil - case String: - if allocbound == "" || allocbound == "-" { - return "", fmt.Errorf("String type %s is unbounded", vname) - } - return "msgp.StringPrefixSize + " + allocbound, nil - default: - return builtinSize(basename), nil - } -} - -// return a fixed-size expression, if possible. -// only possible for *BaseElem, *Array and Struct. -// returns (expr, err) -func maxSizeExpr(e Elem) (string, error) { - switch e := e.(type) { - case *Array: - if str, err := maxSizeExpr(e.Els); err == nil { - return fmt.Sprintf("(%s * (%s))", e.Size, str), nil - } else { - return "", err - } - case *BaseElem: - if fixedSize(e.Value) { - return builtinSize(e.BaseName()), nil - } else if (e.TypeName()) == "msgp.Raw" { - return "", fmt.Errorf("Raw type is unbounded") - } else if (e.Value) == String { - if e.AllocBound() == "" || e.AllocBound() == "-" { - return "", fmt.Errorf("String type is unbounded for %s", e.Varname()) - } - return fmt.Sprintf("(msgp.StringPrefixSize + %s)", e.AllocBound()), nil - } else if (e.Value) == IDENT { - return fmt.Sprintf("(%s)", getMaxSizeMethod(e.TypeName())), nil - } else if (e.Value) == Bytes { - if e.AllocBound() == "" || e.AllocBound() == "-" { - return "", fmt.Errorf("Inner byteslice type is unbounded") - } - return fmt.Sprintf("(msgp.BytesPrefixSize + %s)", e.AllocBound()), nil - } - case *Struct: - return fmt.Sprintf("(%s)", getMaxSizeMethod(e.TypeName())), nil - case *Slice: - if e.AllocBound() == "" || e.AllocBound() == "-" { - return "", fmt.Errorf("Slice %s is unbounded", e.Varname()) - } - if str, err := maxSizeExpr(e.Els); err == nil { - return fmt.Sprintf("(%s * (%s))", e.AllocBound(), str), nil - } else { - return "", err - } - } - return fmt.Sprintf("%s, %s", e.TypeName(), reflect.TypeOf(e)), nil -} - -func getMaxSizeMethod(typeName string) (s string) { - var pos int - dotIndex := strings.Index(typeName, ".") - if dotIndex != -1 { - pos = dotIndex + 1 - } - b := []byte(typeName) - b[pos] = bytes.ToUpper(b)[pos] - return string(b) + "MaxSize()" -} diff --git a/msgp/gen/size.go b/msgp/gen/size.go deleted file mode 100644 index d3cbb3d63d..0000000000 --- a/msgp/gen/size.go +++ /dev/null @@ -1,325 +0,0 @@ -package gen - -import ( - "fmt" - "go/ast" - "io" - "strconv" - - "github.com/algorand/msgp/msgp" -) - -type sizeState uint8 - -const ( - // need to write "s = ..." - assign sizeState = iota - - // need to write "s += ..." - add - - // can just append "+ ..." - expr -) - -func sizes(w io.Writer, topics *Topics) *sizeGen { - return &sizeGen{ - p: printer{w: w}, - state: assign, - topics: topics, - } -} - -type sizeGen struct { - passes - p printer - state sizeState - ctx *Context - topics *Topics -} - -func (s *sizeGen) Method() Method { return Size } - -func (s *sizeGen) Apply(dirs []string) error { - return nil -} - -func builtinSize(typ string) string { - return "msgp." + typ + "Size" -} - -// this lets us chain together addition -// operations where possible -func (s *sizeGen) addConstant(sz string) { - if !s.p.ok() { - return - } - - switch s.state { - case assign: - s.p.print("\ns = " + sz) - s.state = expr - return - case add: - s.p.print("\ns += " + sz) - s.state = expr - return - case expr: - s.p.print(" + " + sz) - return - } - - panic("unknown size state") -} - -func (s *sizeGen) Execute(p Elem) ([]string, error) { - if !s.p.ok() { - return nil, s.p.err - } - p = s.applyall(p) - if p == nil { - return nil, nil - } - - // We might change p.Varname in methodReceiver(); make a copy - // to not affect other code that will use p. - p = p.Copy() - - s.p.comment("Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message") - - if IsDangling(p) { - baseType := p.(*BaseElem).IdentName - ptrName := p.Varname() - receiver := methodReceiver(p) - s.p.printf("\nfunc (%s %s) Msgsize() int {", ptrName, receiver) - s.p.printf("\n return ((*(%s))(%s)).Msgsize()", baseType, ptrName) - s.p.printf("\n}") - s.topics.Add(receiver, "Msgsize") - return nil, s.p.err - } - - s.ctx = &Context{} - s.ctx.PushString(p.TypeName()) - - ptrName := p.Varname() - receiver := imutMethodReceiver(p) - s.p.printf("\nfunc (%s %s) Msgsize() (s int) {", ptrName, receiver) - s.state = assign - next(s, p) - s.p.nakedReturn() - s.topics.Add(receiver, "Msgsize") - return nil, s.p.err -} - -func (s *sizeGen) gStruct(st *Struct) { - if !s.p.ok() { - return - } - - nfields := uint32(0) - for i := range st.Fields { - if ast.IsExported(st.Fields[i].FieldName) { - nfields += 1 - } - } - - if st.AsTuple { - data := msgp.AppendArrayHeader(nil, nfields) - s.addConstant(strconv.Itoa(len(data))) - for i := range st.Fields { - if !ast.IsExported(st.Fields[i].FieldName) { - continue - } - - if !s.p.ok() { - return - } - next(s, st.Fields[i].FieldElem) - } - } else { - data := msgp.AppendMapHeader(nil, nfields) - s.addConstant(strconv.Itoa(len(data))) - for i := range st.Fields { - if !ast.IsExported(st.Fields[i].FieldName) { - continue - } - - data = data[:0] - data = msgp.AppendString(data, st.Fields[i].FieldTag) - s.addConstant(strconv.Itoa(len(data))) - next(s, st.Fields[i].FieldElem) - } - } -} - -func (s *sizeGen) gPtr(p *Ptr) { - s.state = add // inner must use add - s.p.printf("\nif %s == nil {\ns += msgp.NilSize\n} else {", p.Varname()) - next(s, p.Value) - s.state = add // closing block; reset to add - s.p.closeblock() -} - -func (s *sizeGen) gSlice(sl *Slice) { - if !s.p.ok() { - return - } - - s.addConstant(builtinSize(arrayHeader)) - - // if the slice's element is a fixed size - // (e.g. float64, [32]int, etc.), then - // print the length times the element size directly - if str, ok := fixedsizeExpr(sl.Els); ok { - s.addConstant(fmt.Sprintf("(%s * (%s))", lenExpr(sl), str)) - return - } - - // add inside the range block, and immediately after - s.state = add - s.p.rangeBlock(s.ctx, sl.Index, sl.Varname(), s, sl.Els) - s.state = add -} - -func (s *sizeGen) gArray(a *Array) { - if !s.p.ok() { - return - } - - s.addConstant(builtinSize(arrayHeader)) - - // if the array's children are a fixed - // size, we can compile an expression - // that always represents the array's wire size - if str, ok := fixedsizeExpr(a); ok { - s.addConstant(str) - return - } - - s.state = add - s.p.rangeBlock(s.ctx, a.Index, a.Varname(), s, a.Els) - s.state = add -} - -func (s *sizeGen) gMap(m *Map) { - s.addConstant(builtinSize(mapHeader)) - vn := m.Varname() - s.p.printf("\nif %s != nil {", vn) - s.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vn) - s.p.printf("\n_ = %s", m.Keyidx) // we may not use the key - s.p.printf("\n_ = %s", m.Validx) // we may not use the value - s.p.printf("\ns += 0") - s.state = expr - s.ctx.PushVar(m.Keyidx) - next(s, m.Key) - next(s, m.Value) - s.ctx.Pop() - s.p.closeblock() - s.p.closeblock() - s.state = add -} - -func (s *sizeGen) gBase(b *BaseElem) { - if !s.p.ok() { - return - } - if b.Convert && b.ShimMode == Convert { - s.state = add - vname := randIdent() - s.p.printf("\nvar %s %s", vname, b.BaseType()) - - // ensure we don't get "unused variable" warnings from outer slice iterations - s.p.printf("\n_ = %s", b.Varname()) - - s.p.printf("\ns += %s", basesizeExpr(b.Value, vname, b.BaseName())) - s.state = expr - - } else { - vname := b.Varname() - if b.Convert { - vname = tobaseConvert(b) - } - s.addConstant(basesizeExpr(b.Value, vname, b.BaseName())) - } -} - -// returns "len(slice)" -func lenExpr(sl *Slice) string { - return "len(" + sl.Varname() + ")" -} - -// is a given primitive always the same (max) -// size on the wire? -func fixedSize(p Primitive) bool { - switch p { - case Intf, Ext, IDENT, Bytes, String: - return false - default: - return true - } -} - -// strip reference from string -func stripRef(s string) string { - if s[0] == '&' { - return s[1:] - } - return s -} - -// return a fixed-size expression, if possible. -// only possible for *BaseElem and *Array. -// returns (expr, ok) -func fixedsizeExpr(e Elem) (string, bool) { - switch e := e.(type) { - case *Array: - if str, ok := fixedsizeExpr(e.Els); ok { - return fmt.Sprintf("(%s * (%s))", e.Size, str), true - } - case *BaseElem: - if fixedSize(e.Value) { - return builtinSize(e.BaseName()), true - } - case *Struct: - var str string - for _, f := range e.Fields { - if fs, ok := fixedsizeExpr(f.FieldElem); ok { - if str == "" { - str = fs - } else { - str += "+" + fs - } - } else { - return "", false - } - } - var hdrlen int - mhdr := msgp.AppendMapHeader(nil, uint32(len(e.Fields))) - hdrlen += len(mhdr) - var strbody []byte - for _, f := range e.Fields { - strbody = msgp.AppendString(strbody[:0], f.FieldTag) - hdrlen += len(strbody) - } - return fmt.Sprintf("%d + %s", hdrlen, str), true - } - return "", false -} - -// print size expression of a variable name -func basesizeExpr(value Primitive, vname, basename string) string { - switch value { - case Ext: - return "msgp.ExtensionPrefixSize + " + stripRef(vname) + ".Len()" - case Intf: - return "msgp.GuessSize(" + vname + ")" - case IDENT: - return vname + ".Msgsize()" - case Bytes: - return "msgp.BytesPrefixSize + len(" + vname + ")" - case String: - return "msgp.StringPrefixSize + len(" + vname + ")" - default: - return builtinSize(basename) - } -} diff --git a/msgp/gen/spec.go b/msgp/gen/spec.go deleted file mode 100644 index 5567b72c5c..0000000000 --- a/msgp/gen/spec.go +++ /dev/null @@ -1,554 +0,0 @@ -package gen - -import ( - "bytes" - "fmt" - "io" - "strings" -) - -const ( - lenAsUint32 = "uint32(len(%s))" - literalFmt = "%s" - intFmt = "%d" - quotedFmt = `"%s"` - mapHeader = "MapHeader" - arrayHeader = "ArrayHeader" - mapKey = "MapKeyPtr" - stringTyp = "String" - u32 = "uint32" -) - -// Method is a bitfield representing something that the -// generator knows how to print. -type Method uint8 - -// are the bits in 'f' set in 'm'? -func (m Method) isset(f Method) bool { return (m&f == f) } - -// String implements fmt.Stringer -func (m Method) String() string { - switch m { - case 0, invalidmeth: - return "" - case Marshal: - return "marshal" - case Unmarshal: - return "unmarshal" - case Size: - return "size" - case IsZero: - return "iszero" - case MaxSize: - return "maxsize" - case Test: - return "test" - default: - // return e.g. "marshal+unmarshal+test" - modes := [...]Method{Marshal, Unmarshal, Size, IsZero, MaxSize, Test} - any := false - nm := "" - for _, mm := range modes { - if m.isset(mm) { - if any { - nm += "+" + mm.String() - } else { - nm += mm.String() - any = true - } - } - } - return nm - - } -} - -func strtoMeth(s string) Method { - switch s { - case "marshal": - return Marshal - case "unmarshal": - return Unmarshal - case "size": - return Size - case "iszero": - return IsZero - case "maxsize": - return MaxSize - case "test": - return Test - default: - return 0 - } -} - -const ( - Marshal Method = 1 << iota // msgp.Marshaler - Unmarshal // msgp.Unmarshaler - Size // msgp.Sizer - IsZero // implement MsgIsZero() - Test // generate tests - MaxSize // msgp.MaxSize - invalidmeth // this isn't a method - marshaltest = Marshal | Unmarshal | Test // tests for Marshaler and Unmarshaler -) - -type Printer struct { - gens []generator -} - -func NewPrinter(m Method, topics *Topics, out io.Writer, tests io.Writer) *Printer { - if m.isset(Test) && tests == nil { - panic("cannot print tests with 'nil' tests argument!") - } - gens := make([]generator, 0, 7) - if m.isset(Marshal) { - gens = append(gens, marshal(out, topics)) - } - if m.isset(Unmarshal) { - gens = append(gens, unmarshal(out, topics)) - } - if m.isset(Size) { - gens = append(gens, sizes(out, topics)) - } - if m.isset(IsZero) { - gens = append(gens, isZeros(out, topics)) - } - if m.isset(MaxSize) { - gens = append(gens, maxSizes(out, topics)) - } - if m.isset(marshaltest) { - gens = append(gens, mtest(tests)) - } - if len(gens) == 0 { - panic("NewPrinter called with invalid method flags") - } - return &Printer{gens: gens} -} - -// TransformPass is a pass that transforms individual -// elements. (Note that if the returned is different from -// the argument, it should not point to the same objects.) -type TransformPass func(Elem) Elem - -// IgnoreTypename is a pass that just ignores -// types of a given name. -func IgnoreTypename(name string) TransformPass { - return func(e Elem) Elem { - if e.TypeName() == name { - return nil - } - return e - } -} - -// ApplyDirective applies a directive to a named pass -// and all of its dependents. -func (p *Printer) ApplyDirective(pass Method, t TransformPass) { - for _, g := range p.gens { - if g.Method().isset(pass) { - g.Add(t) - } - } -} - -// Print prints an Elem. -func (p *Printer) Print(e Elem) ([]string, error) { - // If the elem is a struct and has no _struct annotations, skip it. - es, ok := e.(*Struct) - if ok && !es.HasAnyStructTag() { - return nil, nil - } - - var msgs []string - - for _, g := range p.gens { - // Elem.SetVarname() is called before the Print() step in parse.FileSet.PrintTo(). - // Elem.SetVarname() generates identifiers as it walks the Elem. This can cause - // collisions between idents created during SetVarname and idents created during Print, - // hence the separate prefixes. - resetIdent("zb") - m, err := g.Execute(e) - resetIdent("za") - - if err != nil { - return nil, err - } - - msgs = append(msgs, m...) - } - return msgs, nil -} - -type contextItem interface { - Arg() string -} - -type contextString string - -func (c contextString) Arg() string { - return fmt.Sprintf("%q", c) -} - -type contextVar string - -func (c contextVar) Arg() string { - return string(c) -} - -type Context struct { - path []contextItem -} - -func (c *Context) PushString(s string) { - c.path = append(c.path, contextString(s)) -} - -func (c *Context) PushVar(s string) { - c.path = append(c.path, contextVar(s)) -} - -func (c *Context) Pop() { - c.path = c.path[:len(c.path)-1] -} - -func (c *Context) ArgsStr() string { - var out string - for idx, p := range c.path { - if idx > 0 { - out += ", " - } - out += p.Arg() - } - return out -} - -// generator is the interface through -// which code is generated. -type generator interface { - Method() Method - Add(p TransformPass) - Execute(Elem) ([]string, error) // execute writes the method for the provided object. -} - -type passes []TransformPass - -func (p *passes) Add(t TransformPass) { - *p = append(*p, t) -} - -func (p *passes) applyall(e Elem) Elem { - for _, t := range *p { - e = t(e) - if e == nil { - return nil - } - } - return e -} - -type traversal interface { - gMap(*Map) - gSlice(*Slice) - gArray(*Array) - gPtr(*Ptr) - gBase(*BaseElem) - gStruct(*Struct) -} - -// type-switch dispatch to the correct -// method given the type of 'e' -func next(t traversal, e Elem) { - switch e := e.(type) { - case *Map: - t.gMap(e) - case *Struct: - t.gStruct(e) - case *Slice: - t.gSlice(e) - case *Array: - t.gArray(e) - case *Ptr: - t.gPtr(e) - case *BaseElem: - t.gBase(e) - default: - panic("bad element type") - } -} - -// possibly-immutable method receiver -func imutMethodReceiver(p Elem) string { - switch e := p.(type) { - case *Struct: - // TODO(HACK): actually do real math here. - if len(e.Fields) <= 3 { - for i := range e.Fields { - if be, ok := e.Fields[i].FieldElem.(*BaseElem); !ok || (be.Value == IDENT || be.Value == Bytes) { - goto nope - } - } - return p.TypeName() - } - nope: - p.SetVarname("(*" + p.Varname() + ")") - return "*" + p.TypeName() - - // gets dereferenced automatically - case *Array: - p.SetVarname("(*" + p.Varname() + ")") - return "*" + p.TypeName() - - // everything else can be - // by-value. - default: - return p.TypeName() - } -} - -// if necessary, wraps a type -// so that its method receiver -// is of the write type. -func methodReceiver(p Elem) string { - p.SetVarname("(*" + p.Varname() + ")") - return "*" + p.TypeName() -} - -// shared utility for generators -type printer struct { - w io.Writer - err error -} - -// writes "var {{name}} {{typ}};" -func (p *printer) declare(name string, typ string) { - p.printf("\nvar %s %s", name, typ) -} - -// does: -// -// if m == nil { -// m = make(type, size) -// } else if len(m) > 0 { -// for key := range m { delete(m, key) } -// } -// -func (p *printer) resizeMap(size string, isnil string, m *Map, ctx string) []string { - vn := m.Varname() - if !p.ok() { - return nil - } - - allocbound := m.AllocBound() - if allocbound == "" { - return []string{fmt.Sprintf("Missing allocbound on map %v", m)} - } - allocbound = strings.Split(allocbound, ",")[0] - if allocbound != "-" { - p.printf("\nif %s > %s {", size, allocbound) - p.printf("\nerr = msgp.ErrOverflow(uint64(%s), uint64(%s))", size, allocbound) - p.printf("\nerr = msgp.WrapError(err, %s)", ctx) - p.printf("\nreturn") - p.printf("\n}") - } - - // go-codec compat: nil clears map, but if a map already exists - // (e.g., because we are decoding the same key twice), then keep - // the map as-is. - - p.printf("\nif %s {", isnil) - p.printf("\n %s = nil", vn) - p.printf("\n} else if %s == nil {", vn) - p.printf("\n %s = make(%s, %s)", vn, m.TypeName(), size) - p.closeblock() - - return nil -} - -// assign key to value based on varnames -func (p *printer) mapAssign(m *Map) { - if !p.ok() { - return - } - p.printf("\n%s[%s] = %s", m.Varname(), m.Keyidx, m.Validx) -} - -// clear map keys -func (p *printer) clearMap(name string) { - p.printf("\nfor key := range %[1]s { delete(%[1]s, key) }", name) -} - -func (p *printer) wrapErrCheck(ctx string) { - p.print("\nif err != nil {") - p.printf("\nerr = msgp.WrapError(err, %s)", ctx) - p.printf("\nreturn") - p.print("\n}") -} - -func (p *printer) resizeSlice(size string, isnil string, s *Slice, ctx string) []string { - allocbound := s.AllocBound() - if allocbound == "" { - return []string{fmt.Sprintf("Missing allocbound on slice %v", s)} - } - allocbound = strings.Split(allocbound, ",")[0] - if allocbound != "-" { - p.printf("\nif %s > %s {", size, allocbound) - p.printf("\nerr = msgp.ErrOverflow(uint64(%s), uint64(%s))", size, allocbound) - p.printf("\nerr = msgp.WrapError(err, %s)", ctx) - p.printf("\nreturn") - p.printf("\n}") - } - - p.printf("\nif %s {", isnil) - p.printf("\n %s = nil", s.Varname()) - p.printf("\n} else if %[1]s != nil && cap(%[1]s) >= %[2]s {", s.Varname(), size) - p.printf("\n %[1]s = (%[1]s)[:%[2]s]", s.Varname(), size) - p.printf("\n} else {") - p.printf("\n %[1]s = make(%[3]s, %[2]s)", s.Varname(), size, s.TypeName()) - p.printf("\n}") - - return nil -} - -func (p *printer) arrayCheck(want string, got string) { - p.printf("\nif %[1]s != %[2]s { err = msgp.ArrayError{Wanted: %[2]s, Got: %[1]s}; return }", got, want) -} - -func (p *printer) arrayCheckBound(want string, got string) { - p.printf("\nif %[1]s > %[2]s { err = msgp.ArrayError{Wanted: %[2]s, Got: %[1]s}; return }", got, want) -} - -func (p *printer) closeblock() { p.print("\n}") } - -// does: -// -// for idx := range iter { -// {{generate inner}} -// } -// -func (p *printer) rangeBlock(ctx *Context, idx string, iter string, t traversal, inner Elem) { - ctx.PushVar(idx) - p.printf("\n for %s := range %s {", idx, iter) - next(t, inner) - p.closeblock() - ctx.Pop() -} - -func (p *printer) nakedReturn() { - if p.ok() { - p.print("\nreturn\n}\n") - } -} - -func (p *printer) comment(s string) { - p.print("\n// " + s) -} - -func (p *printer) printf(format string, args ...interface{}) { - if p.err == nil { - _, p.err = fmt.Fprintf(p.w, format, args...) - } -} - -func (p *printer) print(format string) { - if p.err == nil { - _, p.err = io.WriteString(p.w, format) - } -} - -func (p *printer) initPtr(pt *Ptr) { - if pt.Needsinit() { - vname := pt.Varname() - p.printf("\nif %s == nil { %s = new(%s); }", vname, vname, pt.Value.TypeName()) - } -} - -func (p *printer) ok() bool { return p.err == nil } - -func tobaseConvert(b *BaseElem) string { - return b.ToBase() + "(" + b.Varname() + ")" -} - -func (p *printer) varWriteMapHeader(receiver string, sizeVarname string, maxSize int) { - if maxSize <= 15 { - p.printf("\nerr = %s.Append(0x80 | uint8(%s))", receiver, sizeVarname) - } else { - p.printf("\nerr = %s.WriteMapHeader(%s)", receiver, sizeVarname) - } -} - -func (p *printer) varAppendMapHeader(sliceVarname string, sizeVarname string, maxSize int) { - if maxSize <= 15 { - p.printf("\n%s = append(%s, 0x80 | uint8(%s))", sliceVarname, sliceVarname, sizeVarname) - } else { - p.printf("\n%s = msgp.AppendMapHeader(%s, %s)", sliceVarname, sliceVarname, sizeVarname) - } -} - -// bmask is a bitmask of a the specified number of bits -type bmask struct { - bitlen int - varname string -} - -// typeDecl returns the variable declaration as a var statement -func (b *bmask) typeDecl() string { - return fmt.Sprintf("var %s %s /* %d bits */", b.varname, b.typeName(), b.bitlen) -} - -// typeName returns the type, e.g. "uint8" or "[2]uint64" -func (b *bmask) typeName() string { - - if b.bitlen <= 8 { - return "uint8" - } - if b.bitlen <= 16 { - return "uint16" - } - if b.bitlen <= 32 { - return "uint32" - } - if b.bitlen <= 64 { - return "uint64" - } - - return fmt.Sprintf("[%d]uint64", (b.bitlen+64-1)/64) -} - -// readExpr returns the expression to read from a position in the bitmask. -// Compare ==0 for false or !=0 for true. -func (b *bmask) readExpr(bitoffset int) string { - - if bitoffset < 0 || bitoffset >= b.bitlen { - panic(fmt.Errorf("bitoffset %d out of range for bitlen %d", bitoffset, b.bitlen)) - } - - var buf bytes.Buffer - buf.Grow(len(b.varname) + 16) - buf.WriteByte('(') - buf.WriteString(b.varname) - if b.bitlen > 64 { - fmt.Fprintf(&buf, "[%d]", (bitoffset / 64)) - } - buf.WriteByte('&') - fmt.Fprintf(&buf, "0x%X", (uint64(1) << (uint64(bitoffset) % 64))) - buf.WriteByte(')') - - return buf.String() - -} - -// setStmt returns the statement to set the specified bit in the bitmask. -func (b *bmask) setStmt(bitoffset int) string { - - var buf bytes.Buffer - buf.Grow(len(b.varname) + 16) - buf.WriteString(b.varname) - if b.bitlen > 64 { - fmt.Fprintf(&buf, "[%d]", (bitoffset / 64)) - } - fmt.Fprintf(&buf, " |= 0x%X", (uint64(1) << (uint64(bitoffset) % 64))) - - return buf.String() - -} diff --git a/msgp/gen/testgen.go b/msgp/gen/testgen.go deleted file mode 100644 index f046702ff6..0000000000 --- a/msgp/gen/testgen.go +++ /dev/null @@ -1,104 +0,0 @@ -package gen - -import ( - "io" - "text/template" -) - -var ( - marshalTestTempl = template.New("MarshalTest") -) - -// TODO(philhofer): -// for simplicity's sake, right now -// we can only generate tests for types -// that can be initialized with the -// "Type{}" syntax. -// we should support all the types. - -func mtest(w io.Writer) *mtestGen { - return &mtestGen{w: w} -} - -type mtestGen struct { - passes - w io.Writer -} - -func (m *mtestGen) Execute(p Elem) ([]string, error) { - p = m.applyall(p) - if p != nil && !IsDangling(p) { - switch p.(type) { - case *Struct, *Array, *Slice, *Map: - return nil, marshalTestTempl.Execute(m.w, p) - } - } - return nil, nil -} - -func (m *mtestGen) Method() Method { return marshaltest } - -func init() { - template.Must(marshalTestTempl.Parse(`func TestMarshalUnmarshal{{.TypeName}}(t *testing.T) { - partitiontest.PartitionTest(t) - v := {{.TypeName}}{} - bts := v.MarshalMsg(nil) - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func TestRandomizedEncoding{{.TypeName}}(t *testing.T) { - protocol.RunEncodingTest(t, &{{.TypeName}}{}) -} - -func BenchmarkMarshalMsg{{.TypeName}}(b *testing.B) { - v := {{.TypeName}}{} - b.ReportAllocs() - b.ResetTimer() - for i:=0; i %s\n", value)) - } - outbuf.WriteString("//\n") - } - outbuf.WriteString("\n") - return outbuf.Bytes() -} - -func (t *Topics) Add(key, value string) { - if t.structs == nil { - t.structs = make(map[string][]string) - } - if key[0] == '*' { - key = key[1:] - value = "(*) " + value - } - t.structs[key] = append(t.structs[key], value) -} diff --git a/msgp/gen/unmarshal.go b/msgp/gen/unmarshal.go deleted file mode 100644 index 509d9bece9..0000000000 --- a/msgp/gen/unmarshal.go +++ /dev/null @@ -1,358 +0,0 @@ -package gen - -import ( - "go/ast" - "io" - "strconv" - "strings" -) - -func unmarshal(w io.Writer, topics *Topics) *unmarshalGen { - return &unmarshalGen{ - p: printer{w: w}, - topics: topics, - } -} - -type unmarshalGen struct { - passes - p printer - hasfield bool - ctx *Context - msgs []string - topics *Topics -} - -func (u *unmarshalGen) Method() Method { return Unmarshal } - -func (u *unmarshalGen) needsField() { - if u.hasfield { - return - } - u.p.print("\nvar field []byte; _ = field") - u.hasfield = true -} - -func (u *unmarshalGen) Execute(p Elem) ([]string, error) { - u.msgs = nil - u.hasfield = false - if !u.p.ok() { - return u.msgs, u.p.err - } - p = u.applyall(p) - if p == nil { - return u.msgs, nil - } - - // We might change p.Varname in methodReceiver(); make a copy - // to not affect other code that will use p. - p = p.Copy() - - u.ctx = &Context{} - - u.p.comment("UnmarshalMsg implements msgp.Unmarshaler") - - if IsDangling(p) { - baseType := p.(*BaseElem).IdentName - c := p.Varname() - methodRecv := methodReceiver(p) - u.p.printf("\nfunc (%s %s) UnmarshalMsg(bts []byte) ([]byte, error) {", c, methodRecv) - u.p.printf("\n return ((*(%s))(%s)).UnmarshalMsg(bts)", baseType, c) - u.p.printf("\n}") - - u.p.printf("\nfunc (%s %s) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) ([]byte, error) {", c, methodRecv) - u.p.printf("\n return ((*(%s))(%s)).UnmarshalMsgWithState(bts, st)", baseType, c) - u.p.printf("\n}") - - u.p.printf("\nfunc (_ %[2]s) CanUnmarshalMsg(%[1]s interface{}) bool {", c, methodRecv) - u.p.printf("\n _, ok := (%s).(%s)", c, methodRecv) - u.p.printf("\n return ok") - u.p.printf("\n}") - - u.topics.Add(methodRecv, "UnmarshalMsg") - u.topics.Add(methodRecv, "UnmarshalMsgWithState") - u.topics.Add(methodRecv, "CanUnmarshalMsg") - - return u.msgs, u.p.err - } - - // save the vname before calling methodReceiver - c := p.Varname() - methodRecv := methodReceiver(p) - - u.p.printf("\nfunc (%s %s) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) {", c, methodRecv) - u.p.printf("\n if st.Depth == 0 {") - u.p.printf("\n err = msgp.ErrMaxDepthExceeded{}") - u.p.printf("\n return") - u.p.printf("\n }") - u.p.printf("\n st.Depth--") - next(u, p) - u.p.print("\no = bts") - - // right before the return: attempt to inspect well formed: - for _, callback := range p.GetCallbacks() { - if !callback.IsUnmarshallCallback() { - continue - } - - u.p.printf("\nif err = %s.%s(); err != nil {", c, callback.GetName()) - u.p.printf("\n return") - u.p.printf("\n}") - } - u.p.nakedReturn() - - u.p.printf("\nfunc (%s %s) UnmarshalMsg(bts []byte) (o []byte, err error) {", c, methodRecv) - u.p.printf("\n return %s.UnmarshalMsgWithState(bts, msgp.DefaultUnmarshalState)", c) - u.p.printf("\n}") - - u.p.printf("\nfunc (_ %[2]s) CanUnmarshalMsg(%[1]s interface{}) bool {", c, methodRecv) - u.p.printf("\n _, ok := (%s).(%s)", c, methodRecv) - u.p.printf("\n return ok") - u.p.printf("\n}") - - u.topics.Add(methodRecv, "UnmarshalMsg") - u.topics.Add(methodRecv, "UnmarshalMsgWithState") - u.topics.Add(methodRecv, "CanUnmarshalMsg") - - return u.msgs, u.p.err -} - -// does assignment to the variable "name" with the type "base" -func (u *unmarshalGen) assignAndCheck(name string, isnil string, base string) { - if !u.p.ok() { - return - } - u.p.printf("\n%s, %s, bts, err = msgp.Read%sBytes(bts)", name, isnil, base) - u.p.wrapErrCheck(u.ctx.ArgsStr()) -} - -func (u *unmarshalGen) gStruct(s *Struct) { - if !u.p.ok() { - return - } - if s.AsTuple { - u.tuple(s) - } else { - u.mapstruct(s) - } - return -} - -func (u *unmarshalGen) tuple(s *Struct) { - - // open block - sz := randIdent() - u.p.declare(sz, "int") - u.assignAndCheck(sz, "_", arrayHeader) - u.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz) - for i := range s.Fields { - if !u.p.ok() { - return - } - u.ctx.PushString(s.Fields[i].FieldName) - next(u, s.Fields[i].FieldElem) - u.ctx.Pop() - } -} - -func (u *unmarshalGen) mapstruct(s *Struct) { - u.needsField() - sz := randIdent() - isnil := randIdent() - u.p.declare(sz, "int") - u.p.declare(isnil, "bool") - - // go-codec compat: decode an array as sequential elements from this struct, - // in the order they are defined in the Go type (as opposed to canonical - // order by sorted tag). - u.p.printf("\n%s, %s, bts, err = msgp.Read%sBytes(bts)", sz, isnil, mapHeader) - u.p.printf("\nif _, ok := err.(msgp.TypeError); ok {") - - u.assignAndCheck(sz, isnil, arrayHeader) - - u.ctx.PushString("struct-from-array") - for i := range s.Fields { - if !ast.IsExported(s.Fields[i].FieldName) { - continue - } - - u.p.printf("\nif %s > 0 {", sz) - u.p.printf("\n%s--", sz) - u.ctx.PushString(s.Fields[i].FieldName) - next(u, s.Fields[i].FieldElem) - u.ctx.Pop() - u.p.printf("\n}") - } - - u.p.printf("\nif %s > 0 {", sz) - u.p.printf("\nerr = msgp.ErrTooManyArrayFields(%s)", sz) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - u.p.printf("\n}") - u.ctx.Pop() - - u.p.printf("\n} else {") - u.p.wrapErrCheck(u.ctx.ArgsStr()) - - u.p.printf("\nif %s {", isnil) - u.p.printf("\n %s = %s{}", s.Varname(), s.TypeName()) - u.p.printf("\n}") - - u.p.printf("\nfor %s > 0 {", sz) - u.p.printf("\n%s--; field, bts, err = msgp.ReadMapKeyZC(bts)", sz) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - u.p.print("\nswitch string(field) {") - for i := range s.Fields { - if !ast.IsExported(s.Fields[i].FieldName) { - continue - } - - if !u.p.ok() { - return - } - u.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag) - u.ctx.PushString(s.Fields[i].FieldName) - next(u, s.Fields[i].FieldElem) - u.ctx.Pop() - } - u.p.print("\ndefault:\nerr = msgp.ErrNoField(string(field))") - u.p.wrapErrCheck(u.ctx.ArgsStr()) - u.p.print("\n}") // close switch - u.p.print("\n}") // close for loop - u.p.print("\n}") // close else statement for array decode -} - -func (u *unmarshalGen) gBase(b *BaseElem) { - if !u.p.ok() { - return - } - - refname := b.Varname() // assigned to - lowered := b.Varname() // passed as argument - if b.Convert { - // begin 'tmp' block - refname = randIdent() - lowered = b.ToBase() + "(" + lowered + ")" - u.p.printf("\n{\nvar %s %s", refname, b.BaseType()) - } - - switch b.Value { - case Bytes: - if b.common.AllocBound() != "" { - sz := randIdent() - u.p.printf("\nvar %s int", sz) - u.p.printf("\n%s, err = msgp.ReadBytesBytesHeader(bts)", sz) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - u.p.printf("\nif %s > %s {", sz, b.common.AllocBound()) - u.p.printf("\nerr = msgp.ErrOverflow(uint64(%s), uint64(%s))", sz, b.common.AllocBound()) - u.p.printf("\nreturn") - u.p.printf("\n}") - } - u.p.printf("\n%s, bts, err = msgp.ReadBytesBytes(bts, %s)", refname, lowered) - case Ext: - u.p.printf("\nbts, err = msgp.ReadExtensionBytes(bts, %s)", lowered) - case IDENT: - u.p.printf("\nbts, err = %s.UnmarshalMsgWithState(bts, st)", lowered) - case String: - if b.common.AllocBound() != "" { - sz := randIdent() - u.p.printf("\nvar %s int", sz) - u.p.printf("\n%s, err = msgp.ReadBytesBytesHeader(bts)", sz) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - u.p.printf("\nif %s > %s {", sz, b.common.AllocBound()) - u.p.printf("\nerr = msgp.ErrOverflow(uint64(%s), uint64(%s))", sz, b.common.AllocBound()) - u.p.printf("\nreturn") - u.p.printf("\n}") - } - u.p.printf("\n%s, bts, err = msgp.ReadStringBytes(bts)", refname) - default: - u.p.printf("\n%s, bts, err = msgp.Read%sBytes(bts)", refname, b.BaseName()) - } - u.p.wrapErrCheck(u.ctx.ArgsStr()) - - if b.Convert { - // close 'tmp' block - if b.ShimMode == Cast { - u.p.printf("\n%s = %s(%s)\n", b.Varname(), b.FromBase(), refname) - } else { - u.p.printf("\n%s, err = %s(%s)", b.Varname(), b.FromBase(), refname) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - } - u.p.printf("}") - } -} - -func (u *unmarshalGen) gArray(a *Array) { - if !u.p.ok() { - return - } - - // special case for [const]byte objects - // see decode.go for symmetry - if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte { - u.p.printf("\nbts, err = msgp.ReadExactBytes(bts, (%s)[:])", a.Varname()) - u.p.wrapErrCheck(u.ctx.ArgsStr()) - return - } - - sz := randIdent() - u.p.declare(sz, "int") - u.assignAndCheck(sz, "_", arrayHeader) - u.p.arrayCheckBound(a.Size, sz) - - u.ctx.PushVar(a.Index) - u.p.printf("\nfor %[1]s := 0; %[1]s < %[2]s; %[1]s++ {", a.Index, sz) - next(u, a.Els) - u.p.closeblock() - u.ctx.Pop() -} - -func (u *unmarshalGen) gSlice(s *Slice) { - if !u.p.ok() { - return - } - sz := randIdent() - isnil := randIdent() - u.p.declare(sz, "int") - u.p.declare(isnil, "bool") - u.assignAndCheck(sz, isnil, arrayHeader) - resizemsgs := u.p.resizeSlice(sz, isnil, s, u.ctx.ArgsStr()) - u.msgs = append(u.msgs, resizemsgs...) - childElement := s.Els - if s.Els.AllocBound() == "" && len(strings.Split(s.AllocBound(), ",")) > 1 { - childElement = s.Els.Copy() - childElement.SetAllocBound(s.AllocBound()[strings.Index(s.AllocBound(), ",")+1:]) - } - u.p.rangeBlock(u.ctx, s.Index, s.Varname(), u, childElement) -} - -func (u *unmarshalGen) gMap(m *Map) { - if !u.p.ok() { - return - } - sz := randIdent() - isnil := randIdent() - u.p.declare(sz, "int") - u.p.declare(isnil, "bool") - u.assignAndCheck(sz, isnil, mapHeader) - - // allocate or clear map - resizemsgs := u.p.resizeMap(sz, isnil, m, u.ctx.ArgsStr()) - u.msgs = append(u.msgs, resizemsgs...) - - // loop and get key,value - u.p.printf("\nfor %s > 0 {", sz) - u.p.printf("\nvar %s %s; var %s %s; %s--", m.Keyidx, m.Key.TypeName(), m.Validx, m.Value.TypeName(), sz) - next(u, m.Key) - u.ctx.PushVar(m.Keyidx) - next(u, m.Value) - u.ctx.Pop() - u.p.mapAssign(m) - u.p.closeblock() -} - -func (u *unmarshalGen) gPtr(p *Ptr) { - u.p.printf("\nif msgp.IsNil(bts) { bts, err = msgp.ReadNilBytes(bts); if err != nil { return }; %s = nil; } else { ", p.Varname()) - u.p.initPtr(p) - next(u, p.Value) - u.p.closeblock() -} diff --git a/msgp/go.mod b/msgp/go.mod deleted file mode 100644 index c5f3bfa33b..0000000000 --- a/msgp/go.mod +++ /dev/null @@ -1,19 +0,0 @@ -module github.com/algorand/msgp - -go 1.20 - -require ( - github.com/daixiang0/gci v0.3.2 - github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 - golang.org/x/tools v0.1.5 -) - -require ( - github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/kr/text v0.2.0 // indirect - golang.org/x/mod v0.5.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect -) diff --git a/msgp/go.sum b/msgp/go.sum deleted file mode 100644 index 6b653c5230..0000000000 --- a/msgp/go.sum +++ /dev/null @@ -1,27 +0,0 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/daixiang0/gci v0.3.2 h1:MDBsgEJGSJ++/N6Je/b2yK5x5WqmKYlzmILLVP41nl8= -github.com/daixiang0/gci v0.3.2/go.mod h1:jaASoJmv/ykO9dAAPy31iJnreV19248qKDdVWf3QgC4= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4= -github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= -golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/msgp/issue185_test.go b/msgp/issue185_test.go deleted file mode 100644 index c888b4158a..0000000000 --- a/msgp/issue185_test.go +++ /dev/null @@ -1,312 +0,0 @@ -package main - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "sort" - "testing" - "text/template" - - "github.com/algorand/msgp/gen" -) - -// When stuff's going wrong, you'll be glad this is here! -const debugTemp = false - -// Ensure that consistent identifiers are generated on a per-method basis by msgp. -// -// Also ensure that no duplicate identifiers appear in a method. -// -// structs are currently processed alphabetically by msgp. this test relies on -// that property. -// -func TestIssue185Idents(t *testing.T) { - t.SkipNow() - - var identCases = []struct { - tpl *template.Template - expectedChanged []string - }{ - {tpl: issue185IdentsTpl, expectedChanged: []string{"Test1"}}, - {tpl: issue185ComplexIdentsTpl, expectedChanged: []string{"Test2"}}, - } - - methods := []string{"Msgsize", "MarshalMsg", "UnmarshalMsg"} - - for idx, identCase := range identCases { - // generate the code, extract the generated variable names, mapped to function name - var tplData issue185TplData - varsBefore, err := loadVars(identCase.tpl, tplData) - if err != nil { - t.Fatalf("%d: could not extract before vars: %v", idx, err) - } - - // regenerate the code with extra field(s), extract the generated variable - // names, mapped to function name - tplData.Extra = true - varsAfter, err := loadVars(identCase.tpl, tplData) - if err != nil { - t.Fatalf("%d: could not extract after vars: %v", idx, err) - } - - // ensure that all declared variable names inside each of the methods we - // expect to change have actually changed - for _, stct := range identCase.expectedChanged { - for _, method := range methods { - fn := fmt.Sprintf("%s.%s", stct, method) - - bv, av := varsBefore.Value(fn), varsAfter.Value(fn) - if len(bv) > 0 && len(av) > 0 && reflect.DeepEqual(bv, av) { - t.Fatalf("%d vars identical! expected vars to change for %s", idx, fn) - } - delete(varsBefore, fn) - delete(varsAfter, fn) - } - } - - // all of the remaining keys should not have changed - for bmethod, bvars := range varsBefore { - avars := varsAfter.Value(bmethod) - - if !reflect.DeepEqual(bvars, avars) { - t.Fatalf("%d: vars changed! expected vars identical for %s", idx, bmethod) - } - delete(varsBefore, bmethod) - delete(varsAfter, bmethod) - } - - if len(varsBefore) > 0 || len(varsAfter) > 0 { - t.Fatalf("%d: unexpected methods remaining", idx) - } - } -} - -type issue185TplData struct { - Extra bool -} - -func TestIssue185Overlap(t *testing.T) { - t.SkipNow() - - var overlapCases = []struct { - tpl *template.Template - data issue185TplData - }{ - {tpl: issue185IdentsTpl, data: issue185TplData{Extra: false}}, - {tpl: issue185IdentsTpl, data: issue185TplData{Extra: true}}, - {tpl: issue185ComplexIdentsTpl, data: issue185TplData{Extra: false}}, - {tpl: issue185ComplexIdentsTpl, data: issue185TplData{Extra: true}}, - } - - for idx, o := range overlapCases { - // regenerate the code with extra field(s), extract the generated variable - // names, mapped to function name - mvars, err := loadVars(o.tpl, o.data) - if err != nil { - t.Fatalf("%d: could not extract after vars: %v", idx, err) - } - - identCnt := 0 - for fn, vars := range mvars { - sort.Strings(vars) - - // Loose sanity check to make sure the tests expectations aren't broken. - // If the prefix ever changes, this needs to change. - for _, v := range vars { - if v[0] == 'z' { - identCnt++ - } - } - - for i := 0; i < len(vars)-1; i++ { - if vars[i] == vars[i+1] { - t.Fatalf("%d: duplicate var %s in function %s", idx, vars[i], fn) - } - } - } - - // one last sanity check: if there aren't any vars that start with 'z', - // this test's expectations are unsatisfiable. - if identCnt == 0 { - t.Fatalf("%d: no generated identifiers found", idx) - } - } -} - -func loadVars(tpl *template.Template, tplData interface{}) (vars extractedVars, err error) { - tempDir, err := ioutil.TempDir("", "msgp-") - if err != nil { - err = fmt.Errorf("could not create temp dir: %v", err) - return - } - - if !debugTemp { - defer os.RemoveAll(tempDir) - } else { - fmt.Println(tempDir) - } - tfile := filepath.Join(tempDir, "msg.go") - genFile := newFilename(tfile, "") - - if err = goGenerateTpl(tempDir, tfile, tpl, tplData); err != nil { - err = fmt.Errorf("could not generate code: %v", err) - return - } - - vars, err = extractVars(genFile) - if err != nil { - err = fmt.Errorf("could not extract after vars: %v", err) - return - } - - return -} - -type varVisitor struct { - vars []string - fset *token.FileSet -} - -func (v *varVisitor) Visit(node ast.Node) (w ast.Visitor) { - gen, ok := node.(*ast.GenDecl) - if !ok { - return v - } - for _, spec := range gen.Specs { - if vspec, ok := spec.(*ast.ValueSpec); ok { - for _, n := range vspec.Names { - v.vars = append(v.vars, n.Name) - } - } - } - return v -} - -type extractedVars map[string][]string - -func (e extractedVars) Value(key string) []string { - if v, ok := e[key]; ok { - return v - } - panic(fmt.Errorf("unknown key %s", key)) -} - -func extractVars(file string) (extractedVars, error) { - fset := token.NewFileSet() - - f, err := parser.ParseFile(fset, file, nil, 0) - if err != nil { - return nil, err - } - - vars := make(map[string][]string) - for _, d := range f.Decls { - switch d := d.(type) { - case *ast.FuncDecl: - sn := "" - switch rt := d.Recv.List[0].Type.(type) { - case *ast.Ident: - sn = rt.Name - case *ast.StarExpr: - sn = rt.X.(*ast.Ident).Name - default: - panic("unknown receiver type") - } - - key := fmt.Sprintf("%s.%s", sn, d.Name.Name) - vis := &varVisitor{fset: fset} - ast.Walk(vis, d.Body) - vars[key] = vis.vars - } - } - return vars, nil -} - -func goGenerateTpl(cwd, tfile string, tpl *template.Template, tplData interface{}) error { - outf, err := os.OpenFile(tfile, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) - if err != nil { - return err - } - defer outf.Close() - - if err := tpl.Execute(outf, tplData); err != nil { - return err - } - - mode := gen.Size | gen.Marshal | gen.Unmarshal - - return Run(tfile, mode, false, "") -} - -var issue185IdentsTpl = template.Must(template.New("").Parse(` -package issue185 - -//go:generate msgp - -type Test1 struct { - Foo string - Bar string - {{ if .Extra }}Baz []string{{ end }} - Qux string -} - -type Test2 struct { - Foo string - Bar string - Baz string -} -`)) - -var issue185ComplexIdentsTpl = template.Must(template.New("").Parse(` -package issue185 - -//go:generate msgp - -type Test1 struct { - Foo string - Bar string - Baz string -} - -type Test2 struct { - Foo string - Bar string - Baz []string - Qux map[string]string - Yep map[string]map[string]string - Quack struct { - Quack struct { - Quack struct { - {{ if .Extra }}Extra []string{{ end }} - Quack string - } - } - } - Nup struct { - Foo string - Bar string - Baz []string - Qux map[string]string - Yep map[string]map[string]string - } - Ding struct { - Dong struct { - Dung struct { - Thing string - } - } - } -} - -type Test3 struct { - Foo string - Bar string - Baz string -} -`)) diff --git a/msgp/main.go b/msgp/main.go deleted file mode 100644 index 8a157d28f4..0000000000 --- a/msgp/main.go +++ /dev/null @@ -1,117 +0,0 @@ -// msgp is a code generation tool for -// creating methods to serialize and de-serialize -// Go data structures to and from MessagePack. -// -// This package is targeted at the `go generate` tool. -// To use it, include the following directive in a -// go source file with types requiring source generation: -// -// //go:generate msgp -// -// The go generate tool should set the proper environment variables for -// the generator to execute without any command-line flags. However, the -// following options are supported, if you need them: -// -// -o = output file name (default is {input}_gen.go) -// -file = input file name (or directory; default is $GOFILE, which is set by the `go generate` command) -// -io = satisfy the `msgp.Decodable` and `msgp.Encodable` interfaces (default is true) -// -marshal = satisfy the `msgp.Marshaler` and `msgp.Unmarshaler` interfaces (default is true) -// -tests = generate tests and benchmarks (default is true) -// -// For more information, please read README.md, and the wiki at github.com/tinylib/msgp -// -package main - -import ( - "flag" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/algorand/msgp/gen" - "github.com/algorand/msgp/parse" - "github.com/algorand/msgp/printer" - "github.com/ttacon/chalk" -) - -var ( - out = flag.String("o", "", "output file") - file = flag.String("file", "", "input file") - marshal = flag.Bool("marshal", true, "create Marshal and Unmarshal methods") - tests = flag.Bool("tests", true, "create tests and benchmarks") - unexported = flag.Bool("unexported", true, "also process unexported types") - skipFormat = flag.Bool("skip-format", false, "skip formatting the generated code (for debug)") - warnPkgMask = flag.String("warnmask", "", "skip generating warnings on datatypes outside given package") -) - -func main() { - flag.Parse() - - // GOFILE is set by go generate - if *file == "" { - *file = os.Getenv("GOFILE") - if *file == "" { - fmt.Println(chalk.Red.Color("No file to parse.")) - os.Exit(1) - } - } - - var mode gen.Method - if *marshal { - mode |= (gen.Marshal | gen.Unmarshal | gen.Size | gen.IsZero | gen.MaxSize) - } - if *tests { - mode |= gen.Test - } - - if mode&^gen.Test == 0 { - fmt.Println(chalk.Red.Color("No methods to generate; -marshal=false")) - os.Exit(1) - } - - if err := Run(*file, mode, *unexported, *warnPkgMask); err != nil { - fmt.Println(chalk.Red.Color(err.Error())) - os.Exit(1) - } -} - -// Run writes all methods using the associated file or path, e.g. -// -// err := msgp.Run("path/to/myfile.go", gen.Size|gen.Marshal|gen.Unmarshal|gen.Test, false) -// -func Run(gofile string, mode gen.Method, unexported bool, warnPkgMask string) error { - if mode&^gen.Test == 0 { - return nil - } - fmt.Println(chalk.Magenta.Color("======== MessagePack Code Generator =======")) - fmt.Printf(chalk.Magenta.Color(">>> Input: \"%s\"\n"), gofile) - fs, err := parse.File(gofile, unexported, warnPkgMask) - if err != nil { - return err - } - - if len(fs.Identities) == 0 { - fmt.Println(chalk.Magenta.Color("No types requiring code generation were found!")) - return nil - } - - return printer.PrintFile(newFilename(gofile, fs.Package), fs, mode, *skipFormat) -} - -// picks a new file name based on input flags and input filename(s). -func newFilename(old string, pkg string) string { - if *out != "" { - if pre := strings.TrimPrefix(*out, old); len(pre) > 0 && - !strings.HasSuffix(*out, ".go") { - return filepath.Join(old, *out) - } - return *out - } - - if fi, err := os.Stat(old); err == nil && fi.IsDir() { - old = filepath.Join(old, pkg) - } - // new file name is old file name + _gen.go - return strings.TrimSuffix(old, ".go") + "_gen.go" -} diff --git a/msgp/msgp/defs.go b/msgp/msgp/defs.go deleted file mode 100644 index b1188b041d..0000000000 --- a/msgp/msgp/defs.go +++ /dev/null @@ -1,142 +0,0 @@ -// This package is the support library for the msgp code generator (http://github.com/algorand/msgp). -// -// This package defines the utilites used by the msgp code generator for encoding and decoding MessagePack -// from []byte and io.Reader/io.Writer types. Much of this package is devoted to helping the msgp code -// generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces. -// -// This package defines four "families" of functions: -// - AppendXxxx() appends an object to a []byte in MessagePack encoding. -// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. -// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. -// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. -// -// Once a type has satisfied the `Encodable` and `Decodable` interfaces, -// it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using -// msgp.Encode(io.Writer, msgp.Encodable) -// and -// msgp.Decode(io.Reader, msgp.Decodable) -// -// There are also methods for converting MessagePack to JSON without -// an explicit de-serialization step. -// -// For additional tips, tricks, and gotchas, please visit -// the wiki at http://github.com/tinylib/msgp -package msgp - -const last4 = 0x0f -const first4 = 0xf0 -const last5 = 0x1f -const first3 = 0xe0 -const last7 = 0x7f - -func isfixint(b byte) bool { - return b>>7 == 0 -} - -func isnfixint(b byte) bool { - return b&first3 == mnfixint -} - -func isfixmap(b byte) bool { - return b&first4 == mfixmap -} - -func isfixarray(b byte) bool { - return b&first4 == mfixarray -} - -func isfixstr(b byte) bool { - return b&first3 == mfixstr -} - -func wfixint(u uint8) byte { - return u & last7 -} - -func rfixint(b byte) uint8 { - return b -} - -func wnfixint(i int8) byte { - return byte(i) | mnfixint -} - -func rnfixint(b byte) int8 { - return int8(b) -} - -func rfixmap(b byte) uint8 { - return b & last4 -} - -func wfixmap(u uint8) byte { - return mfixmap | (u & last4) -} - -func rfixstr(b byte) uint8 { - return b & last5 -} - -func wfixstr(u uint8) byte { - return (u & last5) | mfixstr -} - -func rfixarray(b byte) uint8 { - return (b & last4) -} - -func wfixarray(u uint8) byte { - return (u & last4) | mfixarray -} - -// These are all the byte -// prefixes defined by the -// msgpack standard -const ( - // 0XXXXXXX - mfixint uint8 = 0x00 - - // 111XXXXX - mnfixint uint8 = 0xe0 - - // 1000XXXX - mfixmap uint8 = 0x80 - - // 1001XXXX - mfixarray uint8 = 0x90 - - // 101XXXXX - mfixstr uint8 = 0xa0 - - mnil uint8 = 0xc0 - mfalse uint8 = 0xc2 - mtrue uint8 = 0xc3 - mbin8 uint8 = 0xc4 - mbin16 uint8 = 0xc5 - mbin32 uint8 = 0xc6 - mext8 uint8 = 0xc7 - mext16 uint8 = 0xc8 - mext32 uint8 = 0xc9 - mfloat32 uint8 = 0xca - mfloat64 uint8 = 0xcb - muint8 uint8 = 0xcc - muint16 uint8 = 0xcd - muint32 uint8 = 0xce - muint64 uint8 = 0xcf - mint8 uint8 = 0xd0 - mint16 uint8 = 0xd1 - mint32 uint8 = 0xd2 - mint64 uint8 = 0xd3 - mfixext1 uint8 = 0xd4 - mfixext2 uint8 = 0xd5 - mfixext4 uint8 = 0xd6 - mfixext8 uint8 = 0xd7 - mfixext16 uint8 = 0xd8 - mstr8 uint8 = 0xd9 - mstr16 uint8 = 0xda - mstr32 uint8 = 0xdb - marray16 uint8 = 0xdc - marray32 uint8 = 0xdd - mmap16 uint8 = 0xde - mmap32 uint8 = 0xdf -) diff --git a/msgp/msgp/defs_test.go b/msgp/msgp/defs_test.go deleted file mode 100644 index 667dfd6012..0000000000 --- a/msgp/msgp/defs_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package msgp_test - -//go:generate msgp -o=defgen_test.go -tests=false - -type Blobs []Blob - -type Blob struct { - Name string `msg:"name"` - Float float64 `msg:"float"` - Bytes []byte `msg:"bytes"` - Amount int64 `msg:"amount"` -} diff --git a/msgp/msgp/elsize.go b/msgp/msgp/elsize.go deleted file mode 100644 index 95762e7eeb..0000000000 --- a/msgp/msgp/elsize.go +++ /dev/null @@ -1,99 +0,0 @@ -package msgp - -// size of every object on the wire, -// plus type information. gives us -// constant-time type information -// for traversing composite objects. -// -var sizes = [256]bytespec{ - mnil: {size: 1, extra: constsize, typ: NilType}, - mfalse: {size: 1, extra: constsize, typ: BoolType}, - mtrue: {size: 1, extra: constsize, typ: BoolType}, - mbin8: {size: 2, extra: extra8, typ: BinType}, - mbin16: {size: 3, extra: extra16, typ: BinType}, - mbin32: {size: 5, extra: extra32, typ: BinType}, - mext8: {size: 3, extra: extra8, typ: ExtensionType}, - mext16: {size: 4, extra: extra16, typ: ExtensionType}, - mext32: {size: 6, extra: extra32, typ: ExtensionType}, - mfloat32: {size: 5, extra: constsize, typ: Float32Type}, - mfloat64: {size: 9, extra: constsize, typ: Float64Type}, - muint8: {size: 2, extra: constsize, typ: UintType}, - muint16: {size: 3, extra: constsize, typ: UintType}, - muint32: {size: 5, extra: constsize, typ: UintType}, - muint64: {size: 9, extra: constsize, typ: UintType}, - mint8: {size: 2, extra: constsize, typ: IntType}, - mint16: {size: 3, extra: constsize, typ: IntType}, - mint32: {size: 5, extra: constsize, typ: IntType}, - mint64: {size: 9, extra: constsize, typ: IntType}, - mfixext1: {size: 3, extra: constsize, typ: ExtensionType}, - mfixext2: {size: 4, extra: constsize, typ: ExtensionType}, - mfixext4: {size: 6, extra: constsize, typ: ExtensionType}, - mfixext8: {size: 10, extra: constsize, typ: ExtensionType}, - mfixext16: {size: 18, extra: constsize, typ: ExtensionType}, - mstr8: {size: 2, extra: extra8, typ: StrType}, - mstr16: {size: 3, extra: extra16, typ: StrType}, - mstr32: {size: 5, extra: extra32, typ: StrType}, - marray16: {size: 3, extra: array16v, typ: ArrayType}, - marray32: {size: 5, extra: array32v, typ: ArrayType}, - mmap16: {size: 3, extra: map16v, typ: MapType}, - mmap32: {size: 5, extra: map32v, typ: MapType}, -} - -func init() { - // set up fixed fields - - // fixint - for i := mfixint; i < 0x80; i++ { - sizes[i] = bytespec{size: 1, extra: constsize, typ: IntType} - } - - // nfixint - for i := uint16(mnfixint); i < 0x100; i++ { - sizes[uint8(i)] = bytespec{size: 1, extra: constsize, typ: IntType} - } - - // fixstr gets constsize, - // since the prefix yields the size - for i := mfixstr; i < 0xc0; i++ { - sizes[i] = bytespec{size: 1 + rfixstr(i), extra: constsize, typ: StrType} - } - - // fixmap - for i := mfixmap; i < 0x90; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(2 * rfixmap(i)), typ: MapType} - } - - // fixarray - for i := mfixarray; i < 0xa0; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(rfixarray(i)), typ: ArrayType} - } -} - -// a valid bytespsec has -// non-zero 'size' and -// non-zero 'typ' -type bytespec struct { - size uint8 // prefix size information - extra varmode // extra size information - typ Type // type - _ byte // makes bytespec 4 bytes (yes, this matters) -} - -// size mode -// if positive, # elements for composites -type varmode int8 - -const ( - constsize varmode = 0 // constant size (size bytes + uint8(varmode) objects) - extra8 = -1 // has uint8(p[1]) extra bytes - extra16 = -2 // has be16(p[1:]) extra bytes - extra32 = -3 // has be32(p[1:]) extra bytes - map16v = -4 // use map16 - map32v = -5 // use map32 - array16v = -6 // use array16 - array32v = -7 // use array32 -) - -func getType(v byte) Type { - return sizes[v].typ -} diff --git a/msgp/msgp/errors.go b/msgp/msgp/errors.go deleted file mode 100644 index fd2618855f..0000000000 --- a/msgp/msgp/errors.go +++ /dev/null @@ -1,354 +0,0 @@ -package msgp - -import ( - "fmt" - "reflect" -) - -const resumableDefault = false - -var ( - // ErrShortBytes is returned when the - // slice being decoded is too short to - // contain the contents of the message - ErrShortBytes error = errShort{} - - // this error is only returned - // if we reach code that should - // be unreachable - fatal error = errFatal{} -) - -type ErrNoField string - -func (e ErrNoField) Error() string { - return fmt.Sprintf("Unknown field: %s", string(e)) -} - -type ErrTooManyArrayFields int - -func (e ErrTooManyArrayFields) Error() string { - return fmt.Sprintf("Too many array fields when decoding into struct: %d left", int(e)) -} - -// Error is the interface satisfied -// by all of the errors that originate -// from this package. -type Error interface { - error - - // Resumable returns whether - // or not the error means that - // the stream of data is malformed - // and the information is unrecoverable. - Resumable() bool -} - -// contextError allows msgp Error instances to be enhanced with additional -// context about their origin. -type contextError interface { - Error - - // withContext must not modify the error instance - it must clone and - // return a new error with the context added. - withContext(ctx string) error -} - -// Cause returns the underlying cause of an error that has been wrapped -// with additional context. -func Cause(e error) error { - out := e - if e, ok := e.(errWrapped); ok && e.cause != nil { - out = e.cause - } - return out -} - -// Resumable returns whether or not the error means that the stream of data is -// malformed and the information is unrecoverable. -func Resumable(e error) bool { - if e, ok := e.(Error); ok { - return e.Resumable() - } - return resumableDefault -} - -// WrapError wraps an error with additional context that allows the part of the -// serialized type that caused the problem to be identified. Underlying errors -// can be retrieved using Cause() -// -// The input error is not modified - a new error should be returned. -// -// ErrShortBytes is not wrapped with any context due to backward compatibility -// issues with the public API. -func WrapError(err error, ctx ...interface{}) error { - switch e := err.(type) { - case errShort, ErrMaxDepthExceeded: - return e - case contextError: - return e.withContext(ctxString(ctx)) - default: - return errWrapped{cause: err, ctx: ctxString(ctx)} - } -} - -// ctxString converts the incoming interface{} slice into a single string. -func ctxString(ctx []interface{}) string { - out := "" - for idx, cv := range ctx { - if idx > 0 { - out += "/" - } - out += fmt.Sprintf("%v", cv) - } - return out -} - -func addCtx(ctx, add string) string { - if ctx != "" { - return add + "/" + ctx - } else { - return add - } -} - -// errWrapped allows arbitrary errors passed to WrapError to be enhanced with -// context and unwrapped with Cause() -type errWrapped struct { - cause error - ctx string -} - -func (e errWrapped) Error() string { - if e.ctx != "" { - return fmt.Sprintf("%s at %s", e.cause, e.ctx) - } else { - return e.cause.Error() - } -} - -func (e errWrapped) Resumable() bool { - if e, ok := e.cause.(Error); ok { - return e.Resumable() - } - return resumableDefault -} - -type errShort struct{} - -func (e errShort) Error() string { return "msgp: too few bytes left to read object" } -func (e errShort) Resumable() bool { return false } - -// errOverflow is returned when the message -// being decoded has some length field that -// exceeds the maximum allowed length. -type errOverflow struct { - l uint64 - bound uint64 -} - -func (e errOverflow) Error() string { - return fmt.Sprintf("msgp: length overflow: %d > %d", e.l, e.bound) -} - -func (e errOverflow) Resumable() bool { - return false -} - -func ErrOverflow(l uint64, bound uint64) error { - return errOverflow{l, bound} -} - -type errFatal struct { - ctx string -} - -func (f errFatal) Error() string { - out := "msgp: fatal decoding error (unreachable code)" - if f.ctx != "" { - out += " at " + f.ctx - } - return out -} - -func (f errFatal) Resumable() bool { return false } - -func (f errFatal) withContext(ctx string) error { f.ctx = addCtx(f.ctx, ctx); return f } - -// ArrayError is an error returned -// when decoding a fix-sized array -// of the wrong size -type ArrayError struct { - Wanted int - Got int - ctx string -} - -// Error implements the error interface -func (a ArrayError) Error() string { - out := fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got) - if a.ctx != "" { - out += " at " + a.ctx - } - return out -} - -// Resumable is always 'true' for ArrayErrors -func (a ArrayError) Resumable() bool { return true } - -func (a ArrayError) withContext(ctx string) error { a.ctx = addCtx(a.ctx, ctx); return a } - -// IntOverflow is returned when a call -// would downcast an integer to a type -// with too few bits to hold its value. -type IntOverflow struct { - Value int64 // the value of the integer - FailedBitsize int // the bit size that the int64 could not fit into - ctx string -} - -// Error implements the error interface -func (i IntOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize) - if i.ctx != "" { - str += " at " + i.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (i IntOverflow) Resumable() bool { return true } - -func (i IntOverflow) withContext(ctx string) error { i.ctx = addCtx(i.ctx, ctx); return i } - -// UintOverflow is returned when a call -// would downcast an unsigned integer to a type -// with too few bits to hold its value -type UintOverflow struct { - Value uint64 // value of the uint - FailedBitsize int // the bit size that couldn't fit the value - ctx string -} - -// Error implements the error interface -func (u UintOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize) - if u.ctx != "" { - str += " at " + u.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (u UintOverflow) Resumable() bool { return true } - -func (u UintOverflow) withContext(ctx string) error { u.ctx = addCtx(u.ctx, ctx); return u } - -// UintBelowZero is returned when a call -// would cast a signed integer below zero -// to an unsigned integer. -type UintBelowZero struct { - Value int64 // value of the incoming int - ctx string -} - -// Error implements the error interface -func (u UintBelowZero) Error() string { - str := fmt.Sprintf("msgp: attempted to cast int %d to unsigned", u.Value) - if u.ctx != "" { - str += " at " + u.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (u UintBelowZero) Resumable() bool { return true } - -func (u UintBelowZero) withContext(ctx string) error { - u.ctx = ctx - return u -} - -// A TypeError is returned when a particular -// decoding method is unsuitable for decoding -// a particular MessagePack value. -type TypeError struct { - Method Type // Type expected by method - Encoded Type // Type actually encoded - - ctx string -} - -// Error implements the error interface -func (t TypeError) Error() string { - out := fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method) - if t.ctx != "" { - out += " at " + t.ctx - } - return out -} - -// Resumable returns 'true' for TypeErrors -func (t TypeError) Resumable() bool { return true } - -func (t TypeError) withContext(ctx string) error { t.ctx = addCtx(t.ctx, ctx); return t } - -// returns either InvalidPrefixError or -// TypeError depending on whether or not -// the prefix is recognized -func badPrefix(want Type, lead byte) error { - t := sizes[lead].typ - if t == InvalidType { - return InvalidPrefixError(lead) - } - return TypeError{Method: want, Encoded: t} -} - -// InvalidPrefixError is returned when a bad encoding -// uses a prefix that is not recognized in the MessagePack standard. -// This kind of error is unrecoverable. -type InvalidPrefixError byte - -// Error implements the error interface -func (i InvalidPrefixError) Error() string { - return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i)) -} - -// Resumable returns 'false' for InvalidPrefixErrors -func (i InvalidPrefixError) Resumable() bool { return false } - -// ErrUnsupportedType is returned -// when a bad argument is supplied -// to a function that takes `interface{}`. -type ErrUnsupportedType struct { - T reflect.Type - - ctx string -} - -// Error implements error -func (e *ErrUnsupportedType) Error() string { - out := fmt.Sprintf("msgp: type %q not supported", e.T) - if e.ctx != "" { - out += " at " + e.ctx - } - return out -} - -// Resumable returns 'true' for ErrUnsupportedType -func (e *ErrUnsupportedType) Resumable() bool { return true } - -func (e *ErrUnsupportedType) withContext(ctx string) error { - o := *e - o.ctx = addCtx(o.ctx, ctx) - return &o -} - -// ErrMaxDepthExceeded is returned if the maximum traversal depth is exceeded. -type ErrMaxDepthExceeded struct{} - -// Error implements error -func (e ErrMaxDepthExceeded) Error() string { return "Max depth exceeded" } - -// Resumable implements Error -func (e ErrMaxDepthExceeded) Resumable() bool { return false } diff --git a/msgp/msgp/errors_test.go b/msgp/msgp/errors_test.go deleted file mode 100644 index 308bec64d7..0000000000 --- a/msgp/msgp/errors_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package msgp - -import ( - "errors" - "fmt" - "strings" - "testing" -) - -func TestWrapVanillaErrorWithNoAdditionalContext(t *testing.T) { - err := errors.New("test") - w := WrapError(err) - if w == err { - t.Fatal() - } - if w.Error() != err.Error() { - t.Fatal() - } - if w.(errWrapped).Resumable() { - t.Fatal() - } -} - -func TestWrapVanillaErrorWithAdditionalContext(t *testing.T) { - err := errors.New("test") - w := WrapError(err, "foo", "bar") - if w == err { - t.Fatal() - } - if w.Error() == err.Error() { - t.Fatal() - } - if w.(Error).Resumable() { - t.Fatal() - } - if !strings.HasPrefix(w.Error(), err.Error()) { - t.Fatal() - } - rest := w.Error()[len(err.Error()):] - if rest != " at foo/bar" { - t.Fatal() - } -} - -func TestWrapResumableError(t *testing.T) { - err := ArrayError{} - w := WrapError(err) - if !w.(Error).Resumable() { - t.Fatal() - } -} - -func TestWrapMultiple(t *testing.T) { - err := &TypeError{} - w := WrapError(WrapError(err, "b"), "a") - expected := `msgp: attempted to decode type "" with method for "" at a/b` - if expected != w.Error() { - t.Fatal() - } -} - -func TestCause(t *testing.T) { - for idx, err := range []error{ - errors.New("test"), - ArrayError{}, - &ErrUnsupportedType{}, - } { - t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { - cerr := WrapError(err, "test") - if cerr == err { - t.Fatal() - } - if Cause(err) != err { - t.Fatal() - } - }) - } -} - -func TestCauseShortByte(t *testing.T) { - err := ErrShortBytes - cerr := WrapError(err, "test") - if cerr != err { - t.Fatal() - } - if Cause(err) != err { - t.Fatal() - } -} diff --git a/msgp/msgp/extension.go b/msgp/msgp/extension.go deleted file mode 100644 index 4f279baa18..0000000000 --- a/msgp/msgp/extension.go +++ /dev/null @@ -1,287 +0,0 @@ -package msgp - -import ( - "fmt" - "math" -) - -const ( - // Complex64Extension is the extension number used for complex64 - Complex64Extension = 3 - - // Complex128Extension is the extension number used for complex128 - Complex128Extension = 4 - - // TimeExtension is the extension number used for time.Time - TimeExtension = 5 -) - -// our extensions live here -var extensionReg = make(map[int8]func() Extension) - -// RegisterExtension registers extensions so that they -// can be initialized and returned by methods that -// decode `interface{}` values. This should only -// be called during initialization. f() should return -// a newly-initialized zero value of the extension. Keep in -// mind that extensions 3, 4, and 5 are reserved for -// complex64, complex128, and time.Time, respectively, -// and that MessagePack reserves extension types from -127 to -1. -// -// For example, if you wanted to register a user-defined struct: -// -// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) -// -// RegisterExtension will panic if you call it multiple times -// with the same 'typ' argument, or if you use a reserved -// type (3, 4, or 5). -func RegisterExtension(typ int8, f func() Extension) { - switch typ { - case Complex64Extension, Complex128Extension, TimeExtension: - panic(fmt.Sprint("msgp: forbidden extension type:", typ)) - } - if _, ok := extensionReg[typ]; ok { - panic(fmt.Sprint("msgp: RegisterExtension() called with typ", typ, "more than once")) - } - extensionReg[typ] = f -} - -// ExtensionTypeError is an error type returned -// when there is a mis-match between an extension type -// and the type encoded on the wire -type ExtensionTypeError struct { - Got int8 - Want int8 -} - -// Error implements the error interface -func (e ExtensionTypeError) Error() string { - return fmt.Sprintf("msgp: error decoding extension: wanted type %d; got type %d", e.Want, e.Got) -} - -// Resumable returns 'true' for ExtensionTypeErrors -func (e ExtensionTypeError) Resumable() bool { return true } - -func errExt(got int8, wanted int8) error { - return ExtensionTypeError{Got: got, Want: wanted} -} - -// Extension is the interface fulfilled -// by types that want to define their -// own binary encoding. -type Extension interface { - // ExtensionType should return - // a int8 that identifies the concrete - // type of the extension. (Types <0 are - // officially reserved by the MessagePack - // specifications.) - ExtensionType() int8 - - // Len should return the length - // of the data to be encoded - Len() int - - // MarshalBinaryTo should copy - // the data into the supplied slice, - // assuming that the slice has length Len() - MarshalBinaryTo([]byte) error - - UnmarshalBinary([]byte) error -} - -// RawExtension implements the Extension interface -type RawExtension struct { - Data []byte - Type int8 -} - -// ExtensionType implements Extension.ExtensionType, and returns r.Type -func (r *RawExtension) ExtensionType() int8 { return r.Type } - -// Len implements Extension.Len, and returns len(r.Data) -func (r *RawExtension) Len() int { return len(r.Data) } - -// MarshalBinaryTo implements Extension.MarshalBinaryTo, -// and returns a copy of r.Data -func (r *RawExtension) MarshalBinaryTo(d []byte) error { - copy(d, r.Data) - return nil -} - -// UnmarshalBinary implements Extension.UnmarshalBinary, -// and sets r.Data to the contents of the provided slice -func (r *RawExtension) UnmarshalBinary(b []byte) error { - if cap(r.Data) >= len(b) { - r.Data = r.Data[0:len(b)] - } else { - r.Data = make([]byte, len(b)) - } - copy(r.Data, b) - return nil -} - -// peekExtension peeks at the extension encoding type -// (must guarantee at least 1 byte in 'b') -func peekExtension(b []byte) (int8, error) { - spec := sizes[b[0]] - size := spec.size - if spec.typ != ExtensionType { - return 0, badPrefix(ExtensionType, b[0]) - } - if len(b) < int(size) { - return 0, ErrShortBytes - } - // for fixed extensions, - // the type information is in - // the second byte - if spec.extra == constsize { - return int8(b[1]), nil - } - // otherwise, it's in the last - // part of the prefix - return int8(b[size-1]), nil -} - -// AppendExtension appends a MessagePack extension to the provided slice -func AppendExtension(b []byte, e Extension) ([]byte, error) { - l := e.Len() - var o []byte - var n int - switch l { - case 0: - o, n = ensure(b, 3) - o[n] = mext8 - o[n+1] = 0 - o[n+2] = byte(e.ExtensionType()) - return o[:n+3], nil - case 1: - o, n = ensure(b, 3) - o[n] = mfixext1 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 2: - o, n = ensure(b, 4) - o[n] = mfixext2 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 4: - o, n = ensure(b, 6) - o[n] = mfixext4 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 8: - o, n = ensure(b, 10) - o[n] = mfixext8 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 16: - o, n = ensure(b, 18) - o[n] = mfixext16 - o[n+1] = byte(e.ExtensionType()) - n += 2 - default: - switch { - case l < math.MaxUint8: - o, n = ensure(b, l+3) - o[n] = mext8 - o[n+1] = byte(uint8(l)) - o[n+2] = byte(e.ExtensionType()) - n += 3 - case l < math.MaxUint16: - o, n = ensure(b, l+4) - o[n] = mext16 - big.PutUint16(o[n+1:], uint16(l)) - o[n+3] = byte(e.ExtensionType()) - n += 4 - default: - o, n = ensure(b, l+6) - o[n] = mext32 - big.PutUint32(o[n+1:], uint32(l)) - o[n+5] = byte(e.ExtensionType()) - n += 6 - } - } - return o, e.MarshalBinaryTo(o[n:]) -} - -// ReadExtensionBytes reads an extension from 'b' into 'e' -// and returns any remaining bytes. -// Possible errors: -// - ErrShortBytes ('b' not long enough) -// - ExtensionTypeError{} (wire type not the same as e.Type()) -// - TypeError{} (next object not an extension) -// - InvalidPrefixError -// - An umarshal error returned from e.UnmarshalBinary -func ReadExtensionBytes(b []byte, e Extension) ([]byte, error) { - l := len(b) - if l < 3 { - return b, ErrShortBytes - } - lead := b[0] - var ( - sz int // size of 'data' - off int // offset of 'data' - typ int8 - ) - switch lead { - case mfixext1: - typ = int8(b[1]) - sz = 1 - off = 2 - case mfixext2: - typ = int8(b[1]) - sz = 2 - off = 2 - case mfixext4: - typ = int8(b[1]) - sz = 4 - off = 2 - case mfixext8: - typ = int8(b[1]) - sz = 8 - off = 2 - case mfixext16: - typ = int8(b[1]) - sz = 16 - off = 2 - case mext8: - sz = int(uint8(b[1])) - typ = int8(b[2]) - off = 3 - if sz == 0 { - return b[3:], e.UnmarshalBinary(b[3:3]) - } - case mext16: - if l < 4 { - return b, ErrShortBytes - } - sz = int(big.Uint16(b[1:])) - typ = int8(b[3]) - off = 4 - case mext32: - if l < 6 { - return b, ErrShortBytes - } - var err error - sz, err = u32int(big.Uint32(b[1:])) - if err != nil { - return b, err - } - typ = int8(b[5]) - off = 6 - default: - return b, badPrefix(ExtensionType, lead) - } - - if typ != e.ExtensionType() { - return b, errExt(typ, e.ExtensionType()) - } - - // the data of the extension starts - // at 'off' and is 'sz' bytes long - if len(b[off:]) < sz { - return b, ErrShortBytes - } - tot := off + sz - return b[tot:], e.UnmarshalBinary(b[off:tot]) -} diff --git a/msgp/msgp/extension_test.go b/msgp/msgp/extension_test.go deleted file mode 100644 index a977ea4667..0000000000 --- a/msgp/msgp/extension_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package msgp - -import ( - "math/rand" - "testing" - "time" -) - -var extSizes = [...]int{0, 1, 2, 4, 8, 16, int(tint8), int(tuint16), int(tuint32)} - -func randomExt() RawExtension { - e := RawExtension{} - e.Type = int8(rand.Int()) - e.Data = RandBytes(extSizes[rand.Intn(len(extSizes))]) - return e -} - -func TestReadWriteExtensionBytes(t *testing.T) { - var bts []byte - rand.Seed(time.Now().Unix()) - - for i := 0; i < 24; i++ { - e := randomExt() - bts, _ = AppendExtension(bts[0:0], &e) - _, err := ReadExtensionBytes(bts, &e) - if err != nil { - t.Errorf("error with extension (length %d): %s", len(bts), err) - } - } -} diff --git a/msgp/msgp/floatbench_test.go b/msgp/msgp/floatbench_test.go deleted file mode 100644 index 575b081bb5..0000000000 --- a/msgp/msgp/floatbench_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package msgp - -import ( - "testing" -) - -func BenchmarkReadWriteFloat32(b *testing.B) { - var f float32 = 3.9081 - bts := AppendFloat32([]byte{}, f) - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts = AppendFloat32(bts[0:0], f) - f, bts, _ = ReadFloat32Bytes(bts) - } -} - -func BenchmarkReadWriteFloat64(b *testing.B) { - var f float64 = 3.9081 - bts := AppendFloat64([]byte{}, f) - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts = AppendFloat64(bts[0:0], f) - f, bts, _ = ReadFloat64Bytes(bts) - } -} diff --git a/msgp/msgp/int.go b/msgp/msgp/int.go deleted file mode 100644 index 75bfa1500d..0000000000 --- a/msgp/msgp/int.go +++ /dev/null @@ -1,20 +0,0 @@ -package msgp - -// MaxInt is the maximum int, which might be int32 or int64 -const MaxInt = int((^uint(0)) >> 1) - -func u32int(x uint32) (int, error) { - if uint64(x) > uint64(MaxInt) { - return 0, ErrOverflow(uint64(x), uint64(MaxInt)) - } - - return int(x), nil -} - -func u64int(x uint64) (int, error) { - if x > uint64(MaxInt) { - return 0, ErrOverflow(x, uint64(MaxInt)) - } - - return int(x), nil -} diff --git a/msgp/msgp/integers.go b/msgp/msgp/integers.go deleted file mode 100644 index f817d77598..0000000000 --- a/msgp/msgp/integers.go +++ /dev/null @@ -1,174 +0,0 @@ -package msgp - -/* ---------------------------------- - integer encoding utilities - (inline-able) - - TODO(tinylib): there are faster, - albeit non-portable solutions - to the code below. implement - byteswap? - ---------------------------------- */ - -func putMint64(b []byte, i int64) { - b[0] = mint64 - b[1] = byte(i >> 56) - b[2] = byte(i >> 48) - b[3] = byte(i >> 40) - b[4] = byte(i >> 32) - b[5] = byte(i >> 24) - b[6] = byte(i >> 16) - b[7] = byte(i >> 8) - b[8] = byte(i) -} - -func getMint64(b []byte) int64 { - return (int64(b[1]) << 56) | (int64(b[2]) << 48) | - (int64(b[3]) << 40) | (int64(b[4]) << 32) | - (int64(b[5]) << 24) | (int64(b[6]) << 16) | - (int64(b[7]) << 8) | (int64(b[8])) -} - -func putMint32(b []byte, i int32) { - b[0] = mint32 - b[1] = byte(i >> 24) - b[2] = byte(i >> 16) - b[3] = byte(i >> 8) - b[4] = byte(i) -} - -func getMint32(b []byte) int32 { - return (int32(b[1]) << 24) | (int32(b[2]) << 16) | (int32(b[3]) << 8) | (int32(b[4])) -} - -func putMint16(b []byte, i int16) { - b[0] = mint16 - b[1] = byte(i >> 8) - b[2] = byte(i) -} - -func getMint16(b []byte) (i int16) { - return (int16(b[1]) << 8) | int16(b[2]) -} - -func putMint8(b []byte, i int8) { - b[0] = mint8 - b[1] = byte(i) -} - -func getMint8(b []byte) (i int8) { - return int8(b[1]) -} - -func putMuint64(b []byte, u uint64) { - b[0] = muint64 - b[1] = byte(u >> 56) - b[2] = byte(u >> 48) - b[3] = byte(u >> 40) - b[4] = byte(u >> 32) - b[5] = byte(u >> 24) - b[6] = byte(u >> 16) - b[7] = byte(u >> 8) - b[8] = byte(u) -} - -func getMuint64(b []byte) uint64 { - return (uint64(b[1]) << 56) | (uint64(b[2]) << 48) | - (uint64(b[3]) << 40) | (uint64(b[4]) << 32) | - (uint64(b[5]) << 24) | (uint64(b[6]) << 16) | - (uint64(b[7]) << 8) | (uint64(b[8])) -} - -func putMuint32(b []byte, u uint32) { - b[0] = muint32 - b[1] = byte(u >> 24) - b[2] = byte(u >> 16) - b[3] = byte(u >> 8) - b[4] = byte(u) -} - -func getMuint32(b []byte) uint32 { - return (uint32(b[1]) << 24) | (uint32(b[2]) << 16) | (uint32(b[3]) << 8) | (uint32(b[4])) -} - -func putMuint16(b []byte, u uint16) { - b[0] = muint16 - b[1] = byte(u >> 8) - b[2] = byte(u) -} - -func getMuint16(b []byte) uint16 { - return (uint16(b[1]) << 8) | uint16(b[2]) -} - -func putMuint8(b []byte, u uint8) { - b[0] = muint8 - b[1] = byte(u) -} - -func getMuint8(b []byte) uint8 { - return uint8(b[1]) -} - -func getUnix(b []byte) (sec int64, nsec int32) { - sec = (int64(b[0]) << 56) | (int64(b[1]) << 48) | - (int64(b[2]) << 40) | (int64(b[3]) << 32) | - (int64(b[4]) << 24) | (int64(b[5]) << 16) | - (int64(b[6]) << 8) | (int64(b[7])) - - nsec = (int32(b[8]) << 24) | (int32(b[9]) << 16) | (int32(b[10]) << 8) | (int32(b[11])) - return -} - -func putUnix(b []byte, sec int64, nsec int32) { - b[0] = byte(sec >> 56) - b[1] = byte(sec >> 48) - b[2] = byte(sec >> 40) - b[3] = byte(sec >> 32) - b[4] = byte(sec >> 24) - b[5] = byte(sec >> 16) - b[6] = byte(sec >> 8) - b[7] = byte(sec) - b[8] = byte(nsec >> 24) - b[9] = byte(nsec >> 16) - b[10] = byte(nsec >> 8) - b[11] = byte(nsec) -} - -/* ----------------------------- - prefix utilities - ----------------------------- */ - -// write prefix and uint8 -func prefixu8(b []byte, pre byte, sz uint8) { - b[0] = pre - b[1] = byte(sz) -} - -// write prefix and big-endian uint16 -func prefixu16(b []byte, pre byte, sz uint16) { - b[0] = pre - b[1] = byte(sz >> 8) - b[2] = byte(sz) -} - -// write prefix and big-endian uint32 -func prefixu32(b []byte, pre byte, sz uint32) { - b[0] = pre - b[1] = byte(sz >> 24) - b[2] = byte(sz >> 16) - b[3] = byte(sz >> 8) - b[4] = byte(sz) -} - -func prefixu64(b []byte, pre byte, sz uint64) { - b[0] = pre - b[1] = byte(sz >> 56) - b[2] = byte(sz >> 48) - b[3] = byte(sz >> 40) - b[4] = byte(sz >> 32) - b[5] = byte(sz >> 24) - b[6] = byte(sz >> 16) - b[7] = byte(sz >> 8) - b[8] = byte(sz) -} diff --git a/msgp/msgp/read.go b/msgp/msgp/read.go deleted file mode 100644 index f214e07451..0000000000 --- a/msgp/msgp/read.go +++ /dev/null @@ -1,91 +0,0 @@ -package msgp - -// Type is a MessagePack wire type, -// including this package's built-in -// extension types. -type Type byte - -// MessagePack Types -// -// The zero value of Type -// is InvalidType. -const ( - InvalidType Type = iota - - // MessagePack built-in types - - StrType - BinType - MapType - ArrayType - Float64Type - Float32Type - BoolType - IntType - UintType - NilType - ExtensionType - - // pseudo-types provided - // by extensions - - Complex64Type - Complex128Type - TimeType - - _maxtype -) - -// String implements fmt.Stringer -func (t Type) String() string { - switch t { - case StrType: - return "str" - case BinType: - return "bin" - case MapType: - return "map" - case ArrayType: - return "array" - case Float64Type: - return "float64" - case Float32Type: - return "float32" - case BoolType: - return "bool" - case UintType: - return "uint" - case IntType: - return "int" - case ExtensionType: - return "ext" - case NilType: - return "nil" - default: - return "" - } -} - -// Unmarshaler is the interface fulfilled -// by objects that know how to unmarshal -// themselves from MessagePack. -// UnmarshalMsg unmarshals the object -// from binary, returing any leftover -// bytes and any errors encountered. -// CanUnmarshalMsg checks that o is of the same type as -// was used to generate the UnmarshalMsg code; it can be -// used to guard against UnmarshalMsg() going to an embedded -// field in a struct rather than unmarshaling the entire struct. -type Unmarshaler interface { - UnmarshalMsg([]byte) ([]byte, error) - UnmarshalMsgWithState([]byte, UnmarshalState) ([]byte, error) - CanUnmarshalMsg(o interface{}) bool -} - -// UnmarshalState holds state while running UnmarshalMsg. -type UnmarshalState struct { - Depth uint64 -} - -// DefaultUnmarshalState defines the default state. -var DefaultUnmarshalState = UnmarshalState{Depth: 10000} diff --git a/msgp/msgp/read_bytes.go b/msgp/msgp/read_bytes.go deleted file mode 100644 index 0ade0fb1ca..0000000000 --- a/msgp/msgp/read_bytes.go +++ /dev/null @@ -1,1359 +0,0 @@ -package msgp - -import ( - "encoding/binary" - "math" - "time" -) - -var big = binary.BigEndian - -// NextType returns the type of the next -// object in the slice. If the length -// of the input is zero, it returns -// InvalidType. -func NextType(b []byte) Type { - if len(b) == 0 { - return InvalidType - } - spec := sizes[b[0]] - t := spec.typ - if t == ExtensionType && len(b) > int(spec.size) { - var tp int8 - if spec.extra == constsize { - tp = int8(b[1]) - } else { - tp = int8(b[spec.size-1]) - } - switch tp { - case TimeExtension: - return TimeType - case Complex128Extension: - return Complex128Type - case Complex64Extension: - return Complex64Type - default: - return ExtensionType - } - } - return t -} - -// IsNil returns true if len(b)>0 and -// the leading byte is a 'nil' MessagePack -// byte; false otherwise -func IsNil(b []byte) bool { - if len(b) != 0 && b[0] == mnil { - return true - } - return false -} - -// Raw is raw MessagePack. -// Raw allows you to read and write -// data without interpreting its contents. -type Raw []byte - -// CanMarshalMsg returns true if the z interface is a Raw object ( part of the Marshaler interface ) -func (Raw) CanMarshalMsg(z interface{}) bool { - _, ok := (z).(Raw) - if !ok { - _, ok = (z).(*Raw) - } - return ok -} - -// MarshalMsg implements msgp.Marshaler. -// It appends the raw contents of 'raw' -// to the provided byte slice. If 'raw' -// is 0 bytes, 'nil' will be appended instead. -func (r Raw) MarshalMsg(b []byte) []byte { - i := len(r) - if i == 0 { - return AppendNil(b) - } - o, l := ensure(b, i) - copy(o[l:], []byte(r)) - return o -} - -// CanUnmarshalMsg returns true if the z interface is a Raw object ( part of the Unmarshaler interface ) -func (*Raw) CanUnmarshalMsg(z interface{}) bool { - _, ok := (z).(*Raw) - return ok -} - -// UnmarshalMsg implements msgp.Unmarshaler. -// It sets the contents of *Raw to be the next -// object in the provided byte slice. -func (r *Raw) UnmarshalMsg(b []byte) ([]byte, error) { - return r.UnmarshalMsgWithState(b, DefaultUnmarshalState) -} - -// UnmarshalMsg implements msgp.Unmarshaler. -// It sets the contents of *Raw to be the next -// object in the provided byte slice. -func (r *Raw) UnmarshalMsgWithState(b []byte, st UnmarshalState) ([]byte, error) { - if st.Depth == 0 { - return nil, ErrMaxDepthExceeded{} - } - l := len(b) - out, err := Skip(b) - if err != nil { - return b, err - } - rlen := l - len(out) - if IsNil(b[:rlen]) { - rlen = 0 - } - if cap(*r) < rlen { - *r = make(Raw, rlen) - } else { - *r = (*r)[0:rlen] - } - copy(*r, b[:rlen]) - return out, nil -} - -// Msgsize implements msgp.Sizer -func (r Raw) Msgsize() int { - l := len(r) - if l == 0 { - return 1 // for 'nil' - } - return l -} - -// MsgIsZero returns whether this is a zero value -func (r *Raw) MsgIsZero() bool { - return len(*r) == 0 -} - -// ReadMapHeaderBytes reads a map header size -// from 'b' and returns the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a map) -func ReadMapHeaderBytes(b []byte) (sz int, isnil bool, o []byte, err error) { - l := len(b) - if l < 1 { - err = ErrShortBytes - return - } - - lead := b[0] - if isfixmap(lead) { - sz = int(rfixmap(lead)) - o = b[1:] - return - } - - switch lead { - // go-codec compatibility: mnil decodes as a nil map / empty struct - case mnil: - sz = 0 - o = b[1:] - isnil = true - return - - case mmap16: - if l < 3 { - err = ErrShortBytes - return - } - sz = int(big.Uint16(b[1:])) - o = b[3:] - return - - case mmap32: - if l < 5 { - err = ErrShortBytes - return - } - sz, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - o = b[5:] - return - - default: - o = b - err = badPrefix(MapType, lead) - return - } -} - -// ReadMapKeyZC attempts to read a map key -// from 'b' and returns the key bytes and the remaining bytes -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a str or bin) -func ReadMapKeyZC(b []byte) ([]byte, []byte, error) { - o, x, err := ReadStringZC(b) - if err != nil { - if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType { - return ReadBytesZC(b) - } - return nil, b, err - } - return o, x, nil -} - -// ReadArrayHeaderBytes attempts to read -// the array header size off of 'b' and return -// the size and remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not an array) -func ReadArrayHeaderBytes(b []byte) (sz int, isnil bool, o []byte, err error) { - return readArrayHeaderBytes(b, true) -} - -func readArrayHeaderBytes(b []byte, flattenMap bool) (sz int, isnil bool, o []byte, err error) { - if len(b) < 1 { - return 0, false, nil, ErrShortBytes - } - lead := b[0] - if isfixarray(lead) { - sz = int(rfixarray(lead)) - o = b[1:] - return - } - - // go-codec compat: map can be decoded as an array, by alternating - // the map keys and values in the decoded array. - if flattenMap && isfixmap(lead) { - sz = 2 * int(rfixmap(lead)) - o = b[1:] - return - } - - switch lead { - case mnil: - // go-codec compat: nil decodes as an empty array (nil for slice) - sz = 0 - o = b[1:] - isnil = true - return - - case marray16: - if len(b) < 3 { - err = ErrShortBytes - return - } - sz = int(big.Uint16(b[1:])) - o = b[3:] - return - - case marray32: - if len(b) < 5 { - err = ErrShortBytes - return - } - sz, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - o = b[5:] - return - - // go-codec compat: map can be decoded as an array, by alternating - // the map keys and values in the decoded array. - case mmap16: - if flattenMap { - if len(b) < 3 { - err = ErrShortBytes - return - } - sz = 2 * int(big.Uint16(b[1:])) - o = b[3:] - return - } - - case mmap32: - if flattenMap { - if len(b) < 5 { - err = ErrShortBytes - return - } - u64sz := 2 * uint64(big.Uint32(b[1:])) - sz, err = u64int(u64sz) - if err != nil { - return - } - o = b[5:] - return - } - } - - o = b - err = badPrefix(ArrayType, lead) - return -} - -// ReadNilBytes tries to read a "nil" byte -// off of 'b' and return the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'nil') -// - InvalidPrefixError -func ReadNilBytes(b []byte) ([]byte, error) { - if len(b) < 1 { - return nil, ErrShortBytes - } - if b[0] != mnil { - return b, badPrefix(NilType, b[0]) - } - return b[1:], nil -} - -// ReadFloat64Bytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float64) -func ReadFloat64Bytes(b []byte) (f float64, o []byte, err error) { - if len(b) < 9 { - if len(b) >= 5 && b[0] == mfloat32 { - var tf float32 - tf, o, err = ReadFloat32Bytes(b) - f = float64(tf) - return - } - if b[0] == mnil { - o = b[1:] - return - } - err = ErrShortBytes - return - } - - if b[0] != mfloat64 { - if b[0] == mfloat32 { - var tf float32 - tf, o, err = ReadFloat32Bytes(b) - f = float64(tf) - return - } - err = badPrefix(Float64Type, b[0]) - return - } - - f = math.Float64frombits(getMuint64(b)) - o = b[9:] - return -} - -// ReadFloat32Bytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float32) -func ReadFloat32Bytes(b []byte) (f float32, o []byte, err error) { - if len(b) < 1 { - err = ErrShortBytes - return - } - - if b[0] == mnil { - o = b[1:] - return - } - - if b[0] != mfloat32 { - err = TypeError{Method: Float32Type, Encoded: getType(b[0])} - return - } - - if len(b) < 5 { - err = ErrShortBytes - return - } - - f = math.Float32frombits(getMuint32(b)) - o = b[5:] - return -} - -// ReadBoolBytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a bool) -func ReadBoolBytes(b []byte) (bool, []byte, error) { - if len(b) < 1 { - return false, b, ErrShortBytes - } - switch b[0] { - case mtrue: - return true, b[1:], nil - case mfalse: - return false, b[1:], nil - case mnil: - return false, b[1:], nil - default: - return false, b, badPrefix(BoolType, b[0]) - } -} - -// ReadDurationBytes tries to read a time.Duration -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError (not a int) -func ReadDurationBytes(b []byte) (d time.Duration, o []byte, err error) { - i, o, err := ReadInt64Bytes(b) - return time.Duration(i), o, err -} - -// ReadInt64Bytes tries to read an int64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError (not a int) -func ReadInt64Bytes(b []byte) (i int64, o []byte, err error) { - l := len(b) - if l < 1 { - return 0, nil, ErrShortBytes - } - - lead := b[0] - if isfixint(lead) { - i = int64(rfixint(lead)) - o = b[1:] - return - } - if isnfixint(lead) { - i = int64(rnfixint(lead)) - o = b[1:] - return - } - - switch lead { - case mnil: - i = 0 - o = b[1:] - return - - case mint8: - if l < 2 { - err = ErrShortBytes - return - } - i = int64(getMint8(b)) - o = b[2:] - return - - case muint8: - if l < 2 { - err = ErrShortBytes - return - } - i = int64(getMuint8(b)) - o = b[2:] - return - - case mint16: - if l < 3 { - err = ErrShortBytes - return - } - i = int64(getMint16(b)) - o = b[3:] - return - - case muint16: - if l < 3 { - err = ErrShortBytes - return - } - i = int64(getMuint16(b)) - o = b[3:] - return - - case mint32: - if l < 5 { - err = ErrShortBytes - return - } - i = int64(getMint32(b)) - o = b[5:] - return - - case muint32: - if l < 5 { - err = ErrShortBytes - return - } - i = int64(getMuint32(b)) - o = b[5:] - return - - case mint64: - if l < 9 { - err = ErrShortBytes - return - } - i = int64(getMint64(b)) - o = b[9:] - return - - case muint64: - if l < 9 { - err = ErrShortBytes - return - } - u := getMuint64(b) - // go-codec compat: uint64 encodings that exceed MaxInt64 - // just overflow when parsed as int64. - // - // if u > math.MaxInt64 { - // err = UintOverflow{Value: u, FailedBitsize: 64} - // return - // } - i = int64(u) - o = b[9:] - return - - default: - err = badPrefix(IntType, lead) - return - } -} - -// ReadInt32Bytes tries to read an int32 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int32) -func ReadInt32Bytes(b []byte) (int32, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt32 || i < math.MinInt32 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 32} - } - return int32(i), o, err -} - -// ReadInt16Bytes tries to read an int16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int16) -func ReadInt16Bytes(b []byte) (int16, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt16 || i < math.MinInt16 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 16} - } - return int16(i), o, err -} - -// ReadInt8Bytes tries to read an int16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int8) -func ReadInt8Bytes(b []byte) (int8, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt8 || i < math.MinInt8 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 8} - } - return int8(i), o, err -} - -// ReadUint64Bytes tries to read a uint64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -func ReadUint64Bytes(b []byte) (u uint64, o []byte, err error) { - l := len(b) - if l < 1 { - return 0, nil, ErrShortBytes - } - - lead := b[0] - if isfixint(lead) { - u = uint64(rfixint(lead)) - o = b[1:] - return - } - - switch lead { - case mnil: - u = 0 - o = b[1:] - return - - case mint8: - if l < 2 { - err = ErrShortBytes - return - } - v := int64(getMint8(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[2:] - return - - case muint8: - if l < 2 { - err = ErrShortBytes - return - } - u = uint64(getMuint8(b)) - o = b[2:] - return - - case mint16: - if l < 3 { - err = ErrShortBytes - return - } - v := int64(getMint16(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[3:] - return - - case muint16: - if l < 3 { - err = ErrShortBytes - return - } - u = uint64(getMuint16(b)) - o = b[3:] - return - - case mint32: - if l < 5 { - err = ErrShortBytes - return - } - v := int64(getMint32(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[5:] - return - - case muint32: - if l < 5 { - err = ErrShortBytes - return - } - u = uint64(getMuint32(b)) - o = b[5:] - return - - case mint64: - if l < 9 { - err = ErrShortBytes - return - } - v := int64(getMint64(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[9:] - return - - case muint64: - if l < 9 { - err = ErrShortBytes - return - } - u = getMuint64(b) - o = b[9:] - return - - default: - if isnfixint(lead) { - err = UintBelowZero{Value: int64(rnfixint(lead))} - } else { - err = badPrefix(UintType, lead) - } - return - } -} - -// ReadUint32Bytes tries to read a uint32 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint32) -func ReadUint32Bytes(b []byte) (uint32, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint32 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 32} - } - return uint32(v), o, err -} - -// ReadUint16Bytes tries to read a uint16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint16) -func ReadUint16Bytes(b []byte) (uint16, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint16 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 16} - } - return uint16(v), o, err -} - -// ReadUint8Bytes tries to read a uint8 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint8) -func ReadUint8Bytes(b []byte) (uint8, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint8 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 8} - } - return uint8(v), o, err -} - -// ReadByteBytes is analogous to ReadUint8Bytes -func ReadByteBytes(b []byte) (byte, []byte, error) { - return ReadUint8Bytes(b) -} - -// ReadBytesBytes reads a 'bin' object -// from 'b' and returns its value and -// the remaining bytes in 'b'. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'bin' object) -func ReadBytesBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { - return readBytesBytes(b, scratch, false, true) -} - -func readBytesBytesSlow(b []byte, flattenMap bool) (v []byte, o []byte, err error) { - var count int - count, _, o, err = readArrayHeaderBytes(b, flattenMap) - if err != nil { - return - } - - if len(o) < count { - err = ErrShortBytes - return - } - - v = make([]byte, count) - for idx := range v { - v[idx], o, err = ReadByteBytes(o) - if err != nil { - return - } - } - - return -} - -// ReadBytesBytesHeader reads the header of a 'bin' object -// from 'b' and return it's length, in bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'bin' object) -func ReadBytesBytesHeader(b []byte) (sz int, err error) { - l := len(b) - if l < 1 { - return 0, ErrShortBytes - } - - lead := b[0] - - // go-codec compat: decode string encodings into byte arrays - - if isfixstr(lead) { - sz = int(rfixstr(lead)) - return - } - - switch lead { - case mstr8: - if l < 2 { - err = ErrShortBytes - return - } - sz = int(b[1]) - return - - case mstr16: - if l < 3 { - err = ErrShortBytes - return - } - sz = int(big.Uint16(b[1:])) - return - - case mstr32: - if l < 5 { - err = ErrShortBytes - return - } - sz, err = u32int(big.Uint32(b[1:])) - return - - case mnil: - sz = 0 - return - - case mbin8: - if l < 2 { - err = ErrShortBytes - return - } - sz = int(b[1]) - return - - case mbin16: - if l < 3 { - err = ErrShortBytes - return - } - sz = int(big.Uint16(b[1:])) - return - - case mbin32: - if l < 5 { - err = ErrShortBytes - return - } - sz, err = u32int(big.Uint32(b[1:])) - return - - default: - sz, _, _, err = readArrayHeaderBytes(b, true) - return - } -} - -func readBytesBytes(b []byte, scratch []byte, zc bool, flattenMap bool) (v []byte, o []byte, err error) { - l := len(b) - if l < 1 { - return nil, nil, ErrShortBytes - } - - lead := b[0] - var read int - - // go-codec compat: decode string encodings into byte arrays - - if isfixstr(lead) { - read = int(rfixstr(lead)) - b = b[1:] - } else { - switch lead { - case mstr8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - b = b[2:] - - case mstr16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - b = b[3:] - - case mstr32: - if l < 5 { - err = ErrShortBytes - return - } - read, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - b = b[5:] - - case mnil: - v = nil - o = b[1:] - return - - case mbin8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - b = b[2:] - - case mbin16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - b = b[3:] - - case mbin32: - if l < 5 { - err = ErrShortBytes - return - } - read, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - b = b[5:] - - default: - // go-codec compat: decode into byte array/slice from - // explicit array encodings (including the weird case - // of decoding a map as a key-value interleaved array). - v, o, err = readBytesBytesSlow(b, flattenMap) - if err != nil { - // If that doesn't work, return the original error code. - err = badPrefix(BinType, lead) - } - return - } - } - - if len(b) < read { - err = ErrShortBytes - return - } - - // zero-copy - if zc { - v = b[0:read] - o = b[read:] - return - } - - // The "scratch != nil" check is to match go-codec behavior: - // decode zero-length byte slices as a non-nil byte slice. - if scratch != nil && cap(scratch) >= read { - v = scratch[0:read] - } else { - v = make([]byte, read) - } - - o = b[copy(v, b):] - return -} - -// ReadBytesZC extracts the messagepack-encoded -// binary field without copying. The returned []byte -// points to the same memory as the input slice. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'bin') -func ReadBytesZC(b []byte) (v []byte, o []byte, err error) { - return readBytesBytes(b, nil, true, true) -} - -func readExactBytesSlow(b []byte, into []byte) (o []byte, err error) { - var count int - count, _, o, err = ReadArrayHeaderBytes(b) - if err != nil { - return - } - - if count > len(into) { - err = ArrayError{Wanted: len(into), Got: count} - return - } - - for idx := 0; idx < count; idx++ { - into[idx], o, err = ReadByteBytes(o) - if err != nil { - return - } - } - - return -} - -func ReadExactBytes(b []byte, into []byte) (o []byte, err error) { - l := len(b) - if l < 1 { - err = ErrShortBytes - return - } - - lead := b[0] - var read int - var skip int - - // go-codec compat: decode string encodings into byte arrays - - if isfixstr(lead) { - read = int(rfixstr(lead)) - skip = 1 - } else { - switch lead { - case mstr8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - skip = 2 - - case mstr16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - skip = 3 - - case mstr32: - if l < 5 { - err = ErrShortBytes - return - } - read, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - skip = 5 - - case mnil: - // go-codec compat: decoding nil into an array clears the array; - // different from decoding a zero-length array (which updates - // in-place). - for i := range into { - into[i] = 0 - } - read = 0 - skip = 1 - - case mbin8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - skip = 2 - - case mbin16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - skip = 3 - - case mbin32: - if l < 5 { - err = ErrShortBytes - return - } - read, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - skip = 5 - - default: - // go-codec compat: decode into byte array from - // explicit array encodings (including the weird case - // of decoding a map as a key-value interleaved array). - o, err = readExactBytesSlow(b, into) - if err != nil { - // If that doesn't work, return the original error code. - err = badPrefix(BinType, lead) - } - return - } - } - - // go-codec compat: allow decoding a different number of bytes than the - // size of the fixed array; take the min of the size of the Go type and - // the encoded array size. - // - // if read != len(into) { - // err = ArrayError{Wanted: uint32(len(into)), Got: uint32(read)} - // return - // } - - if read > len(b[skip:]) { - err = ErrShortBytes - return - } - - copy(into, b[skip:skip+read]) - o = b[skip+read:] - return -} - -// ReadStringZC reads a messagepack string field -// without copying. The returned []byte points -// to the same memory as the input slice. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'str') -func ReadStringZC(b []byte) (v []byte, o []byte, err error) { - l := len(b) - if l < 1 { - return nil, nil, ErrShortBytes - } - - lead := b[0] - var read int - - if isfixstr(lead) { - read = int(rfixstr(lead)) - b = b[1:] - } else { - switch lead { - case mnil: - read = 0 - b = b[1:] - - case mstr8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - b = b[2:] - - case mstr16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - b = b[3:] - - case mstr32: - if l < 5 { - err = ErrShortBytes - return - } - read, err = u32int(big.Uint32(b[1:])) - if err != nil { - return - } - b = b[5:] - - default: - // go-codec compat: decode bin types into string - v, o, err = readBytesBytes(b, nil, true, false) - if err != nil { - // If the fallback fails, return original error code - err = TypeError{Method: StrType, Encoded: getType(lead)} - } - return - } - } - - if len(b) < read { - err = ErrShortBytes - return - } - - v = b[0:read] - o = b[read:] - return -} - -// ReadStringBytes reads a 'str' object -// from 'b' and returns its value and the -// remaining bytes in 'b'. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError -func ReadStringBytes(b []byte) (string, []byte, error) { - v, o, err := ReadStringZC(b) - return string(v), o, err -} - -// ReadStringAsBytes reads a 'str' object -// into a slice of bytes. 'v' is the value of -// the 'str' object, which may reside in memory -// pointed to by 'scratch.' 'o' is the remaining bytes -// in 'b'. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError (unknown type marker) -func ReadStringAsBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { - var tmp []byte - tmp, o, err = ReadStringZC(b) - v = append(scratch[:0], tmp...) - return -} - -// ReadComplex128Bytes reads a complex128 -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex128) -// - InvalidPrefixError -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex128) -func ReadComplex128Bytes(b []byte) (c complex128, o []byte, err error) { - if len(b) < 18 { - err = ErrShortBytes - return - } - if b[0] != mfixext16 { - err = badPrefix(Complex128Type, b[0]) - return - } - if int8(b[1]) != Complex128Extension { - err = errExt(int8(b[1]), Complex128Extension) - return - } - c = complex(math.Float64frombits(big.Uint64(b[2:])), - math.Float64frombits(big.Uint64(b[10:]))) - o = b[18:] - return -} - -// ReadComplex64Bytes reads a complex64 -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex64) -func ReadComplex64Bytes(b []byte) (c complex64, o []byte, err error) { - if len(b) < 10 { - err = ErrShortBytes - return - } - if b[0] != mfixext8 { - err = badPrefix(Complex64Type, b[0]) - return - } - if b[1] != Complex64Extension { - err = errExt(int8(b[1]), Complex64Extension) - return - } - c = complex(math.Float32frombits(big.Uint32(b[2:])), - math.Float32frombits(big.Uint32(b[6:]))) - o = b[10:] - return -} - -// ReadTimeBytes reads a time.Time -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a time.Time) -func ReadTimeBytes(b []byte) (t time.Time, o []byte, err error) { - if len(b) < 1 { - err = ErrShortBytes - return - } - if b[0] == mnil { - o = b[1:] - return - } - if len(b) < 15 { - err = ErrShortBytes - return - } - if b[0] != mext8 || b[1] != 12 { - err = badPrefix(TimeType, b[0]) - return - } - if int8(b[2]) != TimeExtension { - err = errExt(int8(b[2]), TimeExtension) - return - } - sec, nsec := getUnix(b[3:]) - t = time.Unix(sec, int64(nsec)).Local() - o = b[15:] - return -} - -// Skip skips the next object in 'b' and -// returns the remaining bytes. If the object -// is a map or array, all of its elements -// will be skipped. -// Possible Errors: -// - ErrShortBytes (not enough bytes in b) -// - InvalidPrefixError (bad encoding) -func Skip(b []byte) ([]byte, error) { - sz, asz, err := getSize(b) - if err != nil { - return b, err - } - if uintptr(len(b)) < sz { - return b, ErrShortBytes - } - b = b[sz:] - for asz > 0 { - b, err = Skip(b) - if err != nil { - return b, err - } - asz-- - } - return b, nil -} - -// returns (skip N bytes, skip M objects, error) -func getSize(b []byte) (uintptr, uintptr, error) { - l := len(b) - if l == 0 { - return 0, 0, ErrShortBytes - } - lead := b[0] - spec := &sizes[lead] // get type information - size, mode := spec.size, spec.extra - if size == 0 { - return 0, 0, InvalidPrefixError(lead) - } - if mode >= 0 { // fixed composites - return uintptr(size), uintptr(mode), nil - } - if l < int(size) { - return 0, 0, ErrShortBytes - } - switch mode { - case extra8: - return uintptr(size) + uintptr(b[1]), 0, nil - case extra16: - return uintptr(size) + uintptr(big.Uint16(b[1:])), 0, nil - case extra32: - return uintptr(size) + uintptr(big.Uint32(b[1:])), 0, nil - case map16v: - return uintptr(size), 2 * uintptr(big.Uint16(b[1:])), nil - case map32v: - return uintptr(size), 2 * uintptr(big.Uint32(b[1:])), nil - case array16v: - return uintptr(size), uintptr(big.Uint16(b[1:])), nil - case array32v: - return uintptr(size), uintptr(big.Uint32(b[1:])), nil - default: - return 0, 0, fatal - } -} diff --git a/msgp/msgp/read_bytes_test.go b/msgp/msgp/read_bytes_test.go deleted file mode 100644 index 73a4a16b02..0000000000 --- a/msgp/msgp/read_bytes_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package msgp - -import ( - "testing" - "time" -) - -func BenchmarkReadMapHeaderBytes(b *testing.B) { - sizes := []uint32{1, 100, tuint16, tuint32} - buf := make([]byte, 0, 5*len(sizes)) - for _, sz := range sizes { - buf = AppendMapHeader(buf, sz) - } - b.SetBytes(int64(len(buf) / len(sizes))) - b.ReportAllocs() - b.ResetTimer() - o := buf - for i := 0; i < b.N; i++ { - _, _, buf, _ = ReadMapHeaderBytes(buf) - if len(buf) == 0 { - buf = o - } - } -} - -func BenchmarkReadArrayHeaderBytes(b *testing.B) { - sizes := []uint32{1, 100, tuint16, tuint32} - buf := make([]byte, 0, 5*len(sizes)) - for _, sz := range sizes { - buf = AppendArrayHeader(buf, sz) - } - b.SetBytes(int64(len(buf) / len(sizes))) - b.ReportAllocs() - b.ResetTimer() - o := buf - for i := 0; i < b.N; i++ { - _, _, buf, _ = ReadArrayHeaderBytes(buf) - if len(buf) == 0 { - buf = o - } - } -} - -func BenchmarkReadNilByte(b *testing.B) { - buf := []byte{mnil} - b.SetBytes(1) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - ReadNilBytes(buf) - } -} - -func BenchmarkReadFloat64Bytes(b *testing.B) { - f := float64(3.14159) - buf := make([]byte, 0, 9) - buf = AppendFloat64(buf, f) - b.SetBytes(int64(len(buf))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - ReadFloat64Bytes(buf) - } -} - -func BenchmarkReadFloat32Bytes(b *testing.B) { - f := float32(3.14159) - buf := make([]byte, 0, 5) - buf = AppendFloat32(buf, f) - b.SetBytes(int64(len(buf))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - ReadFloat32Bytes(buf) - } -} - -func BenchmarkReadBoolBytes(b *testing.B) { - buf := []byte{mtrue, mfalse, mtrue, mfalse} - b.SetBytes(1) - b.ReportAllocs() - b.ResetTimer() - o := buf - for i := 0; i < b.N; i++ { - _, buf, _ = ReadBoolBytes(buf) - if len(buf) == 0 { - buf = o - } - } -} - -func BenchmarkReadTimeBytes(b *testing.B) { - data := AppendTime(nil, time.Now()) - b.SetBytes(15) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - ReadTimeBytes(data) - } -} diff --git a/msgp/msgp/size.go b/msgp/msgp/size.go deleted file mode 100644 index e3a613b248..0000000000 --- a/msgp/msgp/size.go +++ /dev/null @@ -1,39 +0,0 @@ -package msgp - -// The sizes provided -// are the worst-case -// encoded sizes for -// each type. For variable- -// length types ([]byte, string), -// the total encoded size is -// the prefix size plus the -// length of the object. -const ( - Int64Size = 9 - IntSize = Int64Size - UintSize = Int64Size - Int8Size = 2 - Int16Size = 3 - Int32Size = 5 - Uint8Size = 2 - ByteSize = Uint8Size - Uint16Size = 3 - Uint32Size = 5 - Uint64Size = Int64Size - Float64Size = 9 - Float32Size = 5 - Complex64Size = 10 - Complex128Size = 18 - - DurationSize = Int64Size - TimeSize = 15 - BoolSize = 1 - NilSize = 1 - - MapHeaderSize = 5 - ArrayHeaderSize = 5 - - BytesPrefixSize = 5 - StringPrefixSize = 5 - ExtensionPrefixSize = 6 -) diff --git a/msgp/msgp/write.go b/msgp/msgp/write.go deleted file mode 100644 index da23cf7010..0000000000 --- a/msgp/msgp/write.go +++ /dev/null @@ -1,65 +0,0 @@ -package msgp - -// Sizer is an interface implemented -// by types that can estimate their -// size when MessagePack encoded. -// This interface is optional, but -// encoding/marshaling implementations -// may use this as a way to pre-allocate -// memory for serialization. -type Sizer interface { - Msgsize() int -} - -// MaxSizer is an interface implemented -// by types that can determine their max -// when implemented. -// This interface is optional, but -// implementations may use this as a way to limit -// number of bytes read during deserialization -type MaxSizer interface { - MaxSize() int -} - -// Require ensures that cap(old)-len(old) >= extra. -// It might be that this is impossible because len(old)+extra -// overflows int. If so, Require will not grow the slice, -// but at this point, we have run out of memory, and panic -// (from subsequent out-of-bounds access) is as good of an -// outcome as any. -func Require(old []byte, extra int) []byte { - l := len(old) - c := cap(old) - r := l + extra - if c >= r { - return old - } else if l == 0 { - return make([]byte, 0, extra) - } - // the new size is the greater - // of double the old capacity - // and the sum of the old length - // and the number of new bytes - // necessary. - c <<= 1 - if c < r { - c = r - } - n := make([]byte, l, c) - copy(n, old) - return n -} - -// Marshaler is the interface implemented -// by types that know how to marshal themselves -// as MessagePack. MarshalMsg appends the marshalled -// form of the object to the provided -// byte slice, returning the extended slice. -// CanMarshalMsg checks that o is of the same type as -// was used to generate the MarshalMsg code; it can be -// used to guard against MarshalMsg() going to an embedded -// field in a struct rather than marshaling the entire struct. -type Marshaler interface { - MarshalMsg([]byte) []byte - CanMarshalMsg(o interface{}) bool -} diff --git a/msgp/msgp/write_bytes.go b/msgp/msgp/write_bytes.go deleted file mode 100644 index b96715ccf3..0000000000 --- a/msgp/msgp/write_bytes.go +++ /dev/null @@ -1,288 +0,0 @@ -package msgp - -import ( - "math" - "time" -) - -// ensure 'sz' extra bytes in 'b' btw len(b) and cap(b) -// If the growth length overflows, we are anyway running -// out of memory, so panic (on a subsequent out-of-bounds -// slice reference) seems like as good of a result as any. -func ensure(b []byte, sz int) ([]byte, int) { - l := len(b) - c := cap(b) - if c-l < sz { - o := make([]byte, (2*c)+sz) // exponential growth - n := copy(o, b) - return o[:n+sz], n - } - return b[:l+sz], l -} - -// AppendMapHeader appends a map header with the -// given size to the slice -func AppendMapHeader(b []byte, sz uint32) []byte { - switch { - case sz <= 15: - return append(b, wfixmap(uint8(sz))) - - case sz <= math.MaxUint16: - o, n := ensure(b, 3) - prefixu16(o[n:], mmap16, uint16(sz)) - return o - - default: - o, n := ensure(b, 5) - prefixu32(o[n:], mmap32, sz) - return o - } -} - -// AppendArrayHeader appends an array header with -// the given size to the slice -func AppendArrayHeader(b []byte, sz uint32) []byte { - switch { - case sz <= 15: - return append(b, wfixarray(uint8(sz))) - - case sz <= math.MaxUint16: - o, n := ensure(b, 3) - prefixu16(o[n:], marray16, uint16(sz)) - return o - - default: - o, n := ensure(b, 5) - prefixu32(o[n:], marray32, sz) - return o - } -} - -// AppendNil appends a 'nil' byte to the slice -func AppendNil(b []byte) []byte { return append(b, mnil) } - -// AppendFloat64 appends a float64 to the slice -func AppendFloat64(b []byte, f float64) []byte { - o, n := ensure(b, Float64Size) - prefixu64(o[n:], mfloat64, math.Float64bits(f)) - return o -} - -// AppendFloat32 appends a float32 to the slice -func AppendFloat32(b []byte, f float32) []byte { - o, n := ensure(b, Float32Size) - prefixu32(o[n:], mfloat32, math.Float32bits(f)) - return o -} - -// AppendDuration appends a time.Duration to the slice -func AppendDuration(b []byte, d time.Duration) []byte { - return AppendInt64(b, int64(d)) -} - -// AppendInt64 appends an int64 to the slice -func AppendInt64(b []byte, i int64) []byte { - if i >= 0 { - return AppendUint64(b, uint64(i)) - } - switch { - case i >= -32: - return append(b, wnfixint(int8(i))) - case i >= math.MinInt8: - o, n := ensure(b, 2) - putMint8(o[n:], int8(i)) - return o - case i >= math.MinInt16: - o, n := ensure(b, 3) - putMint16(o[n:], int16(i)) - return o - case i >= math.MinInt32: - o, n := ensure(b, 5) - putMint32(o[n:], int32(i)) - return o - default: - o, n := ensure(b, 9) - putMint64(o[n:], i) - return o - } -} - -// AppendInt8 appends an int8 to the slice -func AppendInt8(b []byte, i int8) []byte { return AppendInt64(b, int64(i)) } - -// AppendInt16 appends an int16 to the slice -func AppendInt16(b []byte, i int16) []byte { return AppendInt64(b, int64(i)) } - -// AppendInt32 appends an int32 to the slice -func AppendInt32(b []byte, i int32) []byte { return AppendInt64(b, int64(i)) } - -// AppendUint64 appends a uint64 to the slice -func AppendUint64(b []byte, u uint64) []byte { - switch { - case u <= (1<<7)-1: - return append(b, wfixint(uint8(u))) - - case u <= math.MaxUint8: - o, n := ensure(b, 2) - putMuint8(o[n:], uint8(u)) - return o - - case u <= math.MaxUint16: - o, n := ensure(b, 3) - putMuint16(o[n:], uint16(u)) - return o - - case u <= math.MaxUint32: - o, n := ensure(b, 5) - putMuint32(o[n:], uint32(u)) - return o - - default: - o, n := ensure(b, 9) - putMuint64(o[n:], u) - return o - - } -} - -// AppendUint8 appends a uint8 to the slice -func AppendUint8(b []byte, u uint8) []byte { return AppendUint64(b, uint64(u)) } - -// AppendByte is analogous to AppendUint8 -func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, uint8(u)) } - -// AppendUint16 appends a uint16 to the slice -func AppendUint16(b []byte, u uint16) []byte { return AppendUint64(b, uint64(u)) } - -// AppendUint32 appends a uint32 to the slice -func AppendUint32(b []byte, u uint32) []byte { return AppendUint64(b, uint64(u)) } - -// AppendBytes appends bytes to the slice as MessagePack 'bin' data -func AppendBytes(b []byte, bts []byte) []byte { - sz := len(bts) - var o []byte - var n int - switch { - case bts == nil: - o, n = ensure(b, 1) - o[n] = mnil - n += 1 - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mbin8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mbin16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mbin32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], bts)] -} - -// AppendBool appends a bool to the slice -func AppendBool(b []byte, t bool) []byte { - if t { - return append(b, mtrue) - } - return append(b, mfalse) -} - -// AppendString appends a string as a MessagePack 'str' to the slice -func AppendString(b []byte, s string) []byte { - sz := len(s) - var n int - var o []byte - switch { - case sz <= 31: - o, n = ensure(b, 1+sz) - o[n] = wfixstr(uint8(sz)) - n++ - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mstr8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mstr16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mstr32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], s)] -} - -// AppendStringFromBytes appends a []byte -// as a MessagePack 'str' to the slice 'b.' -func AppendStringFromBytes(b []byte, str []byte) []byte { - sz := len(str) - var n int - var o []byte - switch { - case sz <= 31: - o, n = ensure(b, 1+sz) - o[n] = wfixstr(uint8(sz)) - n++ - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mstr8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mstr16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mstr32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], str)] -} - -// AppendComplex64 appends a complex64 to the slice as a MessagePack extension -func AppendComplex64(b []byte, c complex64) []byte { - o, n := ensure(b, Complex64Size) - o[n] = mfixext8 - o[n+1] = Complex64Extension - big.PutUint32(o[n+2:], math.Float32bits(real(c))) - big.PutUint32(o[n+6:], math.Float32bits(imag(c))) - return o -} - -// AppendComplex128 appends a complex128 to the slice as a MessagePack extension -func AppendComplex128(b []byte, c complex128) []byte { - o, n := ensure(b, Complex128Size) - o[n] = mfixext16 - o[n+1] = Complex128Extension - big.PutUint64(o[n+2:], math.Float64bits(real(c))) - big.PutUint64(o[n+10:], math.Float64bits(imag(c))) - return o -} - -// AppendTime appends a time.Time to the slice as a MessagePack extension -func AppendTime(b []byte, t time.Time) []byte { - o, n := ensure(b, TimeSize) - t = t.UTC() - o[n] = mext8 - o[n+1] = 12 - o[n+2] = TimeExtension - putUnix(o[n+3:], t.Unix(), int32(t.Nanosecond())) - return o -} - -// AppendMapStrStr appends a map[string]string to the slice -// as a MessagePack map with 'str'-type keys and values -func AppendMapStrStr(b []byte, m map[string]string) []byte { - sz := uint32(len(m)) - b = AppendMapHeader(b, sz) - for key, val := range m { - b = AppendString(b, key) - b = AppendString(b, val) - } - return b -} diff --git a/msgp/msgp/write_bytes_test.go b/msgp/msgp/write_bytes_test.go deleted file mode 100644 index d7e88738cf..0000000000 --- a/msgp/msgp/write_bytes_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package msgp - -import ( - "testing" - "time" -) - -func BenchmarkAppendMapHeader(b *testing.B) { - buf := make([]byte, 0, 9) - N := b.N / 4 - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < N; i++ { - AppendMapHeader(buf[:0], 0) - AppendMapHeader(buf[:0], uint32(tint8)) - AppendMapHeader(buf[:0], tuint16) - AppendMapHeader(buf[:0], tuint32) - } -} - -func BenchmarkAppendArrayHeader(b *testing.B) { - buf := make([]byte, 0, 9) - N := b.N / 4 - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < N; i++ { - AppendArrayHeader(buf[:0], 0) - AppendArrayHeader(buf[:0], uint32(tint8)) - AppendArrayHeader(buf[:0], tuint16) - AppendArrayHeader(buf[:0], tuint32) - } -} - -func TestAppendNil(t *testing.T) { - var bts []byte - bts = AppendNil(bts[0:0]) - if bts[0] != mnil { - t.Fatal("bts[0] is not 'nil'") - } -} - -func BenchmarkAppendFloat64(b *testing.B) { - f := float64(3.14159) - buf := make([]byte, 0, 9) - b.SetBytes(9) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendFloat64(buf[0:0], f) - } -} - -func BenchmarkAppendFloat32(b *testing.B) { - f := float32(3.14159) - buf := make([]byte, 0, 5) - b.SetBytes(5) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendFloat32(buf[0:0], f) - } -} - -func BenchmarkAppendInt64(b *testing.B) { - is := []int64{0, 1, -5, -50, int64(tint16), int64(tint32), int64(tint64)} - l := len(is) - buf := make([]byte, 0, 9) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendInt64(buf[0:0], is[i%l]) - } -} - -func BenchmarkAppendUint64(b *testing.B) { - us := []uint64{0, 1, 15, uint64(tuint16), uint64(tuint32), tuint64} - buf := make([]byte, 0, 9) - b.ReportAllocs() - b.ResetTimer() - l := len(us) - for i := 0; i < b.N; i++ { - AppendUint64(buf[0:0], us[i%l]) - } -} - -func benchappendBytes(size uint32, b *testing.B) { - bts := RandBytes(int(size)) - buf := make([]byte, 0, len(bts)+5) - b.SetBytes(int64(len(bts) + 5)) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendBytes(buf[0:0], bts) - } -} - -func BenchmarkAppend16Bytes(b *testing.B) { benchappendBytes(16, b) } - -func BenchmarkAppend256Bytes(b *testing.B) { benchappendBytes(256, b) } - -func BenchmarkAppend2048Bytes(b *testing.B) { benchappendBytes(2048, b) } - -func benchappendString(size uint32, b *testing.B) { - str := string(RandBytes(int(size))) - buf := make([]byte, 0, len(str)+5) - b.SetBytes(int64(len(str) + 5)) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendString(buf[0:0], str) - } -} - -func BenchmarkAppend16String(b *testing.B) { benchappendString(16, b) } - -func BenchmarkAppend256String(b *testing.B) { benchappendString(256, b) } - -func BenchmarkAppend2048String(b *testing.B) { benchappendString(2048, b) } - -func BenchmarkAppendBool(b *testing.B) { - vs := []bool{true, false} - buf := make([]byte, 0, 1) - b.SetBytes(1) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendBool(buf[0:0], vs[i%2]) - } -} - -func BenchmarkAppendTime(b *testing.B) { - t := time.Now() - b.SetBytes(15) - buf := make([]byte, 0, 15) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - AppendTime(buf[0:0], t) - } -} diff --git a/msgp/msgp/write_test.go b/msgp/msgp/write_test.go deleted file mode 100644 index 25b65a091e..0000000000 --- a/msgp/msgp/write_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package msgp - -import ( - "math" - "math/rand" -) - -var ( - tint8 int8 = 126 // cannot be most fix* types - tint16 int16 = 150 // cannot be int8 - tint32 int32 = math.MaxInt16 + 100 // cannot be int16 - tint64 int64 = math.MaxInt32 + 100 // cannot be int32 - tuint16 uint32 = 300 // cannot be uint8 - tuint32 uint32 = math.MaxUint16 + 100 // cannot be uint16 - tuint64 uint64 = math.MaxUint32 + 100 // cannot be uint32 -) - -func RandBytes(sz int) []byte { - out := make([]byte, sz) - for i := range out { - out[i] = byte(rand.Int63n(math.MaxInt64) % 256) - } - return out -} diff --git a/msgp/parse/directives.go b/msgp/parse/directives.go deleted file mode 100644 index 75d00111f8..0000000000 --- a/msgp/parse/directives.go +++ /dev/null @@ -1,194 +0,0 @@ -package parse - -import ( - "errors" - "fmt" - "go/ast" - "strings" - - "github.com/algorand/msgp/gen" -) - -const linePrefix = "//msgp:" - -// func(args, fileset) -type directive func([]string, *FileSet) error - -// func(passName, args, printer) -type passDirective func(gen.Method, []string, *gen.Printer) error - -// map of all recognized directives -// -// to add a directive, define a func([]string, *FileSet) error -// and then add it to this list. -var directives = map[string]directive{ - "shim": applyShim, - "ignore": ignore, - "tuple": astuple, - "sort": sortintf, - "allocbound": allocbound, - // _postunmarshalcheck is used to add callbacks to the end of un-marshalling that are tied to a specific Element. - _postunmarshalcheck: postunmarshalcheck, -} - -const _postunmarshalcheck = "postunmarshalcheck" - -var errNotEnoughArguments = errors.New("postunmarshalcheck did not receive enough arguments. expected at least 3") - -//msgp:postunmarshalcheck {Type} {funcName} {funcName} ... -// the functions should have no params, and output zero. -func postunmarshalcheck(text []string, f *FileSet) error { - if len(text) < 3 { - return errNotEnoughArguments - } - // not error but doesn't do anything - if text[0] != _postunmarshalcheck { - return nil - } - text = text[1:] - - elemType := text[0] - elem, ok := f.Identities[elemType] - if !ok { - return errors.New(fmt.Sprintf("postunmarshalcheck error: type %v does not exist", elemType)) - } - for _, fName := range text[1:] { - elem.AddCallback(gen.Callback{ - Fname: fName, - CallbackType: gen.UnmarshalCallBack, - }) - } - return nil -} - -var passDirectives = map[string]passDirective{ - "ignore": passignore, -} - -func passignore(m gen.Method, text []string, p *gen.Printer) error { - pushstate(m.String()) - for _, a := range text { - p.ApplyDirective(m, gen.IgnoreTypename(a)) - infof("ignoring %s\n", a) - } - popstate() - return nil -} - -// find all comment lines that begin with //msgp: -func yieldComments(c []*ast.CommentGroup) []string { - var out []string - for _, cg := range c { - for _, line := range cg.List { - if strings.HasPrefix(line.Text, linePrefix) { - out = append(out, strings.TrimPrefix(line.Text, linePrefix)) - } - } - } - return out -} - -//msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode} -func applyShim(text []string, f *FileSet) error { - if len(text) < 4 || len(text) > 5 { - return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1) - } - - name := text[1] - be := gen.Ident("", strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base} - if name[0] == '*' { - name = name[1:] - be.Needsref(true) - } - be.Alias(name) - - usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method} - - methods := strings.Split(usestr, "/") - if len(methods) != 2 { - return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3]) - } - - be.ShimToBase = methods[0] - be.ShimFromBase = methods[1] - - if len(text) == 5 { - modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode} - switch modestr { - case "cast": - be.ShimMode = gen.Cast - case "convert": - be.ShimMode = gen.Convert - default: - return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr) - } - } - - infof("%s -> %s\n", name, be.Value.String()) - f.findShim(name, be) - - return nil -} - -//msgp:ignore {TypeA} {TypeB}... -func ignore(text []string, f *FileSet) error { - if len(text) < 2 { - return nil - } - for _, item := range text[1:] { - name := strings.TrimSpace(item) - if _, ok := f.Identities[name]; ok { - delete(f.Identities, name) - infof("ignoring %s\n", name) - } - } - return nil -} - -//msgp:tuple {TypeA} {TypeB}... -func astuple(text []string, f *FileSet) error { - if len(text) < 2 { - return nil - } - for _, item := range text[1:] { - name := strings.TrimSpace(item) - if el, ok := f.Identities[name]; ok { - if st, ok := el.(*gen.Struct); ok { - st.AsTuple = true - infoln(name) - } else { - warnf("%s: only structs can be tuples\n", name) - } - } - } - return nil -} - -//msgp:sort {Type} {SortInterface} -func sortintf(text []string, f *FileSet) error { - if len(text) != 3 { - return nil - } - sortType := strings.TrimSpace(text[1]) - sortIntf := strings.TrimSpace(text[2]) - gen.SetSortInterface(sortType, sortIntf) - infof("sorting %s using %s\n", sortType, sortIntf) - return nil -} - -//msgp:allocbound {Type} {Bound} -func allocbound(text []string, f *FileSet) error { - if len(text) != 3 { - return nil - } - allocBoundType := strings.TrimSpace(text[1]) - allocBound := strings.TrimSpace(text[2]) - t, ok := f.Identities[allocBoundType] - if !ok { - warnf("allocbound: cannot find type %s\n", allocBoundType) - } else { - t.SetAllocBound(allocBound) - infof("allocbound(%s): setting to %s\n", allocBoundType, allocBound) - } - return nil -} diff --git a/msgp/parse/directives_test.go b/msgp/parse/directives_test.go deleted file mode 100644 index e237290f1b..0000000000 --- a/msgp/parse/directives_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package parse - -import ( - "testing" - - "github.com/algorand/msgp/gen" -) - -const ( - testStructName = "TestStruct" - testFuncName = "callback" -) - -func TestPostunmarshalcheck(t *testing.T) { - st := gen.Struct{ - Fields: nil, - AsTuple: false, - } - - fl := FileSet{ - Identities: map[string]gen.Elem{testStructName: &st}, - Directives: []string{"postunmarshalcheck"}, // raw preprocessor directives - } - if err := postunmarshalcheck([]string{"postunmarshalcheck", testStructName, testFuncName}, &fl); err != nil { - t.Fatal() - } - if testFuncName != st.GetCallbacks()[0].GetName() { - t.Fatal() - } - if !st.GetCallbacks()[0].IsUnmarshallCallback() { - t.Fatal() - } -} - -func TestPostunmarshalcheckFailures(t *testing.T) { - - st := gen.Struct{ - Fields: nil, - AsTuple: false, - } - - fl := FileSet{ - Identities: map[string]gen.Elem{testStructName: &st}, - Directives: []string{"postunmarshalcheck"}, // raw preprocessor directives - } - if err := postunmarshalcheck([]string{"postunmarshalcheck", testFuncName}, &fl); err == nil { - t.Fatal() - } - - if err := postunmarshalcheck([]string{"postunmarshalcheck", "non-existing-type", testFuncName}, &fl); err == nil { - t.Fatal() - } -} diff --git a/msgp/parse/getast.go b/msgp/parse/getast.go deleted file mode 100644 index d0ff38091f..0000000000 --- a/msgp/parse/getast.go +++ /dev/null @@ -1,818 +0,0 @@ -package parse - -import ( - "fmt" - "go/ast" - "reflect" - "sort" - "strings" - - "github.com/algorand/msgp/gen" - "github.com/ttacon/chalk" - "golang.org/x/tools/go/packages" -) - -// A FileSet is the in-memory representation of a -// parsed file. -type FileSet struct { - Package string // package name - PkgPath string // package path - Specs map[string]ast.Expr // type specs in file - Aliases map[string]ast.Expr // type aliases in file - Interfaces map[string]ast.Expr // type interfaces in file - Consts map[string]ast.Expr // consts - Identities map[string]gen.Elem // processed from specs - Directives []string // raw preprocessor directives - Imports []*ast.ImportSpec // imports - ImportSet ImportSet - ImportName map[string]string -} - -// An ImportSet describes the FileSets for a group of imported packages -type ImportSet map[string]*FileSet - -// File parses a file at the relative path -// provided and produces a new *FileSet. -// If you pass in a path to a directory, the entire -// directory will be parsed. -// If unexport is false, only exported identifiers are included in the FileSet. -// If the resulting FileSet would be empty, an error is returned. -func File(name string, unexported bool, warnPkgMask string) (*FileSet, error) { - pushstate(name) - defer popstate() - - cfg := &packages.Config{ - Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedSyntax | packages.NeedFiles | packages.NeedExportsFile | packages.NeedTypesInfo, - } - - pkgs, err := packages.Load(cfg, name) - if err != nil { - return nil, err - } - - if len(pkgs) != 1 { - return nil, fmt.Errorf("multiple packages in directory: %s", name) - } - - var one *packages.Package - for _, nm := range pkgs { - one = nm - break - } - - imps := make(map[string]*FileSet) - - fs := packageToFileSet(one, imps, unexported) - for _, ifs := range imps { - ifs.process(warnPkgMask) - ifs.applyDirectives() - ifs.propInline() - } - fs.process(warnPkgMask) - fs.applyDirectives() - fs.propInline() - return fs, nil -} - -func packageToFileSet(p *packages.Package, imps map[string]*FileSet, unexported bool) *FileSet { - fs := &FileSet{ - Package: p.Name, - PkgPath: p.PkgPath, - Specs: make(map[string]ast.Expr), - Aliases: make(map[string]ast.Expr), - Interfaces: make(map[string]ast.Expr), - Consts: make(map[string]ast.Expr), - Identities: make(map[string]gen.Elem), - ImportSet: imps, - ImportName: make(map[string]string), - } - - for name, importpkg := range p.Imports { - _, ok := imps[name] - if ok { - continue - } - - imps[name] = packageToFileSet(importpkg, imps, unexported) - } - - for _, fl := range p.Syntax { - pushstate(fl.Name.Name) - fs.Directives = append(fs.Directives, yieldComments(fl.Comments)...) - if !unexported { - ast.FileExports(fl) - } - - for _, importspec := range fl.Imports { - pkgpath := importspec.Path.Value[1 : len(importspec.Path.Value)-1] - if pkgpath == "C" { - continue - } - - var importname string - if importspec.Name != nil { - importname = importspec.Name.Name - } else { - p, ok := imps[pkgpath] - if !ok { - fmt.Printf("missing import %s\n", pkgpath) - } else { - importname = p.Package - } - } - fs.ImportName[importname] = pkgpath - } - - fs.getTypeSpecs(fl) - popstate() - } - - return fs -} - -// applyDirectives applies all of the directives that -// are known to the parser. additional method-specific -// directives remain in f.Directives -func (f *FileSet) applyDirectives() { - newdirs := make([]string, 0, len(f.Directives)) - for _, d := range f.Directives { - chunks := strings.Split(d, " ") - if len(chunks) > 0 { - if fn, ok := directives[chunks[0]]; ok { - pushstate(chunks[0]) - err := fn(chunks, f) - if err != nil { - warnln(err.Error()) - } - popstate() - } else { - newdirs = append(newdirs, d) - } - } - } - f.Directives = newdirs -} - -// A linkset is a graph of unresolved -// identities. -// -// Since gen.Ident can only represent -// one level of type indirection (e.g. Foo -> uint8), -// type declarations like `type Foo Bar` -// aren't resolve-able until we've processed -// everything else. -// -// The goal of this dependency resolution -// is to distill the type declaration -// into just one level of indirection. -// In other words, if we have: -// -// type A uint64 -// type B A -// type C B -// type D C -// -// ... then we want to end up -// figuring out that D is just a uint64. -type linkset map[string]*gen.BaseElem - -func (f *FileSet) resolve(ls linkset) { - progress := true - for progress && len(ls) > 0 { - progress = false - for name, elem := range ls { - real, ok := f.Identities[elem.TypeName()] - if ok { - // copy the old type descriptor, - // alias it to the new value, - // and insert it into the resolved - // identities list - progress = true - nt := real.Copy() - nt.Alias(name) - f.Identities[name] = nt - delete(ls, name) - } - } - } - - // what's left can't be resolved - for name, elem := range ls { - // warnf("couldn't resolve type %s (%s)\n", name, elem.TypeName()) - nt := elem.Copy() - nt.Alias(name) - f.Identities[name] = nt - } -} - -// process takes the contents of f.Specs and -// uses them to populate f.Identities -func (f *FileSet) process(warnPkgMask string) { - if warnPkgMask != "" && !strings.HasPrefix(f.PkgPath, warnPkgMask) { - increasePrintLevel() - defer decreasePrintLevel() - } - deferred := make(linkset) -parse: - for name, def := range f.Specs { - pushstate(name) - - el := f.parseExpr("", def) - - if el == nil { - warnln("failed to parse") - popstate() - continue parse - } - // push unresolved identities into - // the graph of links and resolve after - // we've handled every possible named type. - if be, ok := el.(*gen.BaseElem); ok && be.Value == gen.IDENT { - deferred[name] = be - popstate() - continue parse - } - el.Alias(name) - f.Identities[name] = el - popstate() - } - - if len(deferred) > 0 { - f.resolve(deferred) - } -} - -func strToMethod(s string) gen.Method { - switch s { - case "test": - return gen.Test - case "size": - return gen.Size - case "marshal": - return gen.Marshal - case "unmarshal": - return gen.Unmarshal - case "maxsize": - return gen.MaxSize - default: - return 0 - } -} - -func (f *FileSet) applyDirs(p *gen.Printer) { - // apply directives of the form - // - // //msgp:encode ignore {{TypeName}} - // -loop: - for _, d := range f.Directives { - chunks := strings.Split(d, " ") - if len(chunks) > 1 { - for i := range chunks { - chunks[i] = strings.TrimSpace(chunks[i]) - } - m := strToMethod(chunks[0]) - if m == 0 { - warnf("unknown pass name: %q\n", chunks[0]) - continue loop - } - if fn, ok := passDirectives[chunks[1]]; ok { - pushstate(chunks[1]) - err := fn(m, chunks[2:], p) - if err != nil { - warnf("error applying directive: %s\n", err) - } - popstate() - } else { - warnf("unrecognized directive %q\n", chunks[1]) - } - } else { - warnf("empty directive: %q\n", d) - } - } -} - -func (f *FileSet) PrintTo(p *gen.Printer) error { - var msgs []string - - f.applyDirs(p) - names := make([]string, 0, len(f.Identities)) - for name := range f.Identities { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { - el := f.Identities[name] - el.SetVarname("z") - pushstate(el.TypeName()) - m, err := p.Print(el) - popstate() - if err != nil { - return err - } - msgs = append(msgs, m...) - } - for _, msg := range msgs { - warnln(msg) - } - if len(msgs) > 0 { - return fmt.Errorf("Errors encountered, exiting") - } - return nil -} - -// getTypeSpecs extracts all of the *ast.TypeSpecs in the file -// into fs.Identities, but does not set the actual element -func (fs *FileSet) getTypeSpecs(f *ast.File) { - - // collect all imports... - fs.Imports = append(fs.Imports, f.Imports...) - - // check all declarations... - for i := range f.Decls { - - // for GenDecls... - if g, ok := f.Decls[i].(*ast.GenDecl); ok { - - // and check the specs... - for _, s := range g.Specs { - - // for ast.TypeSpecs.... - switch s := s.(type) { - case *ast.TypeSpec: - switch s.Type.(type) { - - // this is the list of parse-able - // type specs - case *ast.StructType, - *ast.ArrayType, - *ast.StarExpr, - *ast.SelectorExpr, - *ast.MapType, - *ast.Ident: - - if strings.HasPrefix(s.Name.Name, "_Ctype_") || s.Name.Name == "_" { - continue - } - - if s.Assign == 0 { - fs.Specs[s.Name.Name] = s.Type - } else { - fs.Aliases[s.Name.Name] = s.Type - } - case *ast.InterfaceType: - fs.Interfaces[s.Name.Name] = s.Type - } - - case *ast.ValueSpec: - if len(s.Names) == 1 && len(s.Values) == 1 { - fs.Consts[s.Names[0].Name] = s.Values[0] - } - } - } - } - } -} - -func fieldName(f *ast.Field) string { - switch len(f.Names) { - case 0: - return stringify(f.Type) - case 1: - return f.Names[0].Name - default: - return f.Names[0].Name + " (and others)" - } -} - -func (fs *FileSet) parseFieldList(importPrefix string, fl *ast.FieldList) []gen.StructField { - if fl == nil || fl.NumFields() == 0 { - return nil - } - out := make([]gen.StructField, 0, fl.NumFields()) - for _, field := range fl.List { - pushstate(fieldName(field)) - fds := fs.getField(importPrefix, field) - if len(fds) > 0 { - out = append(out, fds...) - } else { - warnln("ignored.") - } - popstate() - } - return out -} - -// translate *ast.Field into []gen.StructField -func (fs *FileSet) getField(importPrefix string, f *ast.Field) []gen.StructField { - sf := make([]gen.StructField, 1) - var extension, flatten bool - var allocbound string - var allocbounds []string - var maxtotalbytes string - - // always flatten embedded structs - flatten = true - - // parse tag; otherwise field name is field tag - if f.Tag != nil { - var body string - body, sf[0].HasCodecTag = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Lookup("codec") - tags := strings.Split(body, ",") - for _, tag := range tags[1:] { - if tag == "extension" { - extension = true - } - if strings.HasPrefix(tag, "allocbound=") { - allocbounds = append(allocbounds, strings.Split(tag, "=")[1]) - } - if strings.HasPrefix(tag, "maxtotalbytes=") { - maxtotalbytes = strings.Split(tag, "=")[1] - } - } - // ignore "-" fields - if tags[0] == "-" { - return nil - } - sf[0].FieldTag = tags[0] - sf[0].FieldTagParts = tags - sf[0].RawTag = f.Tag.Value - } - allocbound = strings.Join(allocbounds, ",") - ex := fs.parseExpr(importPrefix, f.Type) - if ex == nil { - return nil - } - - // parse field name - switch len(f.Names) { - case 0: - if flatten { - maybe := fs.getFieldsFromEmbeddedStruct(importPrefix, f.Type) - if maybe != nil { - // Prefix all field names with the explicit - // embedded struct selector, to avoid ambiguity. - for i := range maybe { - maybe[i].FieldPath = append([]string{embedded(f.Type)}, maybe[i].FieldPath...) - } - - return maybe - } - } - - // If not flattening, or the embedded type wasn't a struct, - // embed it under the type name. - sf[0].FieldName = embedded(f.Type) - case 1: - sf[0].FieldName = f.Names[0].Name - default: - // this is for a multiple in-line declaration, - // e.g. type A struct { One, Two int } - sf = sf[0:0] - for _, nm := range f.Names { - sf = append(sf, gen.StructField{ - FieldTag: nm.Name, - FieldName: nm.Name, - FieldElem: ex.Copy(), - }) - } - return sf - } - - // resolve local package type aliases that referenced in this package structs - resolveAlias := func(el gen.Elem) { - if a, ok := fs.Aliases[el.TypeName()]; ok { - if b, ok := a.(*ast.SelectorExpr); ok { - if c, ok := b.X.(*ast.Ident); ok { - el.Alias(c.Name + "." + b.Sel.Name) - } - } else if b, ok := a.(*ast.Ident); ok { - el.Alias(b.Name) - } - } - } - // resolve field alias type - resolveAlias(ex) - // resolve field map type that have alias type key or value - if m, ok := ex.(*gen.Map); ok { - resolveAlias(m.Key) - resolveAlias(m.Value) - } - // resolve field slice type that have alias type element - if m, ok := ex.(*gen.Slice); ok { - resolveAlias(m.Els) - } - - sf[0].FieldElem = ex - if sf[0].FieldTag == "" { - sf[0].FieldTag = sf[0].FieldName - } - if sf[0].FieldTagParts == nil { - sf[0].FieldTagParts = []string{sf[0].FieldName} - } - sf[0].FieldElem.SetAllocBound(allocbound) - sf[0].FieldElem.SetMaxTotalBytes(maxtotalbytes) - - // validate extension - if extension { - switch ex := ex.(type) { - case *gen.Ptr: - if b, ok := ex.Value.(*gen.BaseElem); ok { - b.Value = gen.Ext - } else { - warnln("couldn't cast to extension.") - return nil - } - case *gen.BaseElem: - ex.Value = gen.Ext - default: - warnln("couldn't cast to extension.") - return nil - } - } - return sf -} - -func (fs *FileSet) getFieldsFromEmbeddedStruct(importPrefix string, f ast.Expr) []gen.StructField { - switch f := f.(type) { - case *ast.Ident: - s, ok := fs.Specs[f.Name] - if !ok { - s = fs.Aliases[f.Name] - } - - switch s := s.(type) { - case *ast.StructType: - return fs.parseFieldList(importPrefix, s.Fields) - default: - return nil - } - case *ast.SelectorExpr: - pkg := f.X - pkgid, ok := pkg.(*ast.Ident) - if !ok { - return nil - } - - pkgname, ok := fs.ImportName[pkgid.Name] - if !ok { - return nil - } - - pkgfs, ok := fs.ImportSet[pkgname] - if !ok { - return nil - } - - return pkgfs.getFieldsFromEmbeddedStruct(pkgid.Name+".", f.Sel) - default: - // other possibilities are disallowed - return nil - } -} - -// extract embedded field name -// -// so, for a struct like -// -// type A struct { -// io.Writer -// } -// -// we want "Writer" -func embedded(f ast.Expr) string { - switch f := f.(type) { - case *ast.Ident: - return f.Name - case *ast.StarExpr: - return embedded(f.X) - case *ast.SelectorExpr: - return f.Sel.Name - default: - // other possibilities are disallowed - return "" - } -} - -// stringify a field type name -func stringify(e ast.Expr) string { - switch e := e.(type) { - case *ast.Ident: - return e.Name - case *ast.StarExpr: - return "*" + stringify(e.X) - case *ast.SelectorExpr: - return stringify(e.X) + "." + e.Sel.Name - case *ast.ArrayType: - if e.Len == nil { - return "[]" + stringify(e.Elt) - } - return fmt.Sprintf("[%s]%s", stringify(e.Len), stringify(e.Elt)) - case *ast.InterfaceType: - if e.Methods == nil || e.Methods.NumFields() == 0 { - return "interface{}" - } - } - return "" -} - -// recursively translate ast.Expr to gen.Elem; nil means type not supported -// expected input types: -// - *ast.MapType (map[T]J) -// - *ast.Ident (name) -// - *ast.ArrayType ([(sz)]T) -// - *ast.StarExpr (*T) -// - *ast.StructType (struct {}) -// - *ast.SelectorExpr (a.B) -// - *ast.InterfaceType (interface {}) -func (fs *FileSet) parseExpr(importPrefix string, e ast.Expr) gen.Elem { - switch e := e.(type) { - - case *ast.MapType: - kt := fs.parseExpr(importPrefix, e.Key) - if kt == nil { - return nil - } - - vt := fs.parseExpr(importPrefix, e.Value) - if vt == nil { - return nil - } - - return &gen.Map{Key: kt, Value: vt} - - case *ast.Ident: - b := gen.Ident(importPrefix, e.Name) - - // work to resove this expression - // can be done later, once we've resolved - // everything else. - if b.Value == gen.IDENT { - _, specOK := fs.Specs[e.Name] - _, aliasOK := fs.Aliases[e.Name] - _, interfaceOK := fs.Interfaces[e.Name] - if !specOK && !aliasOK && !interfaceOK { - warnf("non-local identifier: %s\n", e.Name) - } - } - return b - - case *ast.ArrayType: - - // special case for []byte - if e.Len == nil { - if i, ok := e.Elt.(*ast.Ident); ok && i.Name == "byte" { - return &gen.BaseElem{Value: gen.Bytes} - } - } - - // return early if we don't know - // what the slice element type is - els := fs.parseExpr(importPrefix, e.Elt) - if els == nil { - return nil - } - - // array and not a slice - if e.Len != nil { - switch s := e.Len.(type) { - case *ast.BasicLit: - return &gen.Array{ - Size: s.Value, - Els: els, - } - - case *ast.Ident: - sizeHint := "" - if s.Obj != nil && s.Obj.Kind == ast.Con { - switch d := s.Obj.Decl.(type) { - case *ast.ValueSpec: - if len(d.Names) == 1 && len(d.Values) == 1 { - v := d.Values[0] - // Keep trying to resolve this value - repeat := true - for repeat { - switch vv := v.(type) { - case *ast.BasicLit: - sizeHint = vv.Value - repeat = false - case *ast.SelectorExpr: - switch xv := vv.X.(type) { - case *ast.Ident: - pkgpath := fs.ImportName[xv.Name] - pkg := fs.ImportSet[pkgpath] - v = pkg.Consts[vv.Sel.Name] - } - default: - repeat = false - } - } - } - } - } - return &gen.Array{ - Size: s.String(), - SizeHint: sizeHint, - Els: els, - } - - case *ast.SelectorExpr: - return &gen.Array{ - Size: stringify(s), - Els: els, - } - - default: - return nil - } - } - return &gen.Slice{Els: els} - - case *ast.StarExpr: - if v := fs.parseExpr(importPrefix, e.X); v != nil { - return &gen.Ptr{Value: v} - } - return nil - - case *ast.StructType: - return &gen.Struct{Fields: fs.parseFieldList(importPrefix, e.Fields)} - - case *ast.SelectorExpr: - return gen.Ident("", stringify(e)) - - case *ast.InterfaceType: - // support `interface{}` - if len(e.Methods.List) == 0 { - return &gen.BaseElem{Value: gen.Intf} - } - return nil - - default: // other types not supported - return nil - } -} - -func infof(s string, v ...interface{}) { - pushstate(s) - if print(0) { - fmt.Printf(chalk.Green.Color(strings.Join(logctx, ": ")), v...) - } - popstate() -} - -func infoln(s string) { - pushstate(s) - if print(0) { - fmt.Println(chalk.Green.Color(strings.Join(logctx, ": "))) - } - popstate() -} - -func warnf(s string, v ...interface{}) { - pushstate(s) - if print(1) { - fmt.Printf(chalk.Yellow.Color(strings.Join(logctx, ": ")), v...) - } - popstate() -} - -func warnln(s string) { - pushstate(s) - if print(1) { - fmt.Println(chalk.Yellow.Color(strings.Join(logctx, ": "))) - } - popstate() -} - -func fatalf(s string, v ...interface{}) { - pushstate(s) - if print(2) { - fmt.Printf(chalk.Red.Color(strings.Join(logctx, ": ")), v...) - } - popstate() -} - -var logctx []string -var printlevel int - -func increasePrintLevel() { - printlevel++ -} - -func decreasePrintLevel() { - printlevel-- -} - -func print(level int) bool { - return printlevel < level -} - -// push logging state -func pushstate(s string) { - logctx = append(logctx, s) -} - -// pop logging state -func popstate() { - logctx = logctx[:len(logctx)-1] -} diff --git a/msgp/parse/inline.go b/msgp/parse/inline.go deleted file mode 100644 index 0fba91dbf4..0000000000 --- a/msgp/parse/inline.go +++ /dev/null @@ -1,175 +0,0 @@ -package parse - -import ( - "sort" - - "github.com/algorand/msgp/gen" -) - -// This file defines when and how we -// propagate type information from -// one type declaration to another. -// After the processing pass, every -// non-primitive type is marshalled/unmarshalled/etc. -// through a function call. Here, we propagate -// the type information into the caller's type -// tree *if* the child type is simple enough. -// -// For example, types like -// -// type A [4]int -// -// will get pushed into parent methods, -// whereas types like -// -// type B [3]map[string]struct{A, B [4]string} -// -// will not. - -// this is an approximate measure -// of the number of children in a node -const maxComplex = 5 - -// begin recursive search for identities with the -// given name and replace them with be -func (f *FileSet) findShim(id string, be *gen.BaseElem) { - for name, el := range f.Identities { - pushstate(name) - switch el := el.(type) { - case *gen.Struct: - for i := range el.Fields { - f.nextShim(&el.Fields[i].FieldElem, id, be) - } - case *gen.Array: - f.nextShim(&el.Els, id, be) - case *gen.Slice: - f.nextShim(&el.Els, id, be) - case *gen.Map: - f.nextShim(&el.Value, id, be) - case *gen.Ptr: - f.nextShim(&el.Value, id, be) - } - popstate() - } - // we'll need this at the top level as well - f.Identities[id] = be -} - -func (f *FileSet) nextShim(ref *gen.Elem, id string, be *gen.BaseElem) { - if (*ref).TypeName() == id { - vn := (*ref).Varname() - *ref = be.Copy() - (*ref).SetVarname(vn) - } else { - switch el := (*ref).(type) { - case *gen.Struct: - for i := range el.Fields { - f.nextShim(&el.Fields[i].FieldElem, id, be) - } - case *gen.Array: - f.nextShim(&el.Els, id, be) - case *gen.Slice: - f.nextShim(&el.Els, id, be) - case *gen.Map: - f.nextShim(&el.Value, id, be) - case *gen.Ptr: - f.nextShim(&el.Value, id, be) - } - } -} - -// propInline identifies and inlines candidates -func (f *FileSet) propInline() { - type gelem struct { - name string - el gen.Elem - } - - all := make([]gelem, 0, len(f.Identities)) - - for name, el := range f.Identities { - all = append(all, gelem{name: name, el: el}) - } - - // make sure we process inlining determinstically: - // start with the least-complex elems; - // use identifier names as a tie-breaker - sort.Slice(all, func(i, j int) bool { - ig, jg := &all[i], &all[j] - ic, jc := ig.el.Complexity(), jg.el.Complexity() - return ic < jc || (ic == jc && ig.name < jg.name) - }) - - for i := range all { - name := all[i].name - pushstate(name) - switch el := all[i].el.(type) { - case *gen.Struct: - for i := range el.Fields { - f.nextInline(&el.Fields[i].FieldElem, name) - } - case *gen.Array: - f.nextInline(&el.Els, name) - case *gen.Slice: - f.nextInline(&el.Els, name) - case *gen.Map: - f.nextInline(&el.Value, name) - case *gen.Ptr: - f.nextInline(&el.Value, name) - } - popstate() - } -} - -const fatalloop = `detected infinite recursion in inlining loop! -Please file a bug at github.com/tinylib/msgp/issues! -Thanks! -` - -func (f *FileSet) nextInline(ref *gen.Elem, root string) { - switch el := (*ref).(type) { - case *gen.BaseElem: - // ensure that we're not inlining - // a type into itself - typ := el.TypeName() - if el.Value == gen.IDENT && typ != root { - if node, ok := f.Identities[typ]; ok && node.Complexity() < maxComplex { - // infof("inlining %s\n", typ) - - // This should never happen; it will cause - // infinite recursion. - if node == *ref { - panic(fatalloop) - } - - *ref = node.Copy() - f.nextInline(ref, node.TypeName()) - } else if !ok && !el.Resolved() { - // this is the point at which we're sure that - // we've got a type that isn't a primitive, - // a library builtin, or a processed type - - // we correctly handle this case by forwarding - // methods to the unresolved identifier; the - // code will fail to compile if the requisite - // methods aren't available for that type. - - // warnf("unresolved identifier: %s\n", typ) - } - } - case *gen.Struct: - for i := range el.Fields { - f.nextInline(&el.Fields[i].FieldElem, root) - } - case *gen.Array: - f.nextInline(&el.Els, root) - case *gen.Slice: - f.nextInline(&el.Els, root) - case *gen.Map: - f.nextInline(&el.Value, root) - case *gen.Ptr: - f.nextInline(&el.Value, root) - default: - panic("bad elem type") - } -} diff --git a/msgp/printer/print.go b/msgp/printer/print.go deleted file mode 100644 index fb45e1ed41..0000000000 --- a/msgp/printer/print.go +++ /dev/null @@ -1,168 +0,0 @@ -package printer - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "strings" - - "github.com/algorand/msgp/gen" - "github.com/algorand/msgp/parse" - "github.com/daixiang0/gci/pkg/gci" - "github.com/daixiang0/gci/pkg/gci/sections" - "github.com/ttacon/chalk" - "golang.org/x/tools/imports" -) - -func infof(s string, v ...interface{}) { - fmt.Printf(chalk.Magenta.Color(s), v...) -} - -// PrintFile prints the methods for the provided list -// of elements to the given file name and canonical -// package path. -func PrintFile(file string, f *parse.FileSet, mode gen.Method, skipFormat bool) error { - out, tests, err := generate(f, mode) - if err != nil { - return err - } - - // we'll run goimports on the main file - // in another goroutine, and run it here - // for the test file. empirically, this - // takes about the same amount of time as - // doing them in serial when GOMAXPROCS=1, - // and faster otherwise. - res := goformat(file, out.Bytes(), skipFormat) - if tests != nil { - testfile := strings.TrimSuffix(file, ".go") + "_test.go" - err = format(testfile, tests.Bytes(), skipFormat) - if err != nil { - return err - } - infof(">>> Wrote and formatted \"%s\"\n", testfile) - } - err = <-res - if err != nil { - return err - } - return nil -} - -func format(file string, data []byte, skipFormat bool) error { - if skipFormat { - return ioutil.WriteFile(file, data, 0600) - } - // first run through goimports (which cleans up unused deps & does gofmt) - out, err := imports.Process(file, data, nil) - if err != nil { - return err - } - if err := ioutil.WriteFile(file, out, 0600); err != nil { - return err - } - // then run through gci to arrange import order - if err := gci.WriteFormattedFiles([]string{file}, gci.GciConfiguration{ - Sections: gci.SectionList{ - sections.StandardPackage{}, - sections.DefaultSection{}, - sections.Prefix{ImportPrefix: "github.com/algorand"}, - sections.Prefix{ImportPrefix: "github.com/algorand/go-algorand"}, - }, - SectionSeparators: gci.SectionList{sections.NewLine{}}, - }); err != nil { - return err - } - return nil -} - -func goformat(file string, data []byte, skipFormat bool) <-chan error { - out := make(chan error, 1) - go func(file string, data []byte, end chan error) { - end <- format(file, data, skipFormat) - infof(">>> Wrote and formatted \"%s\"\n", file) - }(file, data, out) - return out -} - -func dedupImports(imp []string) []string { - m := make(map[string]struct{}) - for i := range imp { - m[imp[i]] = struct{}{} - } - r := []string{} - for k := range m { - r = append(r, k) - } - return r -} - -func generate(f *parse.FileSet, mode gen.Method) (*bytes.Buffer, *bytes.Buffer, error) { - outbuf := bytes.NewBuffer(make([]byte, 0, 4096)) - writePkgHeader(outbuf, f.Package) - - myImports := []string{"github.com/algorand/msgp/msgp"} - for _, imp := range f.Imports { - if imp.Name != nil { - // have an alias, include it. - myImports = append(myImports, imp.Name.Name+` `+imp.Path.Value) - } else { - myImports = append(myImports, imp.Path.Value) - } - } - dedup := dedupImports(myImports) - writeImportHeader(outbuf, dedup...) - - var testbuf *bytes.Buffer - var testwr io.Writer - if mode&gen.Test == gen.Test { - testbuf = bytes.NewBuffer(make([]byte, 0, 4096)) - writeBuildHeader(testbuf, []string{"!skip_msgp_testing"}) - writePkgHeader(testbuf, f.Package) - writeImportHeader( - testbuf, - "github.com/algorand/msgp/msgp", - "github.com/algorand/go-algorand/protocol", - "github.com/algorand/go-algorand/test/partitiontest", - "testing") - testwr = testbuf - } - funcbuf := bytes.NewBuffer(make([]byte, 0, 4096)) - var topics gen.Topics - - err := f.PrintTo(gen.NewPrinter(mode, &topics, funcbuf, testwr)) - if err == nil { - outbuf.Write(topics.Bytes()) - outbuf.Write(funcbuf.Bytes()) - } - return outbuf, testbuf, err -} - -func writePkgHeader(b *bytes.Buffer, name string) { - b.WriteString("package ") - b.WriteString(name) - b.WriteByte('\n') - // write generated code marker - // https://github.com/tinylib/msgp/issues/229 - // https://golang.org/s/generatedcode - b.WriteString("// Code generated by github.com/algorand/msgp DO NOT EDIT.\n\n") -} - -func writeImportHeader(b *bytes.Buffer, imports ...string) { - b.WriteString("import (\n") - for _, im := range imports { - if im[len(im)-1] == '"' { - // support aliased imports - fmt.Fprintf(b, "\t%s\n", im) - } else { - fmt.Fprintf(b, "\t%q\n", im) - } - } - b.WriteString(")\n\n") -} - -func writeBuildHeader(b *bytes.Buffer, buildHeaders []string) { - headers := fmt.Sprintf("//go:build %s\n// +build %s\n\n", strings.Join(buildHeaders, " "), strings.Join(buildHeaders, " ")) - b.WriteString(headers) -} diff --git a/msgp/printer/print_test.go b/msgp/printer/print_test.go deleted file mode 100644 index 112fa10a74..0000000000 --- a/msgp/printer/print_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package printer - -import ( - "bytes" - "testing" -) - -func TestWriteBuildHeader(t *testing.T) { - testBuf := bytes.NewBuffer(make([]byte, 0, 4096)) - buildHeaders := []string{"foobar"} - expectedBuf := bytes.NewBuffer(make([]byte, 0, 4096)) - expectedBuf.WriteString("//go:build foobar\n// +build foobar\n\n") - - writeBuildHeader(testBuf, buildHeaders) - - if testBuf.String() != expectedBuf.String() { - t.Errorf("testBuf:\n%s not equal to expectedBuf:\n%s", testBuf, expectedBuf) - } -} diff --git a/netdeploy/network.go b/netdeploy/network.go index 02202d5559..4e86bd831f 100644 --- a/netdeploy/network.go +++ b/netdeploy/network.go @@ -70,7 +70,7 @@ func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, b var err error template := defaultNetworkTemplate - err = loadTemplateFromReader(templateReader, &template) + err = LoadTemplateFromReader(templateReader, &template) if err == nil { if overrideDevMode { @@ -100,7 +100,7 @@ func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, b return n, err } template.Consensus = consensus - err = template.generateGenesisAndWallets(rootDir, n.cfg.Name, binDir) + err = template.generateGenesisAndWallets(rootDir, n.cfg.Name) if err != nil { return n, err } diff --git a/netdeploy/networkTemplate.go b/netdeploy/networkTemplate.go index 7d8b12bf92..43bb9d816f 100644 --- a/netdeploy/networkTemplate.go +++ b/netdeploy/networkTemplate.go @@ -33,6 +33,7 @@ import ( "github.com/algorand/go-algorand/gen" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/netdeploy/remote" + "github.com/algorand/go-algorand/network/p2p" "github.com/algorand/go-algorand/util" ) @@ -47,7 +48,7 @@ var defaultNetworkTemplate = NetworkTemplate{ Genesis: gen.DefaultGenesis, } -func (t NetworkTemplate) generateGenesisAndWallets(targetFolder, networkName, binDir string) error { +func (t NetworkTemplate) generateGenesisAndWallets(targetFolder, networkName string) error { genesisData := t.Genesis genesisData.NetworkName = networkName mergedConsensus := config.Consensus.Merge(t.Consensus) @@ -70,7 +71,7 @@ func (t NetworkTemplate) createNodeDirectories(targetFolder string, binDir strin relaysCount := countRelayNodes(t.Nodes) - for _, cfg := range t.Nodes { + for i, cfg := range t.Nodes { nodeDir := filepath.Join(targetFolder, cfg.Name) err = os.Mkdir(nodeDir, os.ModePerm) if err != nil { @@ -149,10 +150,24 @@ func (t NetworkTemplate) createNodeDirectories(targetFolder string, binDir strin // Create any necessary config.json file for this node nodeCfg := filepath.Join(nodeDir, config.ConfigFilename) - err = createConfigFile(cfg, nodeCfg, len(t.Nodes)-1, relaysCount) // minus 1 to avoid counting self + var mergedCfg config.Local + mergedCfg, err = createConfigFile(cfg, nodeCfg, len(t.Nodes)-1, relaysCount) // minus 1 to avoid counting self if err != nil { return } + + if mergedCfg.EnableP2P { + // generate peer ID file for this node + sk, pkErr := p2p.GetPrivKey(config.Local{P2PPersistPeerID: true}, genesisDir) + if pkErr != nil { + return nil, nil, pkErr + } + pid, pErr := p2p.PeerIDFromPublicKey(sk.GetPublic()) + if pErr != nil { + return nil, nil, pErr + } + t.Nodes[i].P2PPeerID = string(pid) + } } return } @@ -165,11 +180,12 @@ func loadTemplate(templateFile string) (NetworkTemplate, error) { } defer f.Close() - err = loadTemplateFromReader(f, &template) + err = LoadTemplateFromReader(f, &template) return template, err } -func loadTemplateFromReader(reader io.Reader, template *NetworkTemplate) error { +// LoadTemplateFromReader loads and decodes a network template +func LoadTemplateFromReader(reader io.Reader, template *NetworkTemplate) error { if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { // for arm machines, use smaller key dilution @@ -289,7 +305,7 @@ func decodeJSONOverride(override string, cfg *config.Local) error { return nil } -func createConfigFile(node remote.NodeConfigGoal, configFile string, numNodes int, relaysCount int) error { +func createConfigFile(node remote.NodeConfigGoal, configFile string, numNodes int, relaysCount int) (config.Local, error) { cfg := config.GetDefaultLocal() cfg.GossipFanout = numNodes // Override default :8080 REST endpoint, and disable SRV lookup @@ -316,8 +332,8 @@ func createConfigFile(node remote.NodeConfigGoal, configFile string, numNodes in err := decodeJSONOverride(node.ConfigJSONOverride, &cfg) if err != nil { - return err + return config.Local{}, err } - return cfg.SaveToFile(configFile) + return cfg, cfg.SaveToFile(configFile) } diff --git a/netdeploy/networkTemplates_test.go b/netdeploy/networkTemplates_test.go index f21c37ace9..80f2c2a43c 100644 --- a/netdeploy/networkTemplates_test.go +++ b/netdeploy/networkTemplates_test.go @@ -69,9 +69,8 @@ func TestGenerateGenesis(t *testing.T) { targetFolder := t.TempDir() networkName := "testGenGen" - binDir := os.ExpandEnv("${GOPATH}/bin") - err := template.generateGenesisAndWallets(targetFolder, networkName, binDir) + err := template.generateGenesisAndWallets(targetFolder, networkName) a.NoError(err) _, err = os.Stat(filepath.Join(targetFolder, config.GenesisJSONFile)) fileExists := err == nil diff --git a/netdeploy/remote/deployedNetwork.go b/netdeploy/remote/deployedNetwork.go index 8c4b3eaee9..16d562a554 100644 --- a/netdeploy/remote/deployedNetwork.go +++ b/netdeploy/remote/deployedNetwork.go @@ -445,7 +445,8 @@ func (cfg DeployedNetwork) GenerateDatabaseFiles(fileCfgs BootstrappedNetwork, g localCfg.Archival = true localCfg.CatchpointTracking = -1 localCfg.LedgerSynchronousMode = 0 - l, err := ledger.OpenLedger(log, filepath.Join(genesisFolder, "bootstrapped"), false, initState, localCfg) + prefix := filepath.Join(genesisFolder, "bootstrapped") + l, err := ledger.OpenLedger(log, prefix, false, initState, localCfg) if err != nil { return err } @@ -479,7 +480,8 @@ func (cfg DeployedNetwork) GenerateDatabaseFiles(fileCfgs BootstrappedNetwork, g l.Close() localCfg.CatchpointTracking = 0 - l, err = ledger.OpenLedger(log, genesisFolder+"/bootstrapped", false, initState, localCfg) + prefix2 := genesisFolder + "/bootstrapped" + l, err = ledger.OpenLedger(log, prefix2, false, initState, localCfg) if err != nil { return err } diff --git a/netdeploy/remote/nodeConfig.go b/netdeploy/remote/nodeConfig.go index c3daed6713..191f434737 100644 --- a/netdeploy/remote/nodeConfig.go +++ b/netdeploy/remote/nodeConfig.go @@ -57,6 +57,7 @@ type NodeConfigGoal struct { Name string IsRelay bool `json:",omitempty"` Wallets []NodeWalletData + P2PPeerID string `json:",omitempty"` DeadlockDetection int `json:"-"` ConfigJSONOverride string `json:",omitempty"` // Raw json to merge into config.json after other modifications are complete PeerList string `json:",omitempty"` // Semicolon separated list of peers to connect to. Only applicable for non-relays diff --git a/network/addr.go b/network/addr.go new file mode 100644 index 0000000000..f97010b4cc --- /dev/null +++ b/network/addr.go @@ -0,0 +1,95 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "errors" + "net/url" + "path" + "regexp" + "strings" + + "github.com/multiformats/go-multiaddr" +) + +var errURLNoHost = errors.New("could not parse a host from url") + +var errURLColonHost = errors.New("host name starts with a colon") + +// HostColonPortPattern matches "^[-a-zA-Z0-9.]+:\\d+$" e.g. "foo.com.:1234" +var HostColonPortPattern = regexp.MustCompile(`^[-a-zA-Z0-9.]+:\d+$`) + +// ParseHostOrURL handles "host:port" or a full URL. +// Standard library net/url.Parse chokes on "host:port". +func ParseHostOrURL(addr string) (*url.URL, error) { + // If the entire addr is "host:port" grab that right away. + // Don't try url.Parse() because that will grab "host:" as if it were "scheme:" + if HostColonPortPattern.MatchString(addr) { + return &url.URL{Scheme: "http", Host: addr}, nil + } + parsed, err := url.Parse(addr) + if err == nil { + if parsed.Host == "" { + return nil, errURLNoHost + } + return parsed, nil + } + if strings.HasPrefix(addr, "http:") || strings.HasPrefix(addr, "https:") || strings.HasPrefix(addr, "ws:") || strings.HasPrefix(addr, "wss:") || strings.HasPrefix(addr, "://") || strings.HasPrefix(addr, "//") { + return parsed, err + } + // This turns "[::]:4601" into "http://[::]:4601" which url.Parse can do + parsed, e2 := url.Parse("http://" + addr) + if e2 == nil { + // https://datatracker.ietf.org/doc/html/rfc1123#section-2 + // first character is relaxed to allow either a letter or a digit + if parsed.Host[0] == ':' && (len(parsed.Host) < 2 || parsed.Host[1] != ':') { + return nil, errURLColonHost + } + return parsed, nil + } + return parsed, err /* return original err, not our prefix altered try */ +} + +// ParseHostOrURLOrMultiaddr returns an error if it could not parse the provided +// string as a valid "host:port", full URL, or multiaddr. If no error, it returns +// a host:port address, or a multiaddr. +func ParseHostOrURLOrMultiaddr(addr string) (string, error) { + if strings.HasPrefix(addr, "/") && !strings.HasPrefix(addr, "//") { // multiaddr starts with '/' but not '//' which is possible for scheme relative URLS + _, err := multiaddr.NewMultiaddr(addr) + return addr, err + } + url, err := ParseHostOrURL(addr) + if err != nil { + return "", err + } + return url.Host, nil +} + +// addrToGossipAddr parses host:port or a URL and returns the URL to the websocket interface at that address. +func (wn *WebsocketNetwork) addrToGossipAddr(addr string) (string, error) { + parsedURL, err := ParseHostOrURL(addr) + if err != nil { + wn.log.Warnf("could not parse addr %#v: %s", addr, err) + return "", errBadAddr + } + parsedURL.Scheme = websocketsScheme[parsedURL.Scheme] + if parsedURL.Scheme == "" { + parsedURL.Scheme = "ws" + } + parsedURL.Path = strings.Replace(path.Join(parsedURL.Path, GossipNetworkPath), "{genesisID}", wn.GenesisID, -1) + return parsedURL.String(), nil +} diff --git a/network/addr_test.go b/network/addr_test.go new file mode 100644 index 0000000000..6763410147 --- /dev/null +++ b/network/addr_test.go @@ -0,0 +1,137 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "net/url" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +type urlCase struct { + text string + out url.URL +} + +func TestParseHostOrURL(t *testing.T) { + partitiontest.PartitionTest(t) + urlTestCases := []urlCase{ + {"localhost:123", url.URL{Scheme: "http", Host: "localhost:123"}}, + {"http://localhost:123", url.URL{Scheme: "http", Host: "localhost:123"}}, + {"ws://localhost:9999", url.URL{Scheme: "ws", Host: "localhost:9999"}}, + {"wss://localhost:443", url.URL{Scheme: "wss", Host: "localhost:443"}}, + {"https://localhost:123", url.URL{Scheme: "https", Host: "localhost:123"}}, + {"https://somewhere.tld", url.URL{Scheme: "https", Host: "somewhere.tld"}}, + {"http://127.0.0.1:123", url.URL{Scheme: "http", Host: "127.0.0.1:123"}}, + {"//somewhere.tld", url.URL{Scheme: "", Host: "somewhere.tld"}}, + {"//somewhere.tld:4601", url.URL{Scheme: "", Host: "somewhere.tld:4601"}}, + {"http://[::]:123", url.URL{Scheme: "http", Host: "[::]:123"}}, + {"1.2.3.4:123", url.URL{Scheme: "http", Host: "1.2.3.4:123"}}, + {"[::]:123", url.URL{Scheme: "http", Host: "[::]:123"}}, + {"r2-devnet.devnet.algodev.network:4560", url.URL{Scheme: "http", Host: "r2-devnet.devnet.algodev.network:4560"}}, + {"::11.22.33.44:123", url.URL{Scheme: "http", Host: "::11.22.33.44:123"}}, + } + badUrls := []string{ + "justahost", + "localhost:WAT", + "http://localhost:WAT", + "https://localhost:WAT", + "ws://localhost:WAT", + "wss://localhost:WAT", + "//localhost:WAT", + "://badaddress", // See rpcs/blockService_test.go TestRedirectFallbackEndpoints + "://localhost:1234", + ":xxx", + ":xxx:1234", + "::11.22.33.44", + ":a:1", + ":a:", + ":1", + ":a", + ":", + "", + } + for _, tc := range urlTestCases { + t.Run(tc.text, func(t *testing.T) { + v, err := ParseHostOrURL(tc.text) + require.NoError(t, err) + if tc.out != *v { + t.Errorf("url wanted %#v, got %#v", tc.out, v) + return + } + }) + t.Run(tc.text+"-multiaddr", func(t *testing.T) { + v, err := ParseHostOrURLOrMultiaddr(tc.text) + require.NoError(t, err) + if tc.out.Host != v { + t.Errorf("url wanted %#v, got %#v", tc.text, v) + return + } + }) + } + for _, addr := range badUrls { + t.Run(addr, func(t *testing.T) { + _, err := ParseHostOrURL(addr) + require.Error(t, err, "url should fail", addr) + }) + t.Run(addr+"-multiaddr", func(t *testing.T) { + _, err := ParseHostOrURLOrMultiaddr(addr) + require.Error(t, err, "url should fail", addr) + }) + } + +} + +func TestParseHostURLOrMultiaddr(t *testing.T) { + partitiontest.PartitionTest(t) + + validMultiAddrs := []string{ + "/ip4/127.0.0.1/tcp/8080", + "/ip6/::1/tcp/8080", + "/ip4/192.168.1.1/udp/9999/quic", + "/ip4/192.168.1.1/tcp/8180/p2p/Qmewz5ZHN1AAGTarRbMupNPbZRfg3p5jUGoJ3JYEatJVVk", + "/ip4/192.255.2.8/tcp/8180/ws", + } + + badMultiAddrs := []string{ + "/ip4/256.256.256.256/tcp/8080", // Invalid IPv4 address. + "/ip4/127.0.0.1/abc/8080", // abc is not a valid protocol. + "/ip4/127.0.0.1/tcp/abc", // Port is not a valid number. + "/unix", // Unix protocol without a path is invalid. + "/ip4/127.0.0.1/tcp", // Missing a port after tcp + "/p2p/invalidPeerID", // Invalid peer ID after p2p. + "ip4/127.0.0.1/tcp/8080", // Missing starting /. + } + + for _, addr := range validMultiAddrs { + t.Run(addr, func(t *testing.T) { + v, err := ParseHostOrURLOrMultiaddr(addr) + require.NoError(t, err) + require.Equal(t, addr, v) + }) + } + + for _, addr := range badMultiAddrs { + t.Run(addr, func(t *testing.T) { + _, err := ParseHostOrURLOrMultiaddr(addr) + require.Error(t, err) + }) + } + +} diff --git a/network/gossipNode.go b/network/gossipNode.go new file mode 100644 index 0000000000..7ae667170a --- /dev/null +++ b/network/gossipNode.go @@ -0,0 +1,209 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "context" + "net" + "net/http" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/protocol" +) + +// Peer opaque interface for referring to a neighbor in the network +type Peer interface{} + +// PeerOption allows users to specify a subset of peers to query +// +//msgp:ignore PeerOption +type PeerOption int + +const ( + // PeersConnectedOut specifies all peers with outgoing connections + PeersConnectedOut PeerOption = iota + // PeersConnectedIn specifies all peers with inbound connections + PeersConnectedIn PeerOption = iota + // PeersPhonebookRelays specifies all relays in the phonebook + PeersPhonebookRelays PeerOption = iota + // PeersPhonebookArchivalNodes specifies all archival nodes (relay or p2p) + PeersPhonebookArchivalNodes PeerOption = iota + // PeersPhonebookArchivers specifies all archivers in the phonebook + PeersPhonebookArchivers PeerOption = iota +) + +// GossipNode represents a node in the gossip network +type GossipNode interface { + Address() (string, bool) + Broadcast(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error + Relay(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error + Disconnect(badnode Peer) + DisconnectPeers() // only used by testing + + // RegisterHTTPHandler path accepts gorilla/mux path annotations + RegisterHTTPHandler(path string, handler http.Handler) + + // RequestConnectOutgoing asks the system to actually connect to peers. + // `replace` optionally drops existing connections before making new ones. + // `quit` chan allows cancellation. TODO: use `context` + RequestConnectOutgoing(replace bool, quit <-chan struct{}) + + // Get a list of Peers we could potentially send a direct message to. + GetPeers(options ...PeerOption) []Peer + + // Start threads, listen on sockets. + Start() + + // Close sockets. Stop threads. + Stop() + + // RegisterHandlers adds to the set of given message handlers. + RegisterHandlers(dispatch []TaggedMessageHandler) + + // ClearHandlers deregisters all the existing message handlers. + ClearHandlers() + + // GetRoundTripper returns a Transport that would limit the number of outgoing connections. + GetRoundTripper() http.RoundTripper + + // OnNetworkAdvance notifies the network library that the agreement protocol was able to make a notable progress. + // this is the only indication that we have that we haven't formed a clique, where all incoming messages + // arrive very quickly, but might be missing some votes. The usage of this call is expected to have similar + // characteristics as with a watchdog timer. + OnNetworkAdvance() + + // GetHTTPRequestConnection returns the underlying connection for the given request. Note that the request must be the same + // request that was provided to the http handler ( or provide a fallback Context() to that ) + GetHTTPRequestConnection(request *http.Request) (conn net.Conn) + + // SubstituteGenesisID substitutes the "{genesisID}" with their network-specific genesisID. + SubstituteGenesisID(rawURL string) string + + // called from wsPeer to report that it has closed + peerRemoteClose(peer *wsPeer, reason disconnectReason) +} + +var outgoingMessagesBufferSize = int( + max(config.Consensus[protocol.ConsensusCurrentVersion].NumProposers, + config.Consensus[protocol.ConsensusCurrentVersion].SoftCommitteeSize, + config.Consensus[protocol.ConsensusCurrentVersion].CertCommitteeSize, + config.Consensus[protocol.ConsensusCurrentVersion].NextCommitteeSize) + + max(config.Consensus[protocol.ConsensusCurrentVersion].LateCommitteeSize, + config.Consensus[protocol.ConsensusCurrentVersion].RedoCommitteeSize, + config.Consensus[protocol.ConsensusCurrentVersion].DownCommitteeSize), +) + +// IncomingMessage represents a message arriving from some peer in our p2p network +type IncomingMessage struct { + Sender Peer + Tag Tag + Data []byte + Err error + Net GossipNode + + // Received is time.Time.UnixNano() + Received int64 + + // processing is a channel that is used by messageHandlerThread + // to indicate that it has started processing this message. It + // is used to ensure fairness across peers in terms of processing + // messages. + processing chan struct{} +} + +// Tag is a short string (2 bytes) marking a type of message +type Tag = protocol.Tag + +func highPriorityTag(tags []protocol.Tag) bool { + for _, tag := range tags { + if tag == protocol.AgreementVoteTag || tag == protocol.ProposalPayloadTag { + return true + } + } + return false +} + +// OutgoingMessage represents a message we want to send. +type OutgoingMessage struct { + Action ForwardingPolicy + Tag Tag + Payload []byte + Topics Topics + reason disconnectReason // used when Action == Disconnect + + // OnRelease is a function called when outgoing message, resulting from this incoming message, is released + // either by being sent or discarded. + OnRelease func() +} + +// ForwardingPolicy is an enum indicating to whom we should send a message +// +//msgp:ignore ForwardingPolicy +type ForwardingPolicy int + +const ( + // Ignore - discard (don't forward) + Ignore ForwardingPolicy = iota + + // Disconnect - disconnect from the peer that sent this message + Disconnect + + // Broadcast - forward to everyone (except the sender) + Broadcast + + // Respond - reply to the sender + Respond +) + +// MessageHandler takes a IncomingMessage (e.g., vote, transaction), processes it, and returns what (if anything) +// to send to the network in response. +// The ForwardingPolicy field of the returned OutgoingMessage indicates whether to reply directly to the sender +// (unicast), propagate to everyone except the sender (broadcast), or do nothing (ignore). +type MessageHandler interface { + Handle(message IncomingMessage) OutgoingMessage +} + +// HandlerFunc represents an implemenation of the MessageHandler interface +type HandlerFunc func(message IncomingMessage) OutgoingMessage + +// Handle implements MessageHandler.Handle, calling the handler with the IncomingKessage and returning the OutgoingMessage +func (f HandlerFunc) Handle(message IncomingMessage) OutgoingMessage { + return f(message) +} + +// TaggedMessageHandler receives one type of broadcast messages +type TaggedMessageHandler struct { + Tag + MessageHandler +} + +// Propagate is a convenience function to save typing in the common case of a message handler telling us to propagate an incoming message +// "return network.Propagate(msg)" instead of "return network.OutgoingMsg{network.Broadcast, msg.Tag, msg.Data}" +func Propagate(msg IncomingMessage) OutgoingMessage { + return OutgoingMessage{Action: Broadcast, Tag: msg.Tag, Payload: msg.Data, Topics: nil} +} + +// find the max value across the given uint64 numbers. +func max(numbers ...uint64) (maxNum uint64) { + maxNum = 0 // this is the lowest uint64 value. + for _, num := range numbers { + if num > maxNum { + maxNum = num + } + } + return +} diff --git a/network/msgp_gen.go b/network/msgp_gen.go index 53216cec3c..df423c8746 100644 --- a/network/msgp_gen.go +++ b/network/msgp_gen.go @@ -107,11 +107,11 @@ func (_ disconnectReason) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *disconnectReason) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -197,11 +197,11 @@ func (_ *identityChallenge) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityChallenge) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -386,11 +386,11 @@ func (_ *identityChallengeResponse) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityChallengeResponse) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int @@ -548,11 +548,11 @@ func (_ *identityChallengeResponseSigned) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityChallengeResponseSigned) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -691,11 +691,11 @@ func (_ *identityChallengeSigned) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityChallengeSigned) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -810,11 +810,11 @@ func (_ *identityChallengeValue) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityChallengeValue) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- bts, err = msgp.ReadExactBytes(bts, (*z)[:]) if err != nil { err = msgp.WrapError(err) @@ -879,11 +879,11 @@ func (_ *identityVerificationMessage) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityVerificationMessage) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int @@ -1023,11 +1023,11 @@ func (_ *identityVerificationMessageSigned) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *identityVerificationMessageSigned) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0002 int diff --git a/network/p2p/README.md b/network/p2p/README.md new file mode 100644 index 0000000000..8490e391b6 --- /dev/null +++ b/network/p2p/README.md @@ -0,0 +1,65 @@ +# P2P + +This package provides an interface for peer-to-peer networking features, such +as peer identification, addressing, connectivity, messaging, custom protocol streams, +and peer discovery. These features are implemented using [libp2p](https://libp2p.io), +and this package and its sub-packages serve as the main integration point with libp2p. + +The code in this package is part of an ongoing initiative to research and implement +an alternative networking stack that supports decentralized network topologies, while +benefiting from the history and experience of libp2p's protocol development. + +The current status of this initiative is **purely experimental**, and is not enabled by default +or recommended for regular use. **The components implemented here offer no +guarantee of security, reliability, or performance.** Updates will be provided on the +progress and readiness of these components for standard usage in the future. + +## Background + +Decentralized networks depend on mechanisms for discovering other peers and reliably +broadcasting messages to other interested peers. Libp2p provides pluggable transports +and multiplexing to allow multiple stream-based protocols to share the same connection. +Libp2p also provides an implementation of a message-based gossip protocol, GossipSub. + +Algorand's current network protocol sends messages between peers over bidirectional +WebSocket connections. Nodes that are configured to enable message-forwarding (including +nodes currently called "relays") validate incoming messages, then selectively forward +messages to other connected peers. This network implementation (`WebsocketNetwork`) sits +behind the `GossipNode` interface in the network package. + +## Current P2P implementation details + +This package supports a `P2PNetwork` implementation of the `GossipNode` interface, +providing the same peer connection management and message broadcast functions, but +via peer connections managed by libp2p. The `P2PNetwork` implementation uses +[libp2p's multiaddress scheme](https://docs.libp2p.io/concepts/fundamentals/addressing/) +and [peer IDs](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-ids-in-multiaddrs) +to establish connections and identify peers. + +Currently transactions (protocol tag `TX`) are distributed using the GossipSub protocol, +while all other messages are forwarded over a custom message protocol `/algorand-ws/1.0.0` +that uses the same message serialization as the existing `WebsocketNetwork` implementation. +These two protocols are multiplexed over a single connection using libp2p streams. + +```mermaid +graph LR + subgraph "P2P Implementation" + P2P[P2PNetwork] + TX[Transactions] + AW[Other messages] + end + P2P --> TX + P2P --> AW + + subgraph "libp2p" + G["GossipSub 1.1
/meshsub/1.1.0"] + WS["Legacy message serialization
/algorand-ws/1.0.0"] + S["Multiplexer
/yamux/1.0.0"] + T["TCP Transport"] + end + G --> S + WS --> S + TX --> G + AW --> WS + S --> T +``` diff --git a/network/p2p/p2p.go b/network/p2p/p2p.go new file mode 100644 index 0000000000..94643fc61d --- /dev/null +++ b/network/p2p/p2p.go @@ -0,0 +1,205 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package p2p + +import ( + "context" + "fmt" + "runtime" + "strings" + "time" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-deadlock" + + "github.com/libp2p/go-libp2p" + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/p2p/muxer/yamux" + "github.com/libp2p/go-libp2p/p2p/transport/tcp" +) + +// Service defines the interface used by the network integrating with underlying p2p implementation +type Service interface { + Close() error + ID() peer.ID // return peer.ID for self + AddrInfo() peer.AddrInfo // return addrInfo for self + + DialNode(context.Context, *peer.AddrInfo) error + DialPeersUntilTargetCount(targetConnCount int) + ClosePeer(peer.ID) error + + Conns() []network.Conn + ListPeersForTopic(topic string) []peer.ID + Subscribe(topic string, val pubsub.ValidatorEx) (*pubsub.Subscription, error) + Publish(ctx context.Context, topic string, data []byte) error +} + +// serviceImpl manages integration with libp2p and implements the Service interface +type serviceImpl struct { + log logging.Logger + host host.Host + streams *streamManager + pubsub *pubsub.PubSub + pubsubCtx context.Context + + topics map[string]*pubsub.Topic + topicsMu deadlock.RWMutex +} + +// AlgorandWsProtocol defines a libp2p protocol name for algorand's websockets messages +const AlgorandWsProtocol = "/algorand-ws/1.0.0" + +const dialTimeout = 30 * time.Second + +// MakeService creates a P2P service instance +func MakeService(ctx context.Context, log logging.Logger, cfg config.Local, datadir string, pstore peerstore.Peerstore, wsStreamHandler StreamHandler) (*serviceImpl, error) { + // load stored peer ID, or make ephemeral peer ID + privKey, err := GetPrivKey(cfg, datadir) + if err != nil { + return nil, err + } + + // muxer supports tweaking fields from yamux.Config + ymx := *yamux.DefaultTransport + // user-agent copied from wsNetwork.go + version := config.GetCurrentVersion() + ua := fmt.Sprintf("algod/%d.%d (%s; commit=%s; %d) %s(%s)", version.Major, version.Minor, version.Channel, version.CommitHash, version.BuildNumber, runtime.GOOS, runtime.GOARCH) + + var listenAddr string + if cfg.NetAddress != "" { + if parsedListenAddr, perr := netAddressToListenAddress(cfg.NetAddress); perr == nil { + listenAddr = parsedListenAddr + } + } else { + listenAddr = "/ip4/0.0.0.0/tcp/0" + } + + h, err := libp2p.New( + libp2p.Identity(privKey), + libp2p.UserAgent(ua), + libp2p.Transport(tcp.NewTCPTransport), + libp2p.Muxer("/yamux/1.0.0", &ymx), + libp2p.Peerstore(pstore), + libp2p.ListenAddrStrings(listenAddr), + ) + if err != nil { + return nil, err + } + log.Infof("P2P service started: peer ID %s addrs %s", h.ID(), h.Addrs()) + + sm := makeStreamManager(ctx, log, h, wsStreamHandler) + h.Network().Notify(sm) + h.SetStreamHandler(AlgorandWsProtocol, sm.streamHandler) + + ps, err := makePubSub(ctx, cfg, h) + if err != nil { + return nil, err + } + + return &serviceImpl{ + log: log, + host: h, + streams: sm, + pubsub: ps, + pubsubCtx: ctx, + topics: make(map[string]*pubsub.Topic), + }, nil +} + +// Close shuts down the P2P service +func (s *serviceImpl) Close() error { + return s.host.Close() +} + +// ID returns the peer.ID for self +func (s *serviceImpl) ID() peer.ID { + return s.host.ID() +} + +// DialPeersUntilTargetCount attempts to establish connections to the provided phonebook addresses +func (s *serviceImpl) DialPeersUntilTargetCount(targetConnCount int) { + peerIDs := s.host.Peerstore().Peers() + for _, peerID := range peerIDs { + // if we are at our target count stop trying to connect + if len(s.host.Network().Conns()) == targetConnCount { + return + } + // if we are already connected to this peer, skip it + if len(s.host.Network().ConnsToPeer(peerID)) > 0 { + continue + } + peerInfo := s.host.Peerstore().PeerInfo(peerID) + err := s.DialNode(context.Background(), &peerInfo) // leaving the calls as blocking for now, to not over-connect beyond fanout + if err != nil { + s.log.Warnf("failed to connect to peer %s: %v", peerID, err) + } + } +} + +// DialNode attempts to establish a connection to the provided peer +func (s *serviceImpl) DialNode(ctx context.Context, peer *peer.AddrInfo) error { + // don't try connecting to ourselves + if peer.ID == s.host.ID() { + return nil + } + ctx, cancel := context.WithTimeout(ctx, dialTimeout) + defer cancel() + return s.host.Connect(ctx, *peer) +} + +// AddrInfo returns the peer.AddrInfo for self +func (s *serviceImpl) AddrInfo() peer.AddrInfo { + return peer.AddrInfo{ + ID: s.host.ID(), + Addrs: s.host.Addrs(), + } +} + +// Conns returns the current connections +func (s *serviceImpl) Conns() []network.Conn { + return s.host.Network().Conns() +} + +// ClosePeer closes a connection to the provided peer +func (s *serviceImpl) ClosePeer(peer peer.ID) error { + return s.host.Network().ClosePeer(peer) +} + +// netAddressToListenAddress converts a netAddress in "ip:port" format to a listen address +// that can be passed in to libp2p.ListenAddrStrings +func netAddressToListenAddress(netAddress string) (string, error) { + // split the string on ":" + // if there are more than 2 parts, return an error + parts := strings.Split(netAddress, ":") + if len(parts) != 2 { + return "", fmt.Errorf("invalid netAddress %s; required format is \"ip:port\"", netAddress) + } + ip := "0.0.0.0" + if parts[0] != "" { + ip = parts[0] + } + if parts[1] == "" { + return "", fmt.Errorf("invalid netAddress %s, port is required", netAddress) + } + + return fmt.Sprintf("/ip4/%s/tcp/%s", ip, parts[1]), nil +} diff --git a/network/p2p/p2p_test.go b/network/p2p/p2p_test.go new file mode 100644 index 0000000000..5095e0d4b5 --- /dev/null +++ b/network/p2p/p2p_test.go @@ -0,0 +1,76 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package p2p + +import ( + "fmt" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +// Tests the helper function netAddressToListenAddress which converts +// a config value netAddress to a multiaddress usable by libp2p. +func TestNetAddressToListenAddress(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tests := []struct { + input string + output string + err bool + }{ + { + input: "192.168.1.1:8080", + output: "/ip4/192.168.1.1/tcp/8080", + err: false, + }, + { + input: ":8080", + output: "/ip4/0.0.0.0/tcp/8080", + err: false, + }, + { + input: "192.168.1.1:", + output: "", + err: true, + }, + { + input: "192.168.1.1", + output: "", + err: true, + }, + { + input: "192.168.1.1:8080:9090", + output: "", + err: true, + }, + } + + for _, test := range tests { //nolint:paralleltest + t.Run(fmt.Sprintf("input: %s", test.input), func(t *testing.T) { + res, err := netAddressToListenAddress(test.input) + if test.err { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, test.output, res) + } + }) + } +} diff --git a/network/p2p/peerID.go b/network/p2p/peerID.go index fce32a3afc..4d808b05e9 100644 --- a/network/p2p/peerID.go +++ b/network/p2p/peerID.go @@ -21,19 +21,23 @@ package p2p import ( "crypto/rand" "fmt" - "github.com/algorand/go-algorand/util" "os" "path" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/util" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" ) // DefaultPrivKeyPath is the default path inside the node's root directory at which the private key // for p2p identity is found and persisted to when a new one is generated. const DefaultPrivKeyPath = "peerIDPrivKey.pem" +// PeerID is a string representation of a peer's public key, primarily used to avoid importing libp2p into packages that shouldn't need it +type PeerID string + // GetPrivKey manages loading and creation of private keys for network PeerIDs // It prioritizes, in this order: // 1. user supplied path to privKey @@ -47,9 +51,12 @@ func GetPrivKey(cfg config.Local, dataDir string) (crypto.PrivKey, error) { return loadPrivateKeyFromFile(cfg.P2PPrivateKeyLocation) } // if a default path key exists load it - defaultPrivKeyPath := path.Join(dataDir, DefaultPrivKeyPath) - if util.FileExists(defaultPrivKeyPath) { - return loadPrivateKeyFromFile(defaultPrivKeyPath) + var defaultPrivKeyPath string + if dataDir != "" { + defaultPrivKeyPath = path.Join(dataDir, DefaultPrivKeyPath) + if util.FileExists(defaultPrivKeyPath) { + return loadPrivateKeyFromFile(defaultPrivKeyPath) + } } // generate a new key privKey, err := generatePrivKey() @@ -57,12 +64,21 @@ func GetPrivKey(cfg config.Local, dataDir string) (crypto.PrivKey, error) { return privKey, fmt.Errorf("failed to generate private key %w", err) } // if we want persistent PeerID, save the generated PrivKey - if cfg.P2PPersistPeerID { + if cfg.P2PPersistPeerID && defaultPrivKeyPath != "" { return privKey, writePrivateKeyToFile(defaultPrivKeyPath, privKey) } return privKey, nil } +// PeerIDFromPublicKey returns a PeerID from a public key, thin wrapper over libp2p function doing the same +func PeerIDFromPublicKey(pubKey crypto.PubKey) (PeerID, error) { + peerID, err := peer.IDFromPublicKey(pubKey) + if err != nil { + return "", err + } + return PeerID(peerID), nil +} + // loadPrivateKeyFromFile attempts to read raw privKey bytes from path // It only supports Ed25519 keys. func loadPrivateKeyFromFile(path string) (crypto.PrivKey, error) { diff --git a/network/p2p/peerID_test.go b/network/p2p/peerID_test.go index 1f15b32141..a3ce5c444d 100644 --- a/network/p2p/peerID_test.go +++ b/network/p2p/peerID_test.go @@ -80,6 +80,7 @@ func TestGetPrivKeyUserGeneratedPersisted(t *testing.T) { t.Parallel() tempdir := t.TempDir() cfg := config.GetDefaultLocal() + cfg.P2PPersistPeerID = true // get a generated private key privKey, err := GetPrivKey(cfg, tempdir) require.NoError(t, err) diff --git a/network/p2p/peerstore/peerstore.go b/network/p2p/peerstore/peerstore.go index ee6bf90a65..63a88966ff 100644 --- a/network/p2p/peerstore/peerstore.go +++ b/network/p2p/peerstore/peerstore.go @@ -18,15 +18,52 @@ package peerstore import ( "fmt" + "math" + "math/rand" + "time" "github.com/libp2p/go-libp2p/core/peer" libp2p "github.com/libp2p/go-libp2p/core/peerstore" mempstore "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" + "golang.org/x/exp/slices" ) +// when using GetAddresses with getAllAddresses, all the addresses will be retrieved, regardless +// of how many addresses the phonebook actually has. ( with the retry-after logic applied ) +const getAllAddresses = math.MaxInt32 + +// PhoneBookEntryRoles defines the roles that a single entry on the phonebook can take. +// currently, we have two roles : relay role and archiver role, which are mutually exclusive. +// +//msgp:ignore PhoneBookEntryRoles +type PhoneBookEntryRoles int + +const addressDataKey string = "addressData" + // PeerStore implements Peerstore and CertifiedAddrBook. type PeerStore struct { peerStoreCAB + connectionsRateLimitingCount uint + connectionsRateLimitingWindow time.Duration +} + +// addressData: holds the information associated with each phonebook address. +type addressData struct { + // retryAfter is the time to wait before retrying to connect to the address. + retryAfter time.Time + + // recentConnectionTimes is the log of connection times used to observe the maximum + // connections to the address in a given time window. + recentConnectionTimes []time.Time + + // networkNames: lists the networks to which the given address belongs. + networkNames map[string]bool + + // role is the role that this address serves. + role PhoneBookEntryRoles + + // persistent is set true for peers whose record should not be removed for the peer list + persistent bool } // peerStoreCAB combines the libp2p Peerstore and CertifiedAddrBook interfaces. @@ -47,6 +84,294 @@ func NewPeerStore(addrInfo []*peer.AddrInfo) (*PeerStore, error) { info := addrInfo[i] ps.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) } - pstore := &PeerStore{ps} + pstore := &PeerStore{peerStoreCAB: ps} + return pstore, nil +} + +// MakePhonebook creates a phonebook with the passed configuration values +func MakePhonebook(connectionsRateLimitingCount uint, + connectionsRateLimitingWindow time.Duration) (*PeerStore, error) { + ps, err := mempstore.NewPeerstore() + if err != nil { + return &PeerStore{}, fmt.Errorf("cannot initialize a peerstore: %w", err) + } + pstore := &PeerStore{peerStoreCAB: ps, + connectionsRateLimitingCount: connectionsRateLimitingCount, + connectionsRateLimitingWindow: connectionsRateLimitingWindow, + } return pstore, nil } + +// GetAddresses returns up to N addresses, but may return fewer +func (ps *PeerStore) GetAddresses(n int, role PhoneBookEntryRoles) []string { + return shuffleSelect(ps.filterRetryTime(time.Now(), role), n) +} + +// UpdateRetryAfter updates the retryAfter time for the given address. +func (ps *PeerStore) UpdateRetryAfter(addr string, retryAfter time.Time) { + info, err := PeerInfoFromDomainPort(addr) + if err != nil { + return + } + metadata, _ := ps.Get(info.ID, addressDataKey) + if metadata != nil { + ad, ok := metadata.(addressData) + if !ok { + return + } + ad.retryAfter = retryAfter + _ = ps.Put(info.ID, addressDataKey, ad) + } + +} + +// GetConnectionWaitTime will calculate and return the wait +// time to prevent exceeding connectionsRateLimitingCount. +// The connection should be established when the waitTime is 0. +// It will register a provisional next connection time when the waitTime is 0. +// The provisional time should be updated after the connection with UpdateConnectionTime +func (ps *PeerStore) GetConnectionWaitTime(addr string) (bool, time.Duration, time.Time) { + curTime := time.Now() + info, err := PeerInfoFromDomainPort(addr) + if err != nil { + return false, 0 /* not used */, curTime /* not used */ + } + var timeSince time.Duration + var numElmtsToRemove int + metadata, err := ps.Get(info.ID, addressDataKey) + if err != nil { + return false, 0 /* not used */, curTime /* not used */ + } + ad, ok := metadata.(addressData) + if !ok { + return false, 0 /* not used */, curTime /* not used */ + } + // Remove from recentConnectionTimes the times later than ConnectionsRateLimitingWindowSeconds + for numElmtsToRemove < len(ad.recentConnectionTimes) { + timeSince = curTime.Sub(ad.recentConnectionTimes[numElmtsToRemove]) + if timeSince >= ps.connectionsRateLimitingWindow { + numElmtsToRemove++ + } else { + break // break the loop. The rest are earlier than 1 second + } + } + + // Remove the expired elements from e.data[addr].recentConnectionTimes + ps.popNElements(numElmtsToRemove, peer.ID(addr)) + // If there are max number of connections within the time window, wait + metadata, _ = ps.Get(info.ID, addressDataKey) + ad, ok = metadata.(addressData) + if !ok { + return false, 0 /* not used */, curTime /* not used */ + } + numElts := len(ad.recentConnectionTimes) + if uint(numElts) >= ps.connectionsRateLimitingCount { + return true, /* true */ + ps.connectionsRateLimitingWindow - timeSince, curTime /* not used */ + } + + // Else, there is space in connectionsRateLimitingCount. The + // connection request of the caller will proceed + // Update curTime, since it may have significantly changed if waited + provisionalTime := time.Now() + // Append the provisional time for the next connection request + ps.appendTime(info.ID, provisionalTime) + return true, 0 /* no wait. proceed */, provisionalTime +} + +// UpdateConnectionTime updates the connection time for the given address. +func (ps *PeerStore) UpdateConnectionTime(addr string, provisionalTime time.Time) bool { + info, err := PeerInfoFromDomainPort(addr) + if err != nil { + return false + } + metadata, err := ps.Get(info.ID, addressDataKey) + if err != nil { + return false + } + ad, ok := metadata.(addressData) + if !ok { + return false + } + defer func() { + _ = ps.Put(info.ID, addressDataKey, ad) + + }() + + // Find the provisionalTime and update it + entry := ad.recentConnectionTimes + for indx, val := range entry { + if provisionalTime == val { + entry[indx] = time.Now() + return true + } + } + + // Case where the time is not found: it was removed from the list. + // This may happen when the time expires before the connection was established with the server. + // The time should be added again. + entry = append(entry, time.Now()) + ad.recentConnectionTimes = entry + + return true +} + +// ReplacePeerList replaces the peer list for the given networkName and role. +func (ps *PeerStore) ReplacePeerList(addressesThey []string, networkName string, role PhoneBookEntryRoles) { + // prepare a map of items we'd like to remove. + removeItems := make(map[peer.ID]bool, 0) + peerIDs := ps.Peers() + for _, pid := range peerIDs { + data, _ := ps.Get(pid, addressDataKey) + if data != nil { + ad := data.(addressData) + if ad.networkNames[networkName] && ad.role == role && !ad.persistent { + removeItems[pid] = true + } + } + + } + for _, addr := range addressesThey { + info, err := PeerInfoFromDomainPort(addr) + if err != nil { + return + } + data, _ := ps.Get(info.ID, addressDataKey) + if data != nil { + // we already have this. + // Update the networkName + ad := data.(addressData) + ad.networkNames[networkName] = true + + // do not remove this entry + delete(removeItems, info.ID) + } else { + // we don't have this item. add it. + ps.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) + entry := makePhonebookEntryData(networkName, role, false) + _ = ps.Put(info.ID, addressDataKey, entry) + } + } + + // remove items that were missing in addressesThey + for k := range removeItems { + ps.deletePhonebookEntry(k, networkName) + } +} + +// AddPersistentPeers stores addresses of peers which are persistent. +// i.e. they won't be replaced by ReplacePeerList calls +func (ps *PeerStore) AddPersistentPeers(dnsAddresses []string, networkName string, role PhoneBookEntryRoles) { + + for _, addr := range dnsAddresses { + info, err := PeerInfoFromDomainPort(addr) + if err != nil { + return + } + data, _ := ps.Get(info.ID, addressDataKey) + if data != nil { + // we already have this. + // Make sure the persistence field is set to true + ad := data.(addressData) + ad.persistent = true + _ = ps.Put(info.ID, addressDataKey, data) + + } else { + // we don't have this item. add it. + ps.AddAddrs(info.ID, info.Addrs, libp2p.PermanentAddrTTL) + entry := makePhonebookEntryData(networkName, role, true) + _ = ps.Put(info.ID, addressDataKey, entry) + } + } +} + +// Length returns the number of addrs in peerstore +func (ps *PeerStore) Length() int { + return len(ps.Peers()) +} + +// makePhonebookEntryData creates a new address entry for provided network name and role. +func makePhonebookEntryData(networkName string, role PhoneBookEntryRoles, persistent bool) addressData { + pbData := addressData{ + networkNames: make(map[string]bool), + recentConnectionTimes: make([]time.Time, 0), + role: role, + persistent: persistent, + } + pbData.networkNames[networkName] = true + return pbData +} + +func (ps *PeerStore) deletePhonebookEntry(peerID peer.ID, networkName string) { + data, err := ps.Get(peerID, addressDataKey) + if err != nil { + return + } + ad := data.(addressData) + delete(ad.networkNames, networkName) + if 0 == len(ad.networkNames) { + ps.ClearAddrs(peerID) + _ = ps.Put(peerID, addressDataKey, nil) + } +} + +// AppendTime adds the current time to recentConnectionTimes in +// addressData of addr +func (ps *PeerStore) appendTime(peerID peer.ID, t time.Time) { + data, _ := ps.Get(peerID, addressDataKey) + ad := data.(addressData) + ad.recentConnectionTimes = append(ad.recentConnectionTimes, t) + _ = ps.Put(peerID, addressDataKey, ad) +} + +// PopEarliestTime removes the earliest time from recentConnectionTimes in +// addressData for addr +// It is expected to be later than ConnectionsRateLimitingWindow +func (ps *PeerStore) popNElements(n int, peerID peer.ID) { + data, _ := ps.Get(peerID, addressDataKey) + ad := data.(addressData) + ad.recentConnectionTimes = ad.recentConnectionTimes[n:] + _ = ps.Put(peerID, addressDataKey, ad) +} + +func (ps *PeerStore) filterRetryTime(t time.Time, role PhoneBookEntryRoles) []string { + o := make([]string, 0, len(ps.Peers())) + for _, peerID := range ps.Peers() { + data, _ := ps.Get(peerID, addressDataKey) + if data != nil { + ad := data.(addressData) + if t.After(ad.retryAfter) && role == ad.role { + o = append(o, string(peerID)) + } + } + } + return o +} + +func shuffleSelect(set []string, n int) []string { + if n >= len(set) || n == getAllAddresses { + // return shuffled copy of everything + out := slices.Clone(set) + shuffleStrings(out) + return out + } + // Pick random indexes from the set + indexSample := make([]int, n) + for i := range indexSample { + indexSample[i] = rand.Intn(len(set)-i) + i + for oi, ois := range indexSample[:i] { + if ois == indexSample[i] { + indexSample[i] = oi + } + } + } + out := make([]string, n) + for i, index := range indexSample { + out[i] = set[index] + } + return out +} + +func shuffleStrings(set []string) { + rand.Shuffle(len(set), func(i, j int) { set[i], set[j] = set[j], set[i] }) +} diff --git a/network/p2p/peerstore/peerstore_test.go b/network/p2p/peerstore/peerstore_test.go index 2debcae255..4263564c34 100644 --- a/network/p2p/peerstore/peerstore_test.go +++ b/network/p2p/peerstore/peerstore_test.go @@ -19,7 +19,9 @@ package peerstore import ( "crypto/rand" "fmt" + "math" "testing" + "time" "github.com/algorand/go-algorand/test/partitiontest" libp2p_crypto "github.com/libp2p/go-libp2p/core/crypto" @@ -28,6 +30,13 @@ import ( "github.com/stretchr/testify/require" ) +// PhoneBookEntryRelayRole used for all the relays that are provided either via the algobootstrap SRV record +// or via a configuration file. +const PhoneBookEntryRelayRole = 1 + +// PhoneBookEntryArchiverRole used for all the archivers that are provided via the archive SRV record. +const PhoneBookEntryArchiverRole = 2 + func TestPeerstore(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -77,3 +86,342 @@ func TestPeerstore(t *testing.T) { require.Equal(t, 7, len(peers)) } + +func testPhonebookAll(t *testing.T, set []string, ph *PeerStore) { + actual := ph.GetAddresses(len(set), PhoneBookEntryRelayRole) + for _, got := range actual { + ok := false + for _, known := range set { + if got == known { + ok = true + break + } + } + if !ok { + t.Errorf("get returned junk %#v", got) + } + } + for _, known := range set { + ok := false + for _, got := range actual { + if got == known { + ok = true + break + } + } + if !ok { + t.Errorf("get missed %#v; actual=%#v; set=%#v", known, actual, set) + } + } +} + +func testPhonebookUniform(t *testing.T, set []string, ph *PeerStore, getsize int) { + uniformityTestLength := 250000 / len(set) + expected := (uniformityTestLength * getsize) / len(set) + counts := make(map[string]int) + for i := 0; i < len(set); i++ { + counts[set[i]] = 0 + } + for i := 0; i < uniformityTestLength; i++ { + actual := ph.GetAddresses(getsize, PhoneBookEntryRelayRole) + for _, xa := range actual { + if _, ok := counts[xa]; ok { + counts[xa]++ + } + } + } + min, max := math.MaxInt, 0 + for _, count := range counts { + if count > max { + max = count + } + if count < min { + min = count + } + } + // TODO: what's a good probability-theoretic threshold for good enough? + if max-min > (expected / 5) { + t.Errorf("counts %#v", counts) + } +} + +func TestArrayPhonebookAll(t *testing.T) { + partitiontest.PartitionTest(t) + + set := []string{"a:4041", "b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + for _, addr := range set { + entry := makePhonebookEntryData("", PhoneBookEntryRelayRole, false) + info, _ := PeerInfoFromDomainPort(addr) + ph.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) + ph.Put(info.ID, addressDataKey, entry) + } + testPhonebookAll(t, set, ph) +} + +func TestArrayPhonebookUniform1(t *testing.T) { + partitiontest.PartitionTest(t) + + set := []string{"a:4041", "b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + for _, addr := range set { + entry := makePhonebookEntryData("", PhoneBookEntryRelayRole, false) + info, _ := PeerInfoFromDomainPort(addr) + ph.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) + ph.Put(info.ID, addressDataKey, entry) + } + testPhonebookUniform(t, set, ph, 1) +} + +func TestArrayPhonebookUniform3(t *testing.T) { + partitiontest.PartitionTest(t) + + set := []string{"a:4041", "b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + for _, addr := range set { + entry := makePhonebookEntryData("", PhoneBookEntryRelayRole, false) + info, _ := PeerInfoFromDomainPort(addr) + ph.AddAddrs(info.ID, info.Addrs, libp2p.AddressTTL) + ph.Put(info.ID, addressDataKey, entry) + } + testPhonebookUniform(t, set, ph, 3) +} + +func TestMultiPhonebook(t *testing.T) { + partitiontest.PartitionTest(t) + + set := []string{"a:4041", "b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + pha := make([]string, 0) + for _, e := range set[:5] { + pha = append(pha, e) + } + phb := make([]string, 0) + for _, e := range set[5:] { + phb = append(phb, e) + } + + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + ph.ReplacePeerList(pha, "pha", PhoneBookEntryRelayRole) + ph.ReplacePeerList(phb, "phb", PhoneBookEntryRelayRole) + + testPhonebookAll(t, set, ph) + testPhonebookUniform(t, set, ph, 1) + testPhonebookUniform(t, set, ph, 3) +} + +// TestMultiPhonebookPersistentPeers validates that the peers added via Phonebook.AddPersistentPeers +// are not replaced when Phonebook.ReplacePeerList is called +func TestMultiPhonebookPersistentPeers(t *testing.T) { + partitiontest.PartitionTest(t) + + persistentPeers := []string{"a:4041"} + set := []string{"b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + pha := make([]string, 0) + for _, e := range set[:5] { + pha = append(pha, e) + } + phb := make([]string, 0) + for _, e := range set[5:] { + phb = append(phb, e) + } + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + ph.AddPersistentPeers(persistentPeers, "pha", PhoneBookEntryRelayRole) + ph.AddPersistentPeers(persistentPeers, "phb", PhoneBookEntryRelayRole) + ph.ReplacePeerList(pha, "pha", PhoneBookEntryRelayRole) + ph.ReplacePeerList(phb, "phb", PhoneBookEntryRelayRole) + + testPhonebookAll(t, append(set, persistentPeers...), ph) + allAddresses := ph.GetAddresses(len(set)+len(persistentPeers), PhoneBookEntryRelayRole) + for _, pp := range persistentPeers { + require.Contains(t, allAddresses, pp) + } +} + +func TestMultiPhonebookDuplicateFiltering(t *testing.T) { + partitiontest.PartitionTest(t) + + set := []string{"b:4042", "c:4043", "d:4044", "e:4045", "f:4046", "g:4047", "h:4048", "i:4049", "j:4010"} + pha := make([]string, 0) + for _, e := range set[:7] { + pha = append(pha, e) + } + phb := make([]string, 0) + for _, e := range set[3:] { + phb = append(phb, e) + } + ph, err := MakePhonebook(1, 1*time.Millisecond) + require.NoError(t, err) + ph.ReplacePeerList(pha, "pha", PhoneBookEntryRelayRole) + ph.ReplacePeerList(phb, "phb", PhoneBookEntryRelayRole) + + testPhonebookAll(t, set, ph) + testPhonebookUniform(t, set, ph, 1) + testPhonebookUniform(t, set, ph, 3) +} + +func TestWaitAndAddConnectionTimeLongtWindow(t *testing.T) { + partitiontest.PartitionTest(t) + + // make the connectionsRateLimitingWindow long enough to avoid triggering it when the + // test is running in a slow environment + // The test will artificially simulate time passing + timeUnit := 2000 * time.Second + connectionsRateLimitingWindow := 2 * timeUnit + entries, err := MakePhonebook(3, connectionsRateLimitingWindow) + require.NoError(t, err) + addr1 := "addrABC:4040" + addr2 := "addrXYZ:4041" + info1, _ := PeerInfoFromDomainPort(addr1) + info2, _ := PeerInfoFromDomainPort(addr2) + + // Address not in. Should return false + addrInPhonebook, _, provisionalTime := entries.GetConnectionWaitTime(addr1) + require.Equal(t, false, addrInPhonebook) + require.Equal(t, false, entries.UpdateConnectionTime(addr1, provisionalTime)) + + // Test the addresses are populated in the phonebook and a + // time can be added to one of them + entries.ReplacePeerList([]string{addr1, addr2}, "default", PhoneBookEntryRelayRole) + addrInPhonebook, waitTime, provisionalTime := entries.GetConnectionWaitTime(addr1) + require.Equal(t, true, addrInPhonebook) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr1, provisionalTime)) + data, _ := entries.Get(info1.ID, addressDataKey) + require.NotNil(t, data) + ad := data.(addressData) + phBookData := ad.recentConnectionTimes + require.Equal(t, 1, len(phBookData)) + + // simulate passing a unit of time + for rct := range phBookData { + phBookData[rct] = phBookData[rct].Add(-1 * timeUnit) + } + + // add another value to addr + addrInPhonebook, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr1) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr1, provisionalTime)) + data, _ = entries.Get(info1.ID, addressDataKey) + ad = data.(addressData) + phBookData = ad.recentConnectionTimes + require.Equal(t, 2, len(phBookData)) + + // simulate passing a unit of time + for rct := range phBookData { + phBookData[rct] = phBookData[rct].Add(-1 * timeUnit) + } + + // the first time should be removed and a new one added + // there should not be any wait + addrInPhonebook, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr1) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr1, provisionalTime)) + data, _ = entries.Get(info1.ID, addressDataKey) + ad = data.(addressData) + phBookData2 := ad.recentConnectionTimes + require.Equal(t, 2, len(phBookData2)) + + // make sure the right time was removed + require.Equal(t, phBookData[1], phBookData2[0]) + require.Equal(t, true, phBookData2[0].Before(phBookData2[1])) + + // try requesting from another address, make sure + // a separate array is used for these new requests + + // add 3 values to another address. should not wait + // value 1 + _, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr2) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr2, provisionalTime)) + + // introduce a gap between the two requests so that only the first will be removed later when waited + // simulate passing a unit of time + data2, _ := entries.Get(info2.ID, addressDataKey) + require.NotNil(t, data2) + ad2 := data2.(addressData) + for rct := range ad2.recentConnectionTimes { + ad2.recentConnectionTimes[rct] = ad2.recentConnectionTimes[rct].Add(-1 * timeUnit) + } + + // value 2 + _, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr2) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr2, provisionalTime)) + // value 3 + _, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr2) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr2, provisionalTime)) + + data2, _ = entries.Get(info2.ID, addressDataKey) + ad2 = data2.(addressData) + phBookData = ad2.recentConnectionTimes + // all three times should be queued + require.Equal(t, 3, len(phBookData)) + + // add another element to trigger wait + _, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr2) + require.Greater(t, int64(waitTime), int64(0)) + // no element should be removed + data2, _ = entries.Get(info2.ID, addressDataKey) + ad2 = data2.(addressData) + phBookData2 = ad2.recentConnectionTimes + require.Equal(t, phBookData[0], phBookData2[0]) + require.Equal(t, phBookData[1], phBookData2[1]) + require.Equal(t, phBookData[2], phBookData2[2]) + // simulate passing of the waitTime duration + for rct := range ad2.recentConnectionTimes { + ad2.recentConnectionTimes[rct] = ad2.recentConnectionTimes[rct].Add(-1 * waitTime) + } + + // The wait should be sufficient + _, waitTime, provisionalTime = entries.GetConnectionWaitTime(addr2) + require.Equal(t, time.Duration(0), waitTime) + require.Equal(t, true, entries.UpdateConnectionTime(addr2, provisionalTime)) + // only one element should be removed, and one added + data2, _ = entries.Get(info2.ID, addressDataKey) + ad2 = data2.(addressData) + phBookData2 = ad2.recentConnectionTimes + require.Equal(t, 3, len(phBookData2)) + + // make sure the right time was removed + require.Equal(t, phBookData[1], phBookData2[0]) + require.Equal(t, phBookData[2], phBookData2[1]) +} + +// TestPhonebookRoles tests that the filtering by roles for different +// phonebooks entries works as expected. +func TestPhonebookRoles(t *testing.T) { + partitiontest.PartitionTest(t) + + relaysSet := []string{"relay1:4040", "relay2:4041", "relay3:4042"} + archiverSet := []string{"archiver1:1111", "archiver2:1112", "archiver3:1113"} + + ph, err := MakePhonebook(1, 1) + require.NoError(t, err) + ph.ReplacePeerList(relaysSet, "default", PhoneBookEntryRelayRole) + ph.ReplacePeerList(archiverSet, "default", PhoneBookEntryArchiverRole) + require.Equal(t, len(relaysSet)+len(archiverSet), len(ph.Peers())) + require.Equal(t, len(relaysSet)+len(archiverSet), ph.Length()) + + for _, role := range []PhoneBookEntryRoles{PhoneBookEntryRelayRole, PhoneBookEntryArchiverRole} { + for k := 0; k < 100; k++ { + for l := 0; l < 3; l++ { + entries := ph.GetAddresses(l, role) + if role == PhoneBookEntryRelayRole { + for _, entry := range entries { + require.Contains(t, entry, "relay") + } + } else if role == PhoneBookEntryArchiverRole { + for _, entry := range entries { + require.Contains(t, entry, "archiver") + } + } + } + } + } +} diff --git a/network/p2p/peerstore/utils.go b/network/p2p/peerstore/utils.go index eabcccbdae..b96fc1c8e0 100644 --- a/network/p2p/peerstore/utils.go +++ b/network/p2p/peerstore/utils.go @@ -17,6 +17,9 @@ package peerstore import ( + "fmt" + "strings" + "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" ) @@ -49,3 +52,18 @@ func PeerInfoFromAddr(addr string) (*peer.AddrInfo, error) { } return info, nil } + +// PeerInfoFromDomainPort converts a string of the form domain:port to AddrInfo +func PeerInfoFromDomainPort(domainPort string) (*peer.AddrInfo, error) { + parts := strings.Split(domainPort, ":") + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return nil, fmt.Errorf("invalid domain port string %s, found %d colon-separated parts", domainPort, len(parts)) + } + maddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dns4/%s/tcp/%s", parts[0], parts[1])) + if err != nil { + return nil, err + } + // These will never have peer IDs + transport, _ := peer.SplitAddr(maddr) + return &peer.AddrInfo{ID: peer.ID(domainPort), Addrs: []multiaddr.Multiaddr{transport}}, nil +} diff --git a/network/p2p/pubsub.go b/network/p2p/pubsub.go new file mode 100644 index 0000000000..220320d726 --- /dev/null +++ b/network/p2p/pubsub.go @@ -0,0 +1,160 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package p2p + +import ( + "context" + "time" + + "github.com/algorand/go-algorand/config" + pubsub "github.com/libp2p/go-libp2p-pubsub" + pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + "golang.org/x/crypto/blake2b" +) + +func init() { + // configure larger overlay parameters + pubsub.GossipSubD = 8 + pubsub.GossipSubDscore = 6 + pubsub.GossipSubDout = 3 + pubsub.GossipSubDlo = 6 + pubsub.GossipSubDhi = 12 + pubsub.GossipSubDlazy = 12 + pubsub.GossipSubDirectConnectInitialDelay = 30 * time.Second + pubsub.GossipSubIWantFollowupTime = 5 * time.Second + pubsub.GossipSubHistoryLength = 10 + pubsub.GossipSubGossipFactor = 0.1 +} + +const ( + gossipScoreThreshold = -500 + publishScoreThreshold = -1000 + graylistScoreThreshold = -2500 + acceptPXScoreThreshold = 1000 + opportunisticGraftScoreThreshold = 3.5 +) + +// TXTopicName defines a pubsub topic for TX messages +const TXTopicName = "/algo/tx/0.1.0" + +func makePubSub(ctx context.Context, cfg config.Local, host host.Host) (*pubsub.PubSub, error) { + //defaultParams := pubsub.DefaultGossipSubParams() + + options := []pubsub.Option{ + pubsub.WithPeerScore(&pubsub.PeerScoreParams{ + DecayInterval: pubsub.DefaultDecayInterval, + DecayToZero: pubsub.DefaultDecayToZero, + + AppSpecificScore: func(p peer.ID) float64 { return 1000 }, + + Topics: map[string]*pubsub.TopicScoreParams{ + TXTopicName: { + TopicWeight: 0.1, + + TimeInMeshWeight: 0.0002778, // ~1/3600 + TimeInMeshQuantum: time.Second, + TimeInMeshCap: 1, + + FirstMessageDeliveriesWeight: 0.5, // max value is 50 + FirstMessageDeliveriesDecay: pubsub.ScoreParameterDecay(10 * time.Minute), + FirstMessageDeliveriesCap: 100, // 100 messages in 10 minutes + + // invalid messages decay after 1 hour + InvalidMessageDeliveriesWeight: -1000, + InvalidMessageDeliveriesDecay: pubsub.ScoreParameterDecay(time.Hour), + }, + }, + }, + &pubsub.PeerScoreThresholds{ + GossipThreshold: gossipScoreThreshold, + PublishThreshold: publishScoreThreshold, + GraylistThreshold: graylistScoreThreshold, + AcceptPXThreshold: acceptPXScoreThreshold, + OpportunisticGraftThreshold: opportunisticGraftScoreThreshold, + }, + ), + // pubsub.WithPeerGater(&pubsub.PeerGaterParams{}), + pubsub.WithSubscriptionFilter(pubsub.WrapLimitSubscriptionFilter(pubsub.NewAllowlistSubscriptionFilter(TXTopicName), 100)), + // pubsub.WithEventTracer(jsonTracer), + pubsub.WithValidateQueueSize(256), + // pubsub.WithValidateThrottle(cfg.TxBacklogSize), + } + + return pubsub.NewGossipSub(ctx, host, options...) +} + +func txMsgID(m *pubsub_pb.Message) string { + h := blake2b.Sum256(m.Data) + return string(h[:]) +} + +// getOrCreateTopic returns a topic if it was already joined previously and otherwise creates it and adds it to the topics map +func (s *serviceImpl) getOrCreateTopic(topicName string) (*pubsub.Topic, error) { + s.topicsMu.RLock() + topic, ok := s.topics[topicName] + s.topicsMu.RUnlock() + if ok { + return topic, nil + } + + s.topicsMu.Lock() + defer s.topicsMu.Unlock() + // check again in case it was created while we were waiting for the lock + if _, ok := s.topics[topicName]; !ok { + var topt []pubsub.TopicOpt + switch topicName { + case TXTopicName: + topt = append(topt, pubsub.WithTopicMessageIdFn(txMsgID)) + } + + psTopic, err := s.pubsub.Join(topicName, topt...) + if err != nil { + return nil, err + } + s.topics[topicName] = psTopic + } + return s.topics[topicName], nil +} + +// Subscribe returns a subscription to the given topic +func (s *serviceImpl) Subscribe(topic string, val pubsub.ValidatorEx) (*pubsub.Subscription, error) { + if err := s.pubsub.RegisterTopicValidator(topic, val); err != nil { + return nil, err + } + t, err := s.getOrCreateTopic(topic) + if err != nil { + return nil, err + } + // t.SetScoreParams() // already set in makePubSub + return t.Subscribe() +} + +// Publish publishes data to the given topic +func (s *serviceImpl) Publish(ctx context.Context, topic string, data []byte) error { + t, err := s.getOrCreateTopic(topic) + if err != nil { + return err + } + return t.Publish(ctx, data) +} + +// ListPeersForTopic returns a list of peers subscribed to the given topic, exported for access from the network package +func (s *serviceImpl) ListPeersForTopic(topic string) []peer.ID { + return s.pubsub.ListPeers(topic) +} diff --git a/network/p2p/streams.go b/network/p2p/streams.go new file mode 100644 index 0000000000..b6fad35d94 --- /dev/null +++ b/network/p2p/streams.go @@ -0,0 +1,131 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package p2p + +import ( + "context" + "io" + + "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-deadlock" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multiaddr" +) + +// streamManager implements network.Notifiee to create and manage streams for use with non-gossipsub protocols. +type streamManager struct { + ctx context.Context + log logging.Logger + host host.Host + handler StreamHandler + + streams map[peer.ID]network.Stream + streamsLock deadlock.Mutex +} + +// StreamHandler is called when a new bidirectional stream for a given protocol and peer is opened. +type StreamHandler func(ctx context.Context, pid peer.ID, s network.Stream, incoming bool) + +func makeStreamManager(ctx context.Context, log logging.Logger, h host.Host, handler StreamHandler) *streamManager { + return &streamManager{ + ctx: ctx, + log: log, + host: h, + handler: handler, + streams: make(map[peer.ID]network.Stream), + } +} + +// streamHandler is called by libp2p when a new stream is accepted +func (n *streamManager) streamHandler(stream network.Stream) { + n.streamsLock.Lock() + defer n.streamsLock.Unlock() + + // could use stream.ID() for tracking; unique across all conns and peers + remotePeer := stream.Conn().RemotePeer() + + if oldStream, ok := n.streams[remotePeer]; ok { + // there's already a stream, for some reason, check if it's still open + buf := []byte{} // empty buffer for checking + _, err := oldStream.Read(buf) + if err != nil { + if err == io.EOF { + // old stream was closed by the peer + n.log.Infof("Old stream with %s was closed", remotePeer) + } else { + // an error occurred while checking the old stream + n.log.Infof("Failed to check old stream with %s: %v", remotePeer, err) + } + n.streams[stream.Conn().RemotePeer()] = stream + n.handler(n.ctx, remotePeer, stream, true) + return + } + // otherwise, the old stream is still open, so we can close the new one + stream.Close() + return + } + // no old stream + n.streams[stream.Conn().RemotePeer()] = stream + n.handler(n.ctx, remotePeer, stream, true) +} + +// Connected is called when a connection is opened +func (n *streamManager) Connected(net network.Network, conn network.Conn) { + remotePeer := conn.RemotePeer() + localPeer := n.host.ID() + + // ensure that only one of the peers initiates the stream + if localPeer > remotePeer { + return + } + + n.streamsLock.Lock() + defer n.streamsLock.Unlock() + _, ok := n.streams[remotePeer] + if ok { + return // there's already an active stream with this peer for our protocol + } + + stream, err := n.host.NewStream(n.ctx, remotePeer, AlgorandWsProtocol) + if err != nil { + n.log.Infof("Failed to open stream to %s: %v", remotePeer, err) + return + } + + n.streams[remotePeer] = stream + n.handler(n.ctx, remotePeer, stream, false) +} + +// Disconnected is called when a connection is closed +func (n *streamManager) Disconnected(net network.Network, conn network.Conn) { + n.streamsLock.Lock() + defer n.streamsLock.Unlock() + + stream, ok := n.streams[conn.RemotePeer()] + if ok { + stream.Close() + delete(n.streams, conn.RemotePeer()) + } +} + +// Listen is called when network starts listening on an addr +func (n *streamManager) Listen(net network.Network, addr multiaddr.Multiaddr) {} + +// ListenClose is called when network stops listening on an addr +func (n *streamManager) ListenClose(net network.Network, addr multiaddr.Multiaddr) {} diff --git a/network/p2pNetwork.go b/network/p2pNetwork.go new file mode 100644 index 0000000000..f36b0d3280 --- /dev/null +++ b/network/p2pNetwork.go @@ -0,0 +1,475 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "context" + "net" + "net/http" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/network/p2p" + "github.com/algorand/go-algorand/network/p2p/peerstore" + "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-deadlock" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + manet "github.com/multiformats/go-multiaddr/net" +) + +// P2PNetwork implements the GossipNode interface +type P2PNetwork struct { + service p2p.Service + log logging.Logger + config config.Local + genesisID string + networkID protocol.NetworkID + ctx context.Context + ctxCancel context.CancelFunc + peerStats map[peer.ID]*p2pPeerStats + peerStatsMu deadlock.Mutex + + wg sync.WaitGroup + + // which tags to use with libp2p's GossipSub, mapped to topic names + topicTags map[protocol.Tag]string + + // websockets message support + handler msgHandler + broadcaster msgBroadcaster + wsPeers map[peer.ID]*wsPeer + wsPeersLock deadlock.RWMutex + wsPeersChangeCounter int32 + wsPeersConnectivityCheckTicker *time.Ticker +} + +type p2pPeerStats struct { + txReceived uint64 +} + +// NewP2PNetwork returns an instance of GossipNode that uses the p2p.Service +func NewP2PNetwork(log logging.Logger, cfg config.Local, datadir string, phonebookAddresses []string, genesisID string, networkID protocol.NetworkID) (*P2PNetwork, error) { + const readBufferLen = 2048 + + // create Peerstore and add phonebook addresses + addrInfo, malformedAddrs := peerstore.PeerInfoFromAddrs(phonebookAddresses) + for malAddr, malErr := range malformedAddrs { + log.Infof("Ignoring malformed phonebook address %s: %s", malAddr, malErr) + } + pstore, err := peerstore.NewPeerStore(addrInfo) + if err != nil { + return nil, err + } + + net := &P2PNetwork{ + log: log, + config: cfg, + genesisID: genesisID, + networkID: networkID, + topicTags: map[protocol.Tag]string{"TX": p2p.TXTopicName}, + wsPeers: make(map[peer.ID]*wsPeer), + peerStats: make(map[peer.ID]*p2pPeerStats), + } + net.ctx, net.ctxCancel = context.WithCancel(context.Background()) + net.handler = msgHandler{ + ctx: net.ctx, + log: log, + config: cfg, + readBuffer: make(chan IncomingMessage, readBufferLen), + } + net.broadcaster = msgBroadcaster{ + ctx: net.ctx, + log: log, + config: cfg, + broadcastQueueHighPrio: make(chan broadcastRequest, outgoingMessagesBufferSize), + broadcastQueueBulk: make(chan broadcastRequest, 100), + } + + net.service, err = p2p.MakeService(net.ctx, log, cfg, datadir, pstore, net.wsStreamHandler) + if err != nil { + return nil, err + } + + err = net.setup() + if err != nil { + return nil, err + } + + return net, nil +} + +func (n *P2PNetwork) setup() error { + if n.broadcaster.slowWritingPeerMonitorInterval == 0 { + n.broadcaster.slowWritingPeerMonitorInterval = slowWritingPeerMonitorInterval + } + return nil +} + +// Start threads, listen on sockets. +func (n *P2PNetwork) Start() { + n.wg.Add(1) + go n.txTopicHandleLoop() + + if n.wsPeersConnectivityCheckTicker != nil { + n.wsPeersConnectivityCheckTicker.Stop() + } + n.wsPeersConnectivityCheckTicker = time.NewTicker(connectionActivityMonitorInterval) + for i := 0; i < incomingThreads; i++ { + n.wg.Add(1) + // We pass the peersConnectivityCheckTicker.C here so that we don't need to syncronize the access to the ticker's data structure. + go n.handler.messageHandlerThread(&n.wg, n.wsPeersConnectivityCheckTicker.C, n) + } + + n.wg.Add(1) + go n.broadcaster.broadcastThread(&n.wg, n) + n.service.DialPeersUntilTargetCount(n.config.GossipFanout) + + n.wg.Add(1) + go n.meshThread() +} + +// Stop closes sockets and stop threads. +func (n *P2PNetwork) Stop() { + n.handler.ClearHandlers([]Tag{}) + if n.wsPeersConnectivityCheckTicker != nil { + n.wsPeersConnectivityCheckTicker.Stop() + n.wsPeersConnectivityCheckTicker = nil + } + n.innerStop() + n.ctxCancel() + n.service.Close() + n.wg.Wait() +} + +// innerStop context for shutting down peers +func (n *P2PNetwork) innerStop() { + closeGroup := sync.WaitGroup{} + n.wsPeersLock.Lock() + closeGroup.Add(len(n.wsPeers)) + deadline := time.Now().Add(peerDisconnectionAckDuration) + for peerID, peer := range n.wsPeers { + // we need to both close the wsPeer and close the p2p connection + go closeWaiter(&closeGroup, peer, deadline) + err := n.service.ClosePeer(peerID) + if err != nil { + n.log.Warnf("Error closing peer %s: %v", peerID, err) + } + delete(n.wsPeers, peerID) + } + n.wsPeersLock.Unlock() + closeGroup.Wait() +} + +func (n *P2PNetwork) meshThread() { + defer n.wg.Done() + timer := time.NewTicker(meshThreadInterval) + defer timer.Stop() + for { + select { + case <-timer.C: + n.service.DialPeersUntilTargetCount(n.config.GossipFanout) + case <-n.ctx.Done(): + return + } + } +} + +// GetGenesisID implements GossipNode +func (n *P2PNetwork) GetGenesisID() string { + return n.genesisID +} + +// Address returns a string and whether that is a 'final' address or guessed. +func (n *P2PNetwork) Address() (string, bool) { + addrInfo := n.service.AddrInfo() + if len(addrInfo.Addrs) == 0 { + return "", false + } + addrs, err := peer.AddrInfoToP2pAddrs(&addrInfo) + if err != nil { + n.log.Warnf("Failed to generate valid multiaddr: %v", err) + return "", false + } + // loop through and see if we have a non loopback address available + for _, addr := range addrs { + if !manet.IsIPLoopback(addr) && !manet.IsIPUnspecified(addr) { + return addr.String(), true + + } + } + // We don't have a non loopback address, so just return the first one if it contains an ip4 address or port + addr := addrs[0].String() + if strings.Contains(addr, "/ip4/") && strings.Contains(addr, "/tcp/") { + return addr, true + + } + return "", false + +} + +// Broadcast sends a message. +func (n *P2PNetwork) Broadcast(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error { + // For tags using pubsub topics, publish to GossipSub + if topic, ok := n.topicTags[tag]; ok { + return n.service.Publish(ctx, topic, data) + } + // Otherwise broadcast over websocket protocol stream + return n.broadcaster.BroadcastArray(ctx, []protocol.Tag{tag}, [][]byte{data}, wait, except) +} + +// Relay message +func (n *P2PNetwork) Relay(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error { + return n.Broadcast(ctx, tag, data, wait, except) +} + +// Disconnect from a peer, probably due to protocol errors. +func (n *P2PNetwork) Disconnect(badnode Peer) { + node, ok := badnode.(peer.ID) + if !ok { + n.log.Warnf("Unknown peer type %T", badnode) + return + } + n.wsPeersLock.Lock() + defer n.wsPeersLock.Unlock() + if wsPeer, ok := n.wsPeers[node]; ok { + wsPeer.CloseAndWait(time.Now().Add(peerDisconnectionAckDuration)) + delete(n.wsPeers, node) + } else { + n.log.Warnf("Could not find wsPeer reference for peer %s", node) + } + err := n.service.ClosePeer(node) + if err != nil { + n.log.Warnf("Error disconnecting from peer %s: %v", node, err) + } +} + +func (n *P2PNetwork) disconnectThread(badnode Peer, reason disconnectReason) { + defer n.wg.Done() + n.Disconnect(badnode) // ignores reason +} + +// DisconnectPeers is used by testing +func (n *P2PNetwork) DisconnectPeers() { + for _, conn := range n.service.Conns() { + conn.Close() + } +} + +// RegisterHTTPHandler path accepts gorilla/mux path annotations +func (n *P2PNetwork) RegisterHTTPHandler(path string, handler http.Handler) { +} + +// RequestConnectOutgoing asks the system to actually connect to peers. +// `replace` optionally drops existing connections before making new ones. +// `quit` chan allows cancellation. +func (n *P2PNetwork) RequestConnectOutgoing(replace bool, quit <-chan struct{}) { +} + +// GetPeers returns a list of Peers we could potentially send a direct message to. +func (n *P2PNetwork) GetPeers(options ...PeerOption) []Peer { + // currently returns same list of peers for all PeerOption filters. + peers := make([]Peer, 0) + n.wsPeersLock.RLock() + for _, peer := range n.wsPeers { + peers = append(peers, Peer(peer)) + } + n.wsPeersLock.RUnlock() + return peers +} + +// RegisterHandlers adds to the set of given message handlers. +func (n *P2PNetwork) RegisterHandlers(dispatch []TaggedMessageHandler) { + n.handler.RegisterHandlers(dispatch) +} + +// ClearHandlers deregisters all the existing message handlers. +func (n *P2PNetwork) ClearHandlers() { + n.handler.ClearHandlers([]Tag{}) +} + +// GetRoundTripper returns a Transport that would limit the number of outgoing connections. +func (n *P2PNetwork) GetRoundTripper() http.RoundTripper { + return http.DefaultTransport +} + +// OnNetworkAdvance notifies the network library that the agreement protocol was able to make a notable progress. +// this is the only indication that we have that we haven't formed a clique, where all incoming messages +// arrive very quickly, but might be missing some votes. The usage of this call is expected to have similar +// characteristics as with a watchdog timer. +func (n *P2PNetwork) OnNetworkAdvance() {} + +// GetHTTPRequestConnection returns the underlying connection for the given request. Note that the request must be the same +// request that was provided to the http handler ( or provide a fallback Context() to that ) +func (n *P2PNetwork) GetHTTPRequestConnection(request *http.Request) (conn net.Conn) { return nil } + +// SubstituteGenesisID substitutes the "{genesisID}" with their network-specific genesisID. +func (n *P2PNetwork) SubstituteGenesisID(rawURL string) string { + return strings.Replace(rawURL, "{genesisID}", n.genesisID, -1) +} + +// wsStreamHandler is a callback that the p2p package calls when a new peer connects and establishes a +// stream for the websocket protocol. +func (n *P2PNetwork) wsStreamHandler(ctx context.Context, peer peer.ID, stream network.Stream, incoming bool) { + if stream.Protocol() != p2p.AlgorandWsProtocol { + n.log.Warnf("unknown protocol %s", stream.Protocol()) + return + } + + if incoming { + var initMsg [1]byte + rn, err := stream.Read(initMsg[:]) + if rn == 0 || err != nil { + n.log.Warnf("wsStreamHandler: error reading initial message: %s", err) + return + } + } else { + _, err := stream.Write([]byte("1")) + if err != nil { + n.log.Warnf("wsStreamHandler: error sending initial message: %s", err) + return + } + } + + // get address for peer ID + addr := stream.Conn().RemoteMultiaddr().String() + if addr == "" { + n.log.Warnf("Could not get address for peer %s", peer) + } + // create a wsPeer for this stream and added it to the peers map. + wsp := &wsPeer{ + wsPeerCore: makePeerCore(ctx, n, n.log, n.handler.readBuffer, addr, n.GetRoundTripper(), addr), + conn: &wsPeerConnP2PImpl{stream: stream}, + outgoing: !incoming, + } + wsp.init(n.config, outgoingMessagesBufferSize) + n.wsPeersLock.Lock() + n.wsPeers[peer] = wsp + n.wsPeersLock.Unlock() + atomic.AddInt32(&n.wsPeersChangeCounter, 1) +} + +// peerRemoteClose called from wsPeer to report that it has closed +func (n *P2PNetwork) peerRemoteClose(peer *wsPeer, reason disconnectReason) { + remotePeerID := peer.conn.(*wsPeerConnP2PImpl).stream.Conn().RemotePeer() + n.wsPeersLock.Lock() + delete(n.wsPeers, remotePeerID) + n.wsPeersLock.Unlock() + atomic.AddInt32(&n.wsPeersChangeCounter, 1) +} + +func (n *P2PNetwork) peerSnapshot(dest []*wsPeer) ([]*wsPeer, int32) { + n.wsPeersLock.RLock() + defer n.wsPeersLock.RUnlock() + // based on wn.peerSnapshot + if cap(dest) >= len(n.wsPeers) { + toClear := dest[len(n.wsPeers):cap(dest)] + for i := range toClear { + if toClear[i] == nil { + break + } + toClear[i] = nil + } + dest = dest[:len(n.wsPeers)] + } else { + dest = make([]*wsPeer, len(n.wsPeers)) + } + i := 0 + for _, p := range n.wsPeers { + dest[i] = p + i++ + } + return dest, n.getPeersChangeCounter() +} + +func (n *P2PNetwork) getPeersChangeCounter() int32 { + return atomic.LoadInt32(&n.wsPeersChangeCounter) +} + +func (n *P2PNetwork) checkSlowWritingPeers() {} +func (n *P2PNetwork) checkPeersConnectivity() {} + +// txTopicHandleLoop reads messages from the pubsub topic for transactions. +func (n *P2PNetwork) txTopicHandleLoop() { + defer n.wg.Done() + sub, err := n.service.Subscribe(p2p.TXTopicName, n.txTopicValidator) + if err != nil { + n.log.Errorf("Failed to subscribe to topic %s: %v", p2p.TXTopicName, err) + return + } + + for { + msg, err := sub.Next(n.ctx) + if err != nil { + if err != pubsub.ErrSubscriptionCancelled && err != context.Canceled { + n.log.Errorf("Error reading from subscription %v, peerId %s", err, n.service.ID()) + } + sub.Cancel() + return + } + + // discard TX message. + // from gossipsub's point of view, it's just waiting to hear back from the validator, + // and txHandler does all its work in the validator, so we don't need to do anything here + _ = msg + } +} + +// txTopicValidator calls txHandler to validate and process incoming transactions. +func (n *P2PNetwork) txTopicValidator(ctx context.Context, peerID peer.ID, msg *pubsub.Message) pubsub.ValidationResult { + inmsg := IncomingMessage{ + Sender: msg.ReceivedFrom, + Tag: protocol.TxnTag, + Data: msg.Data, + Net: n, + Received: time.Now().UnixNano(), + } + + // if we sent the message, don't validate it + if inmsg.Sender == n.service.ID() { + return pubsub.ValidationAccept + } + + n.peerStatsMu.Lock() + peerStats, ok := n.peerStats[peerID] + if !ok { + n.peerStats[peerID] = &p2pPeerStats{txReceived: 1} + } else { + peerStats.txReceived++ + } + n.peerStatsMu.Unlock() + + outmsg := n.handler.Handle(inmsg) + // there was a decision made in the handler about this message + switch outmsg.Action { + case Ignore: + return pubsub.ValidationIgnore + case Disconnect: + return pubsub.ValidationReject + case Broadcast: // TxHandler.processIncomingTxn does not currently return this Action + return pubsub.ValidationAccept + default: + n.log.Warnf("handler returned invalid action %d", outmsg.Action) + return pubsub.ValidationIgnore + } +} diff --git a/network/p2pNetwork_test.go b/network/p2pNetwork_test.go new file mode 100644 index 0000000000..585d9aaa17 --- /dev/null +++ b/network/p2pNetwork_test.go @@ -0,0 +1,306 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "context" + "fmt" + "sync/atomic" + "testing" + "time" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/logging" + "github.com/algorand/go-algorand/network/p2p" + "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/partitiontest" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + peerstore "github.com/libp2p/go-libp2p/core/peer" + ma "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" +) + +func TestP2PSubmitTX(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := config.GetDefaultLocal() + log := logging.TestingLog(t) + netA, err := NewP2PNetwork(log, cfg, "", nil, genesisID, config.Devtestnet) + require.NoError(t, err) + peerInfoA := netA.service.AddrInfo() + + addrsA, err := peerstore.AddrInfoToP2pAddrs(&peerInfoA) + require.NoError(t, err) + require.NotZero(t, addrsA[0]) + netA.Start() + defer netA.Stop() + + multiAddrStr := addrsA[0].String() + phoneBookAddresses := []string{multiAddrStr} + netB, err := NewP2PNetwork(log, cfg, "", phoneBookAddresses, genesisID, config.Devtestnet) + require.NoError(t, err) + netB.Start() + defer netB.Stop() + + netC, err := NewP2PNetwork(log, cfg, "", phoneBookAddresses, genesisID, config.Devtestnet) + + require.NoError(t, err) + netC.Start() + defer netC.Stop() + + require.Eventually( + t, + func() bool { + return len(netA.service.ListPeersForTopic(p2p.TXTopicName)) == 2 && + len(netB.service.ListPeersForTopic(p2p.TXTopicName)) == 1 && + len(netC.service.ListPeersForTopic(p2p.TXTopicName)) == 1 + }, + 2*time.Second, + 50*time.Millisecond, + ) + time.Sleep(time.Second) // give time for peers to connect. + // now we should be connected in a line: B <-> A <-> C where both B and C are connected to A but not each other + + // Since we aren't using the transaction handler in this test, we need to register a pass-through handler + passThroughHandler := []TaggedMessageHandler{ + {Tag: protocol.TxnTag, MessageHandler: HandlerFunc(func(msg IncomingMessage) OutgoingMessage { + return OutgoingMessage{Action: Broadcast} + })}, + } + + netA.RegisterHandlers(passThroughHandler) + netB.RegisterHandlers(passThroughHandler) + netC.RegisterHandlers(passThroughHandler) + + // send messages from B and confirm that they get received by C (via A) + for i := 0; i < 10; i++ { + err = netB.Broadcast(context.Background(), protocol.TxnTag, []byte(fmt.Sprintf("hello %d", i)), false, nil) + require.NoError(t, err) + } + + require.Eventually( + t, + func() bool { + netC.peerStatsMu.Lock() + netCpeerStatsA, ok := netC.peerStats[netA.service.ID()] + netC.peerStatsMu.Unlock() + if !ok { + return false + } + return atomic.LoadUint64(&netCpeerStatsA.txReceived) == 10 + }, + 1*time.Second, + 50*time.Millisecond, + ) +} + +func TestP2PSubmitWS(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := config.GetDefaultLocal() + log := logging.TestingLog(t) + netA, err := NewP2PNetwork(log, cfg, "", nil, genesisID, config.Devtestnet) + require.NoError(t, err) + + peerInfoA := netA.service.AddrInfo() + addrsA, err := peerstore.AddrInfoToP2pAddrs(&peerInfoA) + require.NoError(t, err) + require.NotZero(t, addrsA[0]) + netA.Start() + defer netA.Stop() + + multiAddrStr := addrsA[0].String() + phoneBookAddresses := []string{multiAddrStr} + netB, err := NewP2PNetwork(log, cfg, "", phoneBookAddresses, genesisID, config.Devtestnet) + require.NoError(t, err) + netB.Start() + defer netB.Stop() + + netC, err := NewP2PNetwork(log, cfg, "", phoneBookAddresses, genesisID, config.Devtestnet) + + require.NoError(t, err) + netC.Start() + defer netC.Stop() + + require.Eventually( + t, + func() bool { + return len(netA.service.ListPeersForTopic(p2p.TXTopicName)) == 2 && + len(netB.service.ListPeersForTopic(p2p.TXTopicName)) == 1 && + len(netC.service.ListPeersForTopic(p2p.TXTopicName)) == 1 + }, + 2*time.Second, + 50*time.Millisecond, + ) + time.Sleep(time.Second) // XX give time for peers to connect. Knowing about them being subscribed to topics is clearly not enough + // now we should be connected in a line: B <-> A <-> C where both B and C are connected to A but not each other + + testTag := protocol.AgreementVoteTag + var handlerCount uint32 + + // Since we aren't using the transaction handler in this test, we need to register a pass-through handler + passThroughHandler := []TaggedMessageHandler{ + {Tag: testTag, MessageHandler: HandlerFunc(func(msg IncomingMessage) OutgoingMessage { + atomic.AddUint32(&handlerCount, 1) + return OutgoingMessage{Action: Broadcast} + })}, + } + + netA.RegisterHandlers(passThroughHandler) + netB.RegisterHandlers(passThroughHandler) + netC.RegisterHandlers(passThroughHandler) + + // send messages from B and confirm that they get received by C (via A) + for i := 0; i < 10; i++ { + err = netB.Broadcast(context.Background(), testTag, []byte(fmt.Sprintf("hello %d", i)), false, nil) + require.NoError(t, err) + } + + require.Eventually( + t, + func() bool { + return atomic.LoadUint32(&handlerCount) == 20 + }, + 1*time.Second, + 50*time.Millisecond, + ) +} + +type mockService struct { + id peer.ID + addrs []ma.Multiaddr + peers map[peer.ID]peer.AddrInfo +} + +func (s *mockService) Close() error { + return nil +} + +func (s *mockService) ID() peer.ID { + return s.id +} + +func (s *mockService) AddrInfo() peer.AddrInfo { + return peer.AddrInfo{ + ID: s.id, + Addrs: s.addrs, + } +} + +func (s *mockService) DialNode(ctx context.Context, peer *peer.AddrInfo) error { + s.peers[peer.ID] = *peer + return nil +} + +func (s *mockService) DialPeersUntilTargetCount(targetConnCount int) { +} + +func (s *mockService) ClosePeer(peer peer.ID) error { + if _, ok := s.peers[peer]; ok { + delete(s.peers, peer) + } + return nil +} + +func (s *mockService) Conns() []network.Conn { + return nil +} + +func (s *mockService) ListPeersForTopic(topic string) []peer.ID { + return nil +} + +func (s *mockService) Subscribe(topic string, val pubsub.ValidatorEx) (*pubsub.Subscription, error) { + return nil, nil +} +func (s *mockService) Publish(ctx context.Context, topic string, data []byte) error { + return nil +} + +func (s *mockService) setAddrs(addrs []ma.Multiaddr) { + s.addrs = addrs +} + +func makeMockService(id peer.ID, addrs []ma.Multiaddr) *mockService { + return &mockService{ + id: id, + addrs: addrs, + } +} + +func TestP2PNetworkAddress(t *testing.T) { + partitiontest.PartitionTest(t) + + cfg := config.GetDefaultLocal() + log := logging.TestingLog(t) + netA, err := NewP2PNetwork(log, cfg, "", nil, genesisID, config.Devtestnet) + defer netA.Stop() + require.NoError(t, err) + addrInfo := netA.service.AddrInfo() + // close the real service since we will substitute a mock one + netA.service.Close() + + // define some multiaddrs we will use in the test + loopbackAddr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1234") + require.NoError(t, err) + unspecifiedAddr, err := ma.NewMultiaddr("/ip4/0.0.0.0/tcp/0") + require.NoError(t, err) + publicAddr, err := ma.NewMultiaddr("/ip4/12.86.192.5/tcp/5678") + require.NoError(t, err) + publicAddr2, err := ma.NewMultiaddr("/ip4/23.97.191.6/tcp/1564") + require.NoError(t, err) + + // first two are invalid so third one should be returned as the first public address + addrsA := []ma.Multiaddr{ + loopbackAddr, + unspecifiedAddr, + publicAddr, + publicAddr2, + } + mockService := makeMockService(addrInfo.ID, addrsA) + netA.service = mockService + + retAddr, ok := netA.Address() + require.True(t, ok) + // using Contains since the return of Address also includes the public peerID + require.Contains(t, retAddr, publicAddr.String()) + + // don't have a public address so return the first one + addrsB := []ma.Multiaddr{ + loopbackAddr, + unspecifiedAddr, + } + mockService.addrs = addrsB + retAddr, ok = netA.Address() + require.True(t, ok) + require.Contains(t, retAddr, loopbackAddr.String()) + + // confirm that we don't return an address if none is supplied + mockService.addrs = nil + retAddr, ok = netA.Address() + require.False(t, ok) + require.Empty(t, retAddr) + + mockService.addrs = addrsA // these are still valid addresses + mockService.id = "invalid peer ID" // this won't parse and encode properly + retAddr, ok = netA.Address() + require.False(t, ok) + require.Empty(t, retAddr) +} diff --git a/network/p2pPeer.go b/network/p2pPeer.go new file mode 100644 index 0000000000..7d788180e6 --- /dev/null +++ b/network/p2pPeer.go @@ -0,0 +1,84 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package network + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "time" + + "github.com/algorand/websocket" + + "github.com/libp2p/go-libp2p/core/network" + yamux "github.com/libp2p/go-yamux/v4" +) + +type wsPeerConnP2PImpl struct { + stream network.Stream +} + +func (c *wsPeerConnP2PImpl) RemoteAddrString() string { + return c.stream.Conn().RemoteMultiaddr().String() +} + +func (c *wsPeerConnP2PImpl) NextReader() (int, io.Reader, error) { + // read length + var lenbuf [4]byte + _, err := io.ReadFull(c.stream, lenbuf[:]) + if err != nil { + return 0, nil, err + } + msglen := binary.BigEndian.Uint32(lenbuf[:]) + if msglen > MaxMessageLength { + return 0, nil, fmt.Errorf("message too long: %d", msglen) + } + // return io.Reader that only reads the next msglen bytes + return websocket.BinaryMessage, io.LimitReader(c.stream, int64(msglen)), nil +} + +func (c *wsPeerConnP2PImpl) WriteMessage(_ int, buf []byte) error { + // simple message framing: + // 1. write encoding of the length + var lenbuf [4]byte + binary.BigEndian.PutUint32(lenbuf[:], uint32(len(buf))) + _, err := c.stream.Write(lenbuf[:]) + if err != nil { + return err + } + // 2. write message + _, err = c.stream.Write(buf) + return err +} + +// Do nothing for now since this doesn't actually close the connection just sends the close message +func (c *wsPeerConnP2PImpl) CloseWithMessage([]byte, time.Time) error { + return nil +} + +func (c *wsPeerConnP2PImpl) SetReadLimit(int64) {} + +func (c *wsPeerConnP2PImpl) CloseWithoutFlush() error { + err := c.stream.Close() + if err != nil && err != yamux.ErrStreamClosed && err != yamux.ErrSessionShutdown && err != yamux.ErrStreamReset { + return err + } + return nil +} + +func (c *wsPeerConnP2PImpl) UnderlyingConn() net.Conn { return nil } diff --git a/network/phonebook.go b/network/phonebook.go index 0ad7be1a0c..cf189eb6a4 100644 --- a/network/phonebook.go +++ b/network/phonebook.go @@ -25,7 +25,7 @@ import ( "golang.org/x/exp/slices" ) -// when using GetAddresses with getAllAddresses, all the addresses will be retrieved, regardless +// getAllAddresses when using GetAddresses with getAllAddresses, all the addresses will be retrieved, regardless // of how many addresses the phonebook actually has. ( with the retry-after logic applied ) const getAllAddresses = math.MaxInt32 diff --git a/network/wsNetwork.go b/network/wsNetwork.go index cb54ffc246..2cce632bed 100644 --- a/network/wsNetwork.go +++ b/network/wsNetwork.go @@ -27,7 +27,6 @@ import ( "net/http" "net/textproto" "net/url" - "path" "regexp" "runtime" "strconv" @@ -148,175 +147,6 @@ const peerDisconnectionAckDuration = 5 * time.Second // peerShutdownDisconnectionAckDuration defines the time we would wait for the peer disconnection to complete during shutdown. const peerShutdownDisconnectionAckDuration = 50 * time.Millisecond -// Peer opaque interface for referring to a neighbor in the network -type Peer interface{} - -// PeerOption allows users to specify a subset of peers to query -// -//msgp:ignore PeerOption -type PeerOption int - -const ( - // PeersConnectedOut specifies all peers with outgoing connections - PeersConnectedOut PeerOption = iota - // PeersConnectedIn specifies all peers with inbound connections - PeersConnectedIn PeerOption = iota - // PeersPhonebookRelays specifies all relays in the phonebook - PeersPhonebookRelays PeerOption = iota - // PeersPhonebookArchivalNodes specifies all archival nodes (relay or p2p) - PeersPhonebookArchivalNodes PeerOption = iota - // PeersPhonebookArchivers specifies all archivers in the phonebook - PeersPhonebookArchivers PeerOption = iota -) - -// GossipNode represents a node in the gossip network -type GossipNode interface { - Address() (string, bool) - Broadcast(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error - Relay(ctx context.Context, tag protocol.Tag, data []byte, wait bool, except Peer) error - Disconnect(badnode Peer) - DisconnectPeers() // only used by testing - - // RegisterHTTPHandler path accepts gorilla/mux path annotations - RegisterHTTPHandler(path string, handler http.Handler) - - // RequestConnectOutgoing asks the system to actually connect to peers. - // `replace` optionally drops existing connections before making new ones. - // `quit` chan allows cancellation. TODO: use `context` - RequestConnectOutgoing(replace bool, quit <-chan struct{}) - - // Get a list of Peers we could potentially send a direct message to. - GetPeers(options ...PeerOption) []Peer - - // Start threads, listen on sockets. - Start() - - // Close sockets. Stop threads. - Stop() - - // RegisterHandlers adds to the set of given message handlers. - RegisterHandlers(dispatch []TaggedMessageHandler) - - // ClearHandlers deregisters all the existing message handlers. - ClearHandlers() - - // GetRoundTripper returns a Transport that would limit the number of outgoing connections. - GetRoundTripper() http.RoundTripper - - // OnNetworkAdvance notifies the network library that the agreement protocol was able to make a notable progress. - // this is the only indication that we have that we haven't formed a clique, where all incoming messages - // arrive very quickly, but might be missing some votes. The usage of this call is expected to have similar - // characteristics as with a watchdog timer. - OnNetworkAdvance() - - // GetHTTPRequestConnection returns the underlying connection for the given request. Note that the request must be the same - // request that was provided to the http handler ( or provide a fallback Context() to that ) - GetHTTPRequestConnection(request *http.Request) (conn net.Conn) - - // RegisterMessageInterest notifies the network library that this node - // wants to receive messages with the specified tag. This will cause - // this node to send corresponding MsgOfInterest notifications to any - // newly connecting peers. This should be called before the network - // is started. - RegisterMessageInterest(protocol.Tag) - - // SubstituteGenesisID substitutes the "{genesisID}" with their network-specific genesisID. - SubstituteGenesisID(rawURL string) string - - // called from wsPeer to report that it has closed - peerRemoteClose(peer *wsPeer, reason disconnectReason) -} - -// IncomingMessage represents a message arriving from some peer in our p2p network -type IncomingMessage struct { - Sender Peer - Tag Tag - Data []byte - Err error - Net GossipNode - - // Received is time.Time.UnixNano() - Received int64 - - // processing is a channel that is used by messageHandlerThread - // to indicate that it has started processing this message. It - // is used to ensure fairness across peers in terms of processing - // messages. - processing chan struct{} -} - -// Tag is a short string (2 bytes) marking a type of message -type Tag = protocol.Tag - -func highPriorityTag(tags []protocol.Tag) bool { - for _, tag := range tags { - if tag == protocol.AgreementVoteTag || tag == protocol.ProposalPayloadTag { - return true - } - } - return false -} - -// OutgoingMessage represents a message we want to send. -type OutgoingMessage struct { - Action ForwardingPolicy - Tag Tag - Payload []byte - Topics Topics - reason disconnectReason // used when Action == Disconnect - - // OnRelease is a function called when outgoing message, resulting from this incoming message, is released - // either by being sent or discarded. - OnRelease func() -} - -// ForwardingPolicy is an enum indicating to whom we should send a message -// -//msgp:ignore ForwardingPolicy -type ForwardingPolicy int - -const ( - // Ignore - discard (don't forward) - Ignore ForwardingPolicy = iota - - // Disconnect - disconnect from the peer that sent this message - Disconnect - - // Broadcast - forward to everyone (except the sender) - Broadcast - - // Respond - reply to the sender - Respond -) - -// MessageHandler takes a IncomingMessage (e.g., vote, transaction), processes it, and returns what (if anything) -// to send to the network in response. -// The ForwardingPolicy field of the returned OutgoingMessage indicates whether to reply directly to the sender -// (unicast), propagate to everyone except the sender (broadcast), or do nothing (ignore). -type MessageHandler interface { - Handle(message IncomingMessage) OutgoingMessage -} - -// HandlerFunc represents an implemenation of the MessageHandler interface -type HandlerFunc func(message IncomingMessage) OutgoingMessage - -// Handle implements MessageHandler.Handle, calling the handler with the IncomingKessage and returning the OutgoingMessage -func (f HandlerFunc) Handle(message IncomingMessage) OutgoingMessage { - return f(message) -} - -// TaggedMessageHandler receives one type of broadcast messages -type TaggedMessageHandler struct { - Tag - MessageHandler -} - -// Propagate is a convenience function to save typing in the common case of a message handler telling us to propagate an incoming message -// "return network.Propagate(msg)" instead of "return network.OutgoingMsg{network.Broadcast, msg.Tag, msg.Data}" -func Propagate(msg IncomingMessage) OutgoingMessage { - return OutgoingMessage{Action: Broadcast, Tag: msg.Tag, Payload: msg.Data, Topics: nil} -} - // GossipNetworkPath is the URL path to connect to the websocket gossip node at. // Contains {genesisID} param to be handled by gorilla/mux const GossipNetworkPath = "/v1/{genesisID}/gossip" @@ -744,17 +574,6 @@ func (wn *WebsocketNetwork) GetPeers(options ...PeerOption) []Peer { return outPeers } -// find the max value across the given uint64 numbers. -func max(numbers ...uint64) (maxNum uint64) { - maxNum = 0 // this is the lowest uint64 value. - for _, num := range numbers { - if num > maxNum { - maxNum = num - } - } - return -} - func (wn *WebsocketNetwork) setup() { var preferredResolver dnssec.ResolverIf if wn.config.DNSSecurityRelayAddrEnforced() { @@ -792,16 +611,7 @@ func (wn *WebsocketNetwork) setup() { // roughly estimate the number of messages that could be seen at any given moment. // For the late/redo/down committee, which happen in parallel, we need to allocate // extra space there. - wn.outgoingMessagesBufferSize = int( - max(config.Consensus[protocol.ConsensusCurrentVersion].NumProposers, - config.Consensus[protocol.ConsensusCurrentVersion].SoftCommitteeSize, - config.Consensus[protocol.ConsensusCurrentVersion].CertCommitteeSize, - config.Consensus[protocol.ConsensusCurrentVersion].NextCommitteeSize) + - max(config.Consensus[protocol.ConsensusCurrentVersion].LateCommitteeSize, - config.Consensus[protocol.ConsensusCurrentVersion].RedoCommitteeSize, - config.Consensus[protocol.ConsensusCurrentVersion].DownCommitteeSize), - ) - + wn.outgoingMessagesBufferSize = outgoingMessagesBufferSize wn.wsMaxHeaderBytes = wsMaxHeaderBytes wn.identityTracker = NewIdentityTracker() @@ -859,7 +669,7 @@ func (wn *WebsocketNetwork) setup() { wn.messagesOfInterestRefresh = make(chan struct{}, 2) wn.messagesOfInterestGeneration = 1 // something nonzero so that any new wsPeer needs updating if wn.relayMessages { - wn.RegisterMessageInterest(protocol.StateProofSigTag) + wn.registerMessageInterest(protocol.StateProofSigTag) } } @@ -2157,59 +1967,6 @@ var errBcastInvalidArray = errors.New("invalid broadcast array") var errBcastQFull = errors.New("broadcast queue full") -var errURLNoHost = errors.New("could not parse a host from url") - -var errURLColonHost = errors.New("host name starts with a colon") - -// HostColonPortPattern matches "^[-a-zA-Z0-9.]+:\\d+$" e.g. "foo.com.:1234" -var HostColonPortPattern = regexp.MustCompile("^[-a-zA-Z0-9.]+:\\d+$") - -// ParseHostOrURL handles "host:port" or a full URL. -// Standard library net/url.Parse chokes on "host:port". -func ParseHostOrURL(addr string) (*url.URL, error) { - // If the entire addr is "host:port" grab that right away. - // Don't try url.Parse() because that will grab "host:" as if it were "scheme:" - if HostColonPortPattern.MatchString(addr) { - return &url.URL{Scheme: "http", Host: addr}, nil - } - parsed, err := url.Parse(addr) - if err == nil { - if parsed.Host == "" { - return nil, errURLNoHost - } - return parsed, nil - } - if strings.HasPrefix(addr, "http:") || strings.HasPrefix(addr, "https:") || strings.HasPrefix(addr, "ws:") || strings.HasPrefix(addr, "wss:") || strings.HasPrefix(addr, "://") || strings.HasPrefix(addr, "//") { - return parsed, err - } - // This turns "[::]:4601" into "http://[::]:4601" which url.Parse can do - parsed, e2 := url.Parse("http://" + addr) - if e2 == nil { - // https://datatracker.ietf.org/doc/html/rfc1123#section-2 - // first character is relaxed to allow either a letter or a digit - if parsed.Host[0] == ':' && (len(parsed.Host) < 2 || parsed.Host[1] != ':') { - return nil, errURLColonHost - } - return parsed, nil - } - return parsed, err /* return original err, not our prefix altered try */ -} - -// addrToGossipAddr parses host:port or a URL and returns the URL to the websocket interface at that address. -func (wn *WebsocketNetwork) addrToGossipAddr(addr string) (string, error) { - parsedURL, err := ParseHostOrURL(addr) - if err != nil { - wn.log.Warnf("could not parse addr %#v: %s", addr, err) - return "", errBadAddr - } - parsedURL.Scheme = websocketsScheme[parsedURL.Scheme] - if parsedURL.Scheme == "" { - parsedURL.Scheme = "ws" - } - parsedURL.Path = strings.Replace(path.Join(parsedURL.Path, GossipNetworkPath), "{genesisID}", wn.GenesisID, -1) - return parsedURL.String(), nil -} - // tryConnectReserveAddr synchronously checks that addr is not already being connected to, returns (websocket URL or "", true if connection may proceed) func (wn *WebsocketNetwork) tryConnectReserveAddr(addr string) (gossipAddr string, ok bool) { wn.tryConnectLock.Lock() @@ -2655,12 +2412,12 @@ func SetUserAgentHeader(header http.Header) { header.Set(UserAgentHeader, ua) } -// RegisterMessageInterest notifies the network library that this node +// registerMessageInterest notifies the network library that this node // wants to receive messages with the specified tag. This will cause // this node to send corresponding MsgOfInterest notifications to any // newly connecting peers. This should be called before the network // is started. -func (wn *WebsocketNetwork) RegisterMessageInterest(t protocol.Tag) { +func (wn *WebsocketNetwork) registerMessageInterest(t protocol.Tag) { wn.messagesOfInterestMu.Lock() defer wn.messagesOfInterestMu.Unlock() @@ -2711,7 +2468,7 @@ func (wn *WebsocketNetwork) postMessagesOfInterestThread() { wantTXGossip := wn.nodeInfo.IsParticipating() if wantTXGossip && (wn.wantTXGossip != wantTXGossipYes) { wn.log.Infof("postMessagesOfInterestThread: enabling TX gossip") - wn.RegisterMessageInterest(protocol.TxnTag) + wn.registerMessageInterest(protocol.TxnTag) atomic.StoreUint32(&wn.wantTXGossip, wantTXGossipYes) } else if !wantTXGossip && (wn.wantTXGossip != wantTXGossipNo) { wn.log.Infof("postMessagesOfInterestThread: disabling TX gossip") diff --git a/network/wsNetwork_test.go b/network/wsNetwork_test.go index 4a61691292..099b04e895 100644 --- a/network/wsNetwork_test.go +++ b/network/wsNetwork_test.go @@ -2866,7 +2866,7 @@ func TestWebsocketNetworkMessageOfInterest(t *testing.T) { // have netB asking netA to send it ft2, deregister ping handler to make sure that we aren't exceeding the maximum MOI messagesize // Max MOI size is calculated by encoding all of the valid tags, since we are using a custom tag here we must deregister one in the default set. netB.DeregisterMessageInterest(protocol.PingTag) - netB.RegisterMessageInterest(ft2) + netB.registerMessageInterest(ft2) netB.Start() defer netStop(t, netB, "B") @@ -2918,7 +2918,7 @@ func TestWebsocketNetworkMessageOfInterest(t *testing.T) { waitReady(t, netB, readyTimeout.C) // have netB asking netA to send it only AgreementVoteTag and ProposalPayloadTag - netB.RegisterMessageInterest(ft2) + netB.registerMessageInterest(ft2) netB.DeregisterMessageInterest(ft1) netB.DeregisterMessageInterest(ft3) netB.DeregisterMessageInterest(ft4) @@ -3635,67 +3635,6 @@ func BenchmarkVariableTransactionMessageBlockSizes(t *testing.B) { } } -type urlCase struct { - text string - out url.URL -} - -func TestParseHostOrURL(t *testing.T) { - partitiontest.PartitionTest(t) - urlTestCases := []urlCase{ - {"localhost:123", url.URL{Scheme: "http", Host: "localhost:123"}}, - {"http://localhost:123", url.URL{Scheme: "http", Host: "localhost:123"}}, - {"ws://localhost:9999", url.URL{Scheme: "ws", Host: "localhost:9999"}}, - {"wss://localhost:443", url.URL{Scheme: "wss", Host: "localhost:443"}}, - {"https://localhost:123", url.URL{Scheme: "https", Host: "localhost:123"}}, - {"https://somewhere.tld", url.URL{Scheme: "https", Host: "somewhere.tld"}}, - {"http://127.0.0.1:123", url.URL{Scheme: "http", Host: "127.0.0.1:123"}}, - {"//somewhere.tld", url.URL{Scheme: "", Host: "somewhere.tld"}}, - {"//somewhere.tld:4601", url.URL{Scheme: "", Host: "somewhere.tld:4601"}}, - {"http://[::]:123", url.URL{Scheme: "http", Host: "[::]:123"}}, - {"1.2.3.4:123", url.URL{Scheme: "http", Host: "1.2.3.4:123"}}, - {"[::]:123", url.URL{Scheme: "http", Host: "[::]:123"}}, - {"r2-devnet.devnet.algodev.network:4560", url.URL{Scheme: "http", Host: "r2-devnet.devnet.algodev.network:4560"}}, - {"::11.22.33.44:123", url.URL{Scheme: "http", Host: "::11.22.33.44:123"}}, - } - badUrls := []string{ - "justahost", - "localhost:WAT", - "http://localhost:WAT", - "https://localhost:WAT", - "ws://localhost:WAT", - "wss://localhost:WAT", - "//localhost:WAT", - "://badaddress", // See rpcs/blockService_test.go TestRedirectFallbackEndpoints - "://localhost:1234", - ":xxx", - ":xxx:1234", - "::11.22.33.44", - ":a:1", - ":a:", - ":1", - ":a", - ":", - "", - } - for _, tc := range urlTestCases { - t.Run(tc.text, func(t *testing.T) { - v, err := ParseHostOrURL(tc.text) - require.NoError(t, err) - if tc.out != *v { - t.Errorf("url wanted %#v, got %#v", tc.out, v) - return - } - }) - } - for _, addr := range badUrls { - t.Run(addr, func(t *testing.T) { - _, err := ParseHostOrURL(addr) - require.Error(t, err, "url should fail", addr) - }) - } -} - func TestPreparePeerData(t *testing.T) { partitiontest.PartitionTest(t) @@ -4136,82 +4075,95 @@ func TestRefreshRelayArchivePhonebookAddresses(t *testing.T) { var netA *WebsocketNetwork var refreshRelayDNSBootstrapID = ".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)" - rapid.Check(t, func(t1 *rapid.T) { - refreshTestConf := defaultConfig - refreshTestConf.DNSBootstrapID = refreshRelayDNSBootstrapID - netA = makeTestWebsocketNodeWithConfig(t, refreshTestConf) - netA.NetworkID = nonHardcodedNetworkIDGen().Draw(t1, "network") - - primarySRVBootstrap := strings.Replace(".algorand.network", "", string(netA.NetworkID), -1) - backupSRVBootstrap := strings.Replace(".algorand.net", "", string(netA.NetworkID), -1) - var primaryRelayResolvedRecords []string - var secondaryRelayResolvedRecords []string - var primaryArchiveResolvedRecords []string - var secondaryArchiveResolvedRecords []string - - for _, record := range []string{"r1.algorand-.network", - "r2.algorand-.network", "r3.algorand-.network"} { - var recordSub = strings.Replace(record, "", string(netA.NetworkID), -1) - primaryRelayResolvedRecords = append(primaryRelayResolvedRecords, recordSub) - secondaryRelayResolvedRecords = append(secondaryRelayResolvedRecords, strings.Replace(recordSub, "network", "net", -1)) - } - - for _, record := range []string{"r1archive.algorand-.network", - "r2archive.algorand-.network", "r3archive.algorand-.network"} { - var recordSub = strings.Replace(record, "", string(netA.NetworkID), -1) - primaryArchiveResolvedRecords = append(primaryArchiveResolvedRecords, recordSub) - secondaryArchiveResolvedRecords = append(secondaryArchiveResolvedRecords, strings.Replace(recordSub, "network", "net", -1)) - } + testRefreshWithConfig := func(refreshTestConf config.Local) { + rapid.Check(t, func(t1 *rapid.T) { + refreshTestConf.DNSBootstrapID = refreshRelayDNSBootstrapID + netA = makeTestWebsocketNodeWithConfig(t, refreshTestConf) + netA.NetworkID = nonHardcodedNetworkIDGen().Draw(t1, "network") + + primarySRVBootstrap := strings.Replace(".algorand.network", "", string(netA.NetworkID), -1) + backupSRVBootstrap := strings.Replace(".algorand.net", "", string(netA.NetworkID), -1) + var primaryRelayResolvedRecords []string + var secondaryRelayResolvedRecords []string + var primaryArchiveResolvedRecords []string + var secondaryArchiveResolvedRecords []string + + for _, record := range []string{"r1.algorand-.network", + "r2.algorand-.network", "r3.algorand-.network"} { + var recordSub = strings.Replace(record, "", string(netA.NetworkID), -1) + primaryRelayResolvedRecords = append(primaryRelayResolvedRecords, recordSub) + secondaryRelayResolvedRecords = append(secondaryRelayResolvedRecords, strings.Replace(recordSub, "network", "net", -1)) + } - // Mock the SRV record lookup - netA.resolveSRVRecords = func(service string, protocol string, name string, fallbackDNSResolverAddress string, - secure bool) (addrs []string, err error) { - if service == "algobootstrap" && protocol == "tcp" && name == primarySRVBootstrap { - return primaryRelayResolvedRecords, nil - } else if service == "algobootstrap" && protocol == "tcp" && name == backupSRVBootstrap { - return secondaryRelayResolvedRecords, nil + for _, record := range []string{"r1archive.algorand-.network", + "r2archive.algorand-.network", "r3archive.algorand-.network"} { + var recordSub = strings.Replace(record, "", string(netA.NetworkID), -1) + primaryArchiveResolvedRecords = append(primaryArchiveResolvedRecords, recordSub) + secondaryArchiveResolvedRecords = append(secondaryArchiveResolvedRecords, strings.Replace(recordSub, "network", "net", -1)) } - if service == "archive" && protocol == "tcp" && name == primarySRVBootstrap { - return primaryArchiveResolvedRecords, nil - } else if service == "archive" && protocol == "tcp" && name == backupSRVBootstrap { - return secondaryArchiveResolvedRecords, nil + // Mock the SRV record lookup + netA.resolveSRVRecords = func(service string, protocol string, name string, fallbackDNSResolverAddress string, + secure bool) (addrs []string, err error) { + if service == "algobootstrap" && protocol == "tcp" && name == primarySRVBootstrap { + return primaryRelayResolvedRecords, nil + } else if service == "algobootstrap" && protocol == "tcp" && name == backupSRVBootstrap { + return secondaryRelayResolvedRecords, nil + } + + if service == "archive" && protocol == "tcp" && name == primarySRVBootstrap { + return primaryArchiveResolvedRecords, nil + } else if service == "archive" && protocol == "tcp" && name == backupSRVBootstrap { + return secondaryArchiveResolvedRecords, nil + } + + return } - return - } + relayPeers := netA.GetPeers(PeersPhonebookRelays) + assert.Equal(t, 0, len(relayPeers)) - relayPeers := netA.GetPeers(PeersPhonebookRelays) - assert.Equal(t, 0, len(relayPeers)) + archivePeers := netA.GetPeers(PeersPhonebookArchivers) + assert.Equal(t, 0, len(archivePeers)) - archivePeers := netA.GetPeers(PeersPhonebookArchivers) - assert.Equal(t, 0, len(archivePeers)) + netA.refreshRelayArchivePhonebookAddresses() - netA.refreshRelayArchivePhonebookAddresses() + relayPeers = netA.GetPeers(PeersPhonebookRelays) - relayPeers = netA.GetPeers(PeersPhonebookRelays) + assert.Equal(t, 3, len(relayPeers)) + relayAddrs := make([]string, 0, len(relayPeers)) + for _, peer := range relayPeers { + relayAddrs = append(relayAddrs, peer.(HTTPPeer).GetAddress()) + } - assert.Equal(t, 3, len(relayPeers)) - relayAddrs := make([]string, 0, len(relayPeers)) - for _, peer := range relayPeers { - relayAddrs = append(relayAddrs, peer.(HTTPPeer).GetAddress()) - } + assert.ElementsMatch(t, primaryRelayResolvedRecords, relayAddrs) - assert.ElementsMatch(t, primaryRelayResolvedRecords, relayAddrs) + archivePeers = netA.GetPeers(PeersPhonebookArchivers) - archivePeers = netA.GetPeers(PeersPhonebookArchivers) + if refreshTestConf.EnableBlockServiceFallbackToArchiver { + // For the time being, we do not dedup resolved archive nodes + assert.Equal(t, len(primaryArchiveResolvedRecords)+len(secondaryArchiveResolvedRecords), len(archivePeers)) - // For the time being, we do not dedup resolved archive nodes - assert.Equal(t, 6, len(archivePeers)) + archiveAddrs := make([]string, 0, len(archivePeers)) + for _, peer := range archivePeers { + archiveAddrs = append(archiveAddrs, peer.(HTTPPeer).GetAddress()) + } - archiveAddrs := make([]string, 0, len(archivePeers)) - for _, peer := range archivePeers { - archiveAddrs = append(archiveAddrs, peer.(HTTPPeer).GetAddress()) - } + assert.ElementsMatch(t, append(primaryArchiveResolvedRecords, secondaryArchiveResolvedRecords...), archiveAddrs) - assert.ElementsMatch(t, append(primaryArchiveResolvedRecords, secondaryArchiveResolvedRecords...), archiveAddrs) + } else { + assert.Equal(t, 0, len(archivePeers)) + } - }) + }) + } + + testRefreshWithConfig(defaultConfig) + + configWithBlockServiceFallbackToArchiverEnabled := config.GetDefaultLocal() + configWithBlockServiceFallbackToArchiverEnabled.EnableBlockServiceFallbackToArchiver = true + + testRefreshWithConfig(configWithBlockServiceFallbackToArchiverEnabled) } /* @@ -4308,39 +4260,6 @@ func TestUpdatePhonebookAddresses(t *testing.T) { }) } -func TestUpdatePhonebookAddressesPersistentPeers(t *testing.T) { - partitiontest.PartitionTest(t) - - rapid.Check(t, func(t1 *rapid.T) { - nw := makeTestWebsocketNode(t) - // Generate a new set of relay domains - // Dont overlap with archive nodes previously specified, duplicates between them not stored in phonebook as of this writing - relayDomainsGen := rapid.SliceOfN(rapidgen.DomainOf(253, 63, "", nil), 0, 200) - relayDomains := relayDomainsGen.Draw(t1, "relayDomains") - - var persistentPeers []string - // Add an initial set of relay domains as Persistent Peers in the Phonebook, - persistentPeers = rapid.SliceOfN(rapidgen.DomainOf(253, 63, "", relayDomains), 0, 200).Draw(t1, "") - nw.phonebook.AddPersistentPeers(persistentPeers, string(nw.NetworkID), PhoneBookEntryRelayRole) - - // run updatePhonebookAddresses - nw.updatePhonebookAddresses(relayDomains, nil) - - // Check that entries are in fact in phonebook less any duplicates - dedupedRelayDomains := removeDuplicateStr(relayDomains, false) - - relayPeers := nw.GetPeers(PeersPhonebookRelays) - require.Equal(t, len(dedupedRelayDomains)+len(persistentPeers), len(relayPeers)) - - relayAddrs := make([]string, 0, len(relayPeers)) - for _, peer := range relayPeers { - relayAddrs = append(relayAddrs, peer.(HTTPPeer).GetAddress()) - } - - require.ElementsMatch(t, append(dedupedRelayDomains, persistentPeers...), relayAddrs) - }) -} - func removeDuplicateStr(strSlice []string, lowerCase bool) []string { allKeys := make(map[string]bool) var dedupStrSlice = make([]string, 0) @@ -4399,85 +4318,60 @@ func TestMergePrimarySecondaryRelayAddressListsMinOverlap(t *testing.T) { }) } -type MergeTestDNSInputs struct { - dedupExpStr string - - primaryDomainSuffix string - - secondaryDomainSuffix string -} - -func mergePrimarySecondaryRelayAddressListsPartialOverlapTestInputsGen() *rapid.Generator[*MergeTestDNSInputs] { - - algorandNetBase := rapid.Custom(func(t *rapid.T) *MergeTestDNSInputs { - //unused/satisfying rapid expectation - rapid.String().Draw(t, "algorandNetBase") - //.algorand.network?backup=.algorand.net - // dedup=.algorand-.(network|net) - return &MergeTestDNSInputs{ - dedupExpStr: "(algorand-.(network|net))", - primaryDomainSuffix: "algorand-.network", - secondaryDomainSuffix: "algorand-.net", - } - }) - - algorandNetInverse := rapid.Custom(func(t *rapid.T) *MergeTestDNSInputs { - //unused/satisfying rapid expectation - rapid.String().Draw(t, "algorandNetInverse") - //.algorand.net?backup=.algorand.network" + - // "&dedup=.algorand-.(network|net) - return &MergeTestDNSInputs{ - dedupExpStr: "(algorand-.(network|net))", - primaryDomainSuffix: "algorand-.net", - secondaryDomainSuffix: "algorand-.network", - } - }) - - return rapid.OneOf(algorandNetBase, algorandNetInverse) +func alphaNumStr(n int) string { + var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321") + str := make([]rune, n) + for i := range str { + str[i] = chars[rand.Intn(len(chars))] + } + return string(str) } func TestMergePrimarySecondaryRelayAddressListsPartialOverlap(t *testing.T) { partitiontest.PartitionTest(t) - var netA *WebsocketNetwork - - rapid.Check(t, func(t1 *rapid.T) { - netA = makeTestWebsocketNode(t) - network := supportedNetworkGen().Draw(t1, "network") - mergeTestInputs := mergePrimarySecondaryRelayAddressListsPartialOverlapTestInputsGen().Draw(t1, "mergeTestInputs") + networks := []protocol.NetworkID{config.Testnet, config.Mainnet, config.Devnet, config.Betanet, + config.Alphanet, config.Devtestnet} + var netA *WebsocketNetwork + for _, network := range networks { dedupExp := regexp.MustCompile(strings.Replace( - mergeTestInputs.dedupExpStr, "", network, -1)) - primaryDomainSuffix := strings.Replace( - mergeTestInputs.primaryDomainSuffix, "", network, -1) - - // Generate hosts for a primary network domain - primaryNetworkDomainGen := rapidgen.DomainWithSuffixAndPort(primaryDomainSuffix, nil) - primaryDomainsGen := rapid.SliceOfN(primaryNetworkDomainGen, 0, 200) - - primaryRelayAddresses := primaryDomainsGen.Draw(t1, "primaryRelayAddresses") - - secondaryDomainSuffix := strings.Replace( - mergeTestInputs.secondaryDomainSuffix, "", network, -1) - // Generate these addresses from primary ones, find/replace domain suffix appropriately - secondaryRelayAddresses := replaceAllIn(primaryRelayAddresses, primaryDomainSuffix, secondaryDomainSuffix) - // Add some generated addresses to secondary list - to simplify verification further down - // (substituting suffixes, etc), we dont want the generated addresses to duplicate any of - // the replaced secondary ones - secondaryNetworkDomainGen := rapidgen.DomainWithSuffixAndPort(secondaryDomainSuffix, secondaryRelayAddresses) - secondaryDomainsGen := rapid.SliceOfN(secondaryNetworkDomainGen, 0, 200) - generatedSecondaryRelayAddresses := secondaryDomainsGen.Draw(t1, "secondaryRelayAddresses") - secondaryRelayAddresses = append(secondaryRelayAddresses, generatedSecondaryRelayAddresses...) + "(algorand-.(network|net))", "", string(network), -1)) + primaryRelayAddresses := make([]string, 0) + secondaryRelayAddresses := make([]string, 0) + extraSecondaryRelayAddresses := make([]string, 0) + for i := 0; i < 100; i++ { + relayID := alphaNumStr(2) + primaryRelayAddresses = append(primaryRelayAddresses, fmt.Sprintf("r-%s.algorand-%s.network", + relayID, network)) + secondaryRelayAddresses = append(secondaryRelayAddresses, fmt.Sprintf("r-%s.algorand-%s.net", + relayID, network)) + } + for i := 0; i < 20; i++ { + relayID := alphaNumStr(2) + "-" + alphaNumStr(1) + primaryRelayAddresses = append(primaryRelayAddresses, fmt.Sprintf("relay-%s.algorand-%s.network", + relayID, network)) + secondaryRelayAddresses = append(secondaryRelayAddresses, fmt.Sprintf("relay-%s.algorand-%s.net", + relayID, network)) + } + // Add additional secondary ones that intentionally do not duplicate primary ones + for i := 0; i < 10; i++ { + relayID := alphaNumStr(2) + "-" + alphaNumStr(1) + extraSecondaryRelayAddresses = append(extraSecondaryRelayAddresses, fmt.Sprintf("noduprelay-%s.algorand-%s.net", + relayID, network)) + } + secondaryRelayAddresses = append(secondaryRelayAddresses, extraSecondaryRelayAddresses...) - mergedRelayAddresses := netA.mergePrimarySecondaryRelayAddressSlices(protocol.NetworkID(network), + mergedRelayAddresses := netA.mergePrimarySecondaryRelayAddressSlices(network, primaryRelayAddresses, secondaryRelayAddresses, dedupExp) - // We expect the primary addresses to take precedence over a "matching" secondary address, randomly generated + // We expect the primary addresses to take precedence over a "matching" secondary address, extra non-duplicate // secondary addresses should be present in the merged slice - expectedRelayAddresses := removeDuplicateStr(append(primaryRelayAddresses, generatedSecondaryRelayAddresses...), true) + expectedRelayAddresses := removeDuplicateStr(append(primaryRelayAddresses, extraSecondaryRelayAddresses...), true) assert.ElementsMatch(t, expectedRelayAddresses, mergedRelayAddresses) - }) + } + } // Case where a "backup" network is specified, but no dedup expression is provided. Technically possible, diff --git a/network/wsPeer.go b/network/wsPeer.go index a717eef60c..56f2b6a4f0 100644 --- a/network/wsPeer.go +++ b/network/wsPeer.go @@ -949,11 +949,11 @@ func (wp *wsPeer) Close(deadline time.Time) { close(wp.closing) err := wp.conn.CloseWithMessage(websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""), deadline) if err != nil { - wp.log.Infof("failed to write CloseMessage to connection for %s", wp.conn.RemoteAddrString()) + wp.log.Infof("failed to write CloseMessage to connection for %s, err: %s", wp.conn.RemoteAddrString(), err) } err = wp.conn.CloseWithoutFlush() if err != nil { - wp.log.Infof("failed to CloseWithoutFlush to connection for %s", wp.conn.RemoteAddrString()) + wp.log.Infof("failed to CloseWithoutFlush to connection for %s, err: %s", wp.conn.RemoteAddrString(), err) } } diff --git a/node/follower_node.go b/node/follower_node.go index e044333d42..b7fb81065b 100644 --- a/node/follower_node.go +++ b/node/follower_node.go @@ -20,8 +20,6 @@ package node import ( "context" "fmt" - "os" - "path/filepath" "time" "github.com/algorand/go-deadlock" @@ -59,7 +57,7 @@ type AlgorandFollowerNode struct { catchpointCatchupService *catchup.CatchpointCatchupService blockService *rpcs.BlockService - rootDir string + genesisDirs config.ResolvedGenesisDirs genesisID string genesisHash crypto.Digest devMode bool // is this node operates in a developer mode ? ( benign agreement, broadcasting transaction generates a new block ) @@ -80,11 +78,15 @@ type AlgorandFollowerNode struct { // MakeFollower sets up an Algorand data node func MakeFollower(log logging.Logger, rootDir string, cfg config.Local, phonebookAddresses []string, genesis bookkeeping.Genesis) (*AlgorandFollowerNode, error) { node := new(AlgorandFollowerNode) - node.rootDir = rootDir node.log = log.With("name", cfg.NetAddress) node.genesisID = genesis.ID() node.genesisHash = genesis.Hash() node.devMode = genesis.DevMode + var err error + node.genesisDirs, err = cfg.EnsureAndResolveGenesisDirs(rootDir, genesis.ID()) + if err != nil { + return nil, err + } if node.devMode { log.Warn("Follower running on a devMode network. Must submit txns to a different node.") @@ -102,16 +104,6 @@ func MakeFollower(log logging.Logger, rootDir string, cfg config.Local, phoneboo p2pNode.DeregisterMessageInterest(protocol.VoteBundleTag) node.net = p2pNode - // load stored data - genesisDir := filepath.Join(rootDir, genesis.ID()) - ledgerPathnamePrefix := filepath.Join(genesisDir, config.LedgerFilenamePrefix) - - // create initial ledger, if it doesn't exist - err = os.Mkdir(genesisDir, 0700) - if err != nil && !os.IsExist(err) { - log.Errorf("Unable to create genesis directory: %v", err) - return nil, err - } genalloc, err := genesis.Balances() if err != nil { log.Errorf("Cannot load genesis allocation: %v", err) @@ -120,9 +112,13 @@ func MakeFollower(log logging.Logger, rootDir string, cfg config.Local, phoneboo node.cryptoPool = execpool.MakePool(node) node.lowPriorityCryptoVerificationPool = execpool.MakeBacklog(node.cryptoPool, 2*node.cryptoPool.GetParallelism(), execpool.LowPriority, node) - node.ledger, err = data.LoadLedger(node.log, ledgerPathnamePrefix, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledgercore.BlockListener{}, cfg) + ledgerPaths := ledger.DirsAndPrefix{ + DBFilePrefix: config.LedgerFilenamePrefix, + ResolvedGenesisDirs: node.genesisDirs, + } + node.ledger, err = data.LoadLedger(node.log, ledgerPaths, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledgercore.BlockListener{}, cfg) if err != nil { - log.Errorf("Cannot initialize ledger (%s): %v", ledgerPathnamePrefix, err) + log.Errorf("Cannot initialize ledger (%v): %v", ledgerPaths, err) return nil, err } @@ -245,9 +241,9 @@ func (node *AlgorandFollowerNode) BroadcastInternalSignedTxGroup(_ []transaction // Simulate speculatively runs a transaction group against the current // blockchain state and returns the effects and/or errors that would result. -func (node *AlgorandFollowerNode) Simulate(_ simulation.Request) (result simulation.Result, err error) { - err = fmt.Errorf("cannot simulate in data mode") - return +func (node *AlgorandFollowerNode) Simulate(request simulation.Request) (result simulation.Result, err error) { + simulator := simulation.MakeSimulator(node.ledger, node.config.EnableDeveloperAPI) + return simulator.Simulate(request) } // GetPendingTransaction no-ops in follower mode diff --git a/node/follower_node_test.go b/node/follower_node_test.go index 192b333be0..b6402a9b57 100644 --- a/node/follower_node_test.go +++ b/node/follower_node_test.go @@ -18,6 +18,7 @@ package node import ( "context" + "path/filepath" "testing" "github.com/sirupsen/logrus" @@ -31,6 +32,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/simulation" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" @@ -65,7 +67,8 @@ func setupFollowNode(t *testing.T) *AlgorandFollowerNode { cfg := config.GetDefaultLocal() cfg.EnableFollowMode = true genesis := followNodeDefaultGenesis() - node, err := MakeFollower(logging.Base(), t.TempDir(), cfg, []string{}, genesis) + root := t.TempDir() + node, err := MakeFollower(logging.Base(), root, cfg, []string{}, genesis) require.NoError(t, err) return node } @@ -136,7 +139,8 @@ func TestDevModeWarning(t *testing.T) { logger, hook := test.NewNullLogger() tlogger := logging.NewWrappedLogger(logger) - _, err := MakeFollower(tlogger, t.TempDir(), cfg, []string{}, genesis) + root := t.TempDir() + _, err := MakeFollower(tlogger, root, cfg, []string{}, genesis) require.NoError(t, err) // check for the warning @@ -169,3 +173,169 @@ func TestFastCatchupResume(t *testing.T) { // Verify the sync was reset. assert.Equal(t, uint64(0), node.GetSyncRound()) } + +// TestDefaultResourcePaths confirms that when no extra configuration is provided, all resources are created in the dataDir +func TestDefaultResourcePaths_Follower(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + + genesis := bookkeeping.Genesis{ + SchemaID: "go-test-node-genesis", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFollower(log, testDirectory, cfg, []string{}, genesis) + require.NoError(t, err) + + n.Start() + defer n.Stop() + + // confirm genesis dir exists in the data dir, and that resources exist in the expected locations + require.DirExists(t, filepath.Join(testDirectory, genesis.ID())) + + require.FileExists(t, filepath.Join(testDirectory, genesis.ID(), "ledger.tracker.sqlite")) + require.FileExists(t, filepath.Join(testDirectory, genesis.ID(), "ledger.block.sqlite")) +} + +// TestConfiguredDataDirs tests to see that when HotDataDir and ColdDataDir are set, underlying resources are created in the correct locations +// Not all resources are tested here, because not all resources use the paths provided to them immediately. For example, catchpoint only creates +// a directory when writing a catchpoint file, which is not being done here with this simple node +func TestConfiguredDataDirs_Follower(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + testDirHot := t.TempDir() + testDirCold := t.TempDir() + + genesis := bookkeeping.Genesis{ + SchemaID: "go-test-node-genesis", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + cfg.HotDataDir = testDirHot + cfg.ColdDataDir = testDirCold + cfg.CatchpointTracking = 2 + cfg.CatchpointInterval = 1 + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFollower(log, testDirectory, cfg, []string{}, genesis) + require.NoError(t, err) + + n.Start() + defer n.Stop() + + // confirm hot data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirHot, genesis.ID())) + + // confirm the tracker is in the genesis dir of hot data dir + require.FileExists(t, filepath.Join(testDirHot, genesis.ID(), "ledger.tracker.sqlite")) + + // confirm cold data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirCold, genesis.ID())) + + // confirm the blockdb is in the genesis dir of cold data dir + require.FileExists(t, filepath.Join(testDirCold, genesis.ID(), "ledger.block.sqlite")) + +} + +// TestConfiguredResourcePaths tests to see that when individual paths are set, underlying resources are created in the correct locations +func TestConfiguredResourcePaths_Follower(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + testDirHot := t.TempDir() + testDirCold := t.TempDir() + + // add a path for each resource now + trackerPath := filepath.Join(testDirectory, "custom_tracker") + blockPath := filepath.Join(testDirectory, "custom_block") + + genesis := bookkeeping.Genesis{ + SchemaID: "go-test-node-genesis", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + // Configure everything even though a follower node will only use Tracker and Block DBs + cfg.HotDataDir = testDirHot + cfg.ColdDataDir = testDirCold + cfg.TrackerDBDir = trackerPath + cfg.BlockDBDir = blockPath + cfg.CatchpointTracking = 2 + cfg.CatchpointInterval = 1 + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFollower(log, testDirectory, cfg, []string{}, genesis) + require.NoError(t, err) + + n.Start() + defer n.Stop() + + // confirm hot data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirHot, genesis.ID())) + + // the tracker shouldn't be in the hot data dir, but rather the custom path's genesis dir + require.NoFileExists(t, filepath.Join(testDirHot, genesis.ID(), "ledger.tracker.sqlite")) + require.FileExists(t, filepath.Join(cfg.TrackerDBDir, genesis.ID(), "ledger.tracker.sqlite")) + + // confirm cold data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirCold, genesis.ID())) + + // block db shouldn't be in the cold data dir, but rather the custom path's genesis dir + require.NoFileExists(t, filepath.Join(testDirCold, genesis.ID(), "ledger.block.sqlite")) + require.FileExists(t, filepath.Join(cfg.BlockDBDir, genesis.ID(), "ledger.block.sqlite")) +} + +func TestSimulate(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + node := setupFollowNode(t) + + round := node.ledger.LastRound() + + stxn := txntest.Txn{ + Type: protocol.PaymentTx, + Sender: sinkAddr, + Receiver: poolAddr, + Amount: 1, + Fee: 1000, + FirstValid: round, + LastValid: round + 1000, + GenesisHash: node.ledger.GenesisHash(), + }.SignedTxn() + + request := simulation.Request{ + TxnGroups: [][]transactions.SignedTxn{{stxn}}, + AllowEmptySignatures: true, + } + + result, err := node.Simulate(request) + require.NoError(t, err) + + require.Len(t, result.TxnGroups, 1) + require.Len(t, result.TxnGroups[0].Txns, 1) + require.Equal(t, stxn, result.TxnGroups[0].Txns[0].Txn.SignedTxn) + require.Empty(t, result.TxnGroups[0].FailureMessage) +} diff --git a/node/msgp_gen.go b/node/msgp_gen.go index ee32584eb9..9d79065fe8 100644 --- a/node/msgp_gen.go +++ b/node/msgp_gen.go @@ -60,11 +60,11 @@ func (_ *netPrioResponse) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *netPrioResponse) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -240,11 +240,11 @@ func (_ *netPrioResponseSigned) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *netPrioResponseSigned) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/node/node.go b/node/node.go index 1220751877..4c18ad1d51 100644 --- a/node/node.go +++ b/node/node.go @@ -127,7 +127,7 @@ type AlgorandFullNode struct { ledgerService *rpcs.LedgerService txPoolSyncerService *rpcs.TxSyncer - rootDir string + genesisDirs config.ResolvedGenesisDirs genesisID string genesisHash crypto.Digest devMode bool // is this node operating in a developer mode ? ( benign agreement, broadcasting transaction generates a new block ) @@ -177,44 +177,54 @@ type TxnWithStatus struct { // (i.e., it returns a node that participates in consensus) func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAddresses []string, genesis bookkeeping.Genesis) (*AlgorandFullNode, error) { node := new(AlgorandFullNode) - node.rootDir = rootDir node.log = log.With("name", cfg.NetAddress) node.genesisID = genesis.ID() node.genesisHash = genesis.Hash() node.devMode = genesis.DevMode node.config = cfg - - // tie network, block fetcher, and agreement services together - p2pNode, err := network.NewWebsocketNetwork(node.log, node.config, phonebookAddresses, genesis.ID(), genesis.Network, node) + var err error + node.genesisDirs, err = cfg.EnsureAndResolveGenesisDirs(rootDir, genesis.ID()) if err != nil { - log.Errorf("could not create websocket node: %v", err) return nil, err } - p2pNode.SetPrioScheme(node) - node.net = p2pNode - - // load stored data - genesisDir := filepath.Join(rootDir, genesis.ID()) - ledgerPathnamePrefix := filepath.Join(genesisDir, config.LedgerFilenamePrefix) - // create initial ledger, if it doesn't exist - err = os.Mkdir(genesisDir, 0700) - if err != nil && !os.IsExist(err) { - log.Errorf("Unable to create genesis directory: %v", err) - return nil, err - } genalloc, err := genesis.Balances() if err != nil { log.Errorf("Cannot load genesis allocation: %v", err) return nil, err } + // tie network, block fetcher, and agreement services together + var p2pNode network.GossipNode + if cfg.EnableP2P { + // TODO: pass more appropriate genesisDir (hot/cold). Presently this is just used to store a peerID key. + p2pNode, err = network.NewP2PNetwork(node.log, node.config, node.genesisDirs.RootGenesisDir, phonebookAddresses, genesis.ID(), genesis.Network) + if err != nil { + log.Errorf("could not create p2p node: %v", err) + return nil, err + } + } else { + var wsNode *network.WebsocketNetwork + wsNode, err = network.NewWebsocketNetwork(node.log, node.config, phonebookAddresses, genesis.ID(), genesis.Network, node) + if err != nil { + log.Errorf("could not create websocket node: %v", err) + return nil, err + } + wsNode.SetPrioScheme(node) + p2pNode = wsNode + } + node.net = p2pNode + node.cryptoPool = execpool.MakePool(node) node.lowPriorityCryptoVerificationPool = execpool.MakeBacklog(node.cryptoPool, 2*node.cryptoPool.GetParallelism(), execpool.LowPriority, node) node.highPriorityCryptoVerificationPool = execpool.MakeBacklog(node.cryptoPool, 2*node.cryptoPool.GetParallelism(), execpool.HighPriority, node) - node.ledger, err = data.LoadLedger(node.log, ledgerPathnamePrefix, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledgercore.BlockListener{}, cfg) + ledgerPaths := ledger.DirsAndPrefix{ + DBFilePrefix: config.LedgerFilenamePrefix, + ResolvedGenesisDirs: node.genesisDirs, + } + node.ledger, err = data.LoadLedger(node.log, ledgerPaths, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledgercore.BlockListener{}, cfg) if err != nil { - log.Errorf("Cannot initialize ledger (%s): %v", ledgerPathnamePrefix, err) + log.Errorf("Cannot initialize ledger (%v): %v", ledgerPaths, err) return nil, err } @@ -245,7 +255,8 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd node.ledgerService = rpcs.MakeLedgerService(cfg, node.ledger, p2pNode, node.genesisID) rpcs.RegisterTxService(node.transactionPool, p2pNode, node.genesisID, cfg.TxPoolSize, cfg.TxSyncServeResponseSize) - crashPathname := filepath.Join(genesisDir, config.CrashFilename) + // crash data is stored in the cold data directory unless otherwise specified + crashPathname := filepath.Join(node.genesisDirs.CrashGenesisDir, config.CrashFilename) crashAccess, err := db.MakeAccessor(crashPathname, false, false) if err != nil { log.Errorf("Cannot load crash data: %v", err) @@ -260,6 +271,7 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd } else { agreementClock = timers.MakeMonotonicClock[agreement.TimeoutType](time.Now()) } + agreementParameters := agreement.Parameters{ Logger: log, Accessor: crashAccess, @@ -283,7 +295,7 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd node.catchupService = catchup.MakeService(node.log, node.config, p2pNode, node.ledger, node.catchupBlockAuth, agreementLedger.UnmatchedPendingCertificates, node.lowPriorityCryptoVerificationPool) node.txPoolSyncerService = rpcs.MakeTxSyncer(node.transactionPool, node.net, node.txHandler.SolicitedTxHandler(), time.Duration(cfg.TxSyncIntervalSeconds)*time.Second, time.Duration(cfg.TxSyncTimeoutSeconds)*time.Second, cfg.TxSyncServeResponseSize) - registry, err := ensureParticipationDB(genesisDir, node.log) + registry, err := ensureParticipationDB(node.genesisDirs.ColdGenesisDir, node.log) if err != nil { log.Errorf("unable to initialize the participation registry database: %v", err) return nil, err @@ -316,7 +328,7 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd node.tracer = messagetracer.NewTracer(log).Init(cfg) gossip.SetTrace(agreementParameters.Network, node.tracer) - node.stateProofWorker = stateproof.NewWorker(genesisDir, node.log, node.accountManager, node.ledger.Ledger, node.net, node) + node.stateProofWorker = stateproof.NewWorker(node.genesisDirs.StateproofGenesisDir, node.log, node.accountManager, node.ledger.Ledger, node.net, node) return node, err } @@ -422,7 +434,7 @@ func (node *AlgorandFullNode) Stop() { // note: unlike the other two functions, this accepts a whole filename func (node *AlgorandFullNode) getExistingPartHandle(filename string) (db.Accessor, error) { - filename = filepath.Join(node.rootDir, node.genesisID, filename) + filename = filepath.Join(node.genesisDirs.RootGenesisDir, filename) _, err := os.Stat(filename) if err == nil { @@ -826,9 +838,7 @@ func (node *AlgorandFullNode) RemoveParticipationKey(partKeyID account.Participa return account.ErrParticipationIDNotFound } - genID := node.GenesisID() - - outDir := filepath.Join(node.rootDir, genID) + outDir := node.genesisDirs.RootGenesisDir filename := config.PartKeyFilename(partRecord.ParticipationID.String(), uint64(partRecord.FirstValid), uint64(partRecord.LastValid)) fullyQualifiedFilename := filepath.Join(outDir, filepath.Base(filename)) @@ -890,9 +900,7 @@ func createTemporaryParticipationKey(outDir string, partKeyBinary []byte) (strin // InstallParticipationKey Given a participation key binary stream install the participation key. func (node *AlgorandFullNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) { - genID := node.GenesisID() - - outDir := filepath.Join(node.rootDir, genID) + outDir := node.genesisDirs.RootGenesisDir fullyQualifiedTempFile, err := createTemporaryParticipationKey(outDir, partKeyBinary) // We need to make sure no tempfile is created/remains if there is an error @@ -947,7 +955,7 @@ func (node *AlgorandFullNode) InstallParticipationKey(partKeyBinary []byte) (acc func (node *AlgorandFullNode) loadParticipationKeys() error { // Generate a list of all potential participation key files - genesisDir := filepath.Join(node.rootDir, node.genesisID) + genesisDir := node.genesisDirs.RootGenesisDir files, err := os.ReadDir(genesisDir) if err != nil { return fmt.Errorf("AlgorandFullNode.loadPartitipationKeys: could not read directory %v: %v", genesisDir, err) diff --git a/node/node_test.go b/node/node_test.go index 64e285eac1..c905fa78da 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -516,6 +516,165 @@ func TestMismatchingGenesisDirectoryPermissions(t *testing.T) { require.NoError(t, os.RemoveAll(testDirectroy)) } +// TestDefaultResourcePaths confirms that when no extra configuration is provided, all resources are created in the dataDir +func TestDefaultResourcePaths(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + + genesis := bookkeeping.Genesis{ + SchemaID: "gen", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFull(log, testDirectory, cfg, []string{}, genesis) + + n.Start() + defer n.Stop() + + require.NoError(t, err) + + // confirm genesis dir exists in the data dir, and that resources exist in the expected locations + require.DirExists(t, filepath.Join(testDirectory, genesis.ID())) + + _, err = os.Stat(filepath.Join(testDirectory, genesis.ID(), "ledger.tracker.sqlite")) + require.NoError(t, err) + _, err = os.Stat(filepath.Join(testDirectory, genesis.ID(), "stateproof.sqlite")) + require.NoError(t, err) + _, err = os.Stat(filepath.Join(testDirectory, genesis.ID(), "ledger.block.sqlite")) + require.NoError(t, err) + _, err = os.Stat(filepath.Join(testDirectory, genesis.ID(), "partregistry.sqlite")) + require.NoError(t, err) + _, err = os.Stat(filepath.Join(testDirectory, genesis.ID(), "crash.sqlite")) + require.NoError(t, err) +} + +// TestConfiguredDataDirs tests to see that when HotDataDir and ColdDataDir are set, underlying resources are created in the correct locations +// Not all resources are tested here, because not all resources use the paths provided to them immediately. For example, catchpoint only creates +// a directory when writing a catchpoint file, which is not being done here with this simple node +func TestConfiguredDataDirs(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + testDirHot := t.TempDir() + testDirCold := t.TempDir() + + genesis := bookkeeping.Genesis{ + SchemaID: "go-test-node-genesis", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + cfg.HotDataDir = testDirHot + cfg.ColdDataDir = testDirCold + cfg.CatchpointTracking = 2 + cfg.CatchpointInterval = 1 + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFull(log, testDirectory, cfg, []string{}, genesis) + require.NoError(t, err) + + n.Start() + defer n.Stop() + + // confirm hot data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirHot, genesis.ID())) + + // confirm the tracker is in the genesis dir of hot data dir + require.FileExists(t, filepath.Join(testDirHot, genesis.ID(), "ledger.tracker.sqlite")) + + // confirm the stateproof db in the genesis dir of hot data dir + require.FileExists(t, filepath.Join(testDirCold, genesis.ID(), "stateproof.sqlite")) + + // confirm cold data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirCold, genesis.ID())) + + // confirm the blockdb is in the genesis dir of cold data dir + require.FileExists(t, filepath.Join(testDirCold, genesis.ID(), "ledger.block.sqlite")) + + // confirm the partregistry is in the genesis dir of cold data dir + require.FileExists(t, filepath.Join(testDirCold, genesis.ID(), "partregistry.sqlite")) + + // confirm the partregistry is in the genesis dir of cold data dir + require.FileExists(t, filepath.Join(testDirCold, genesis.ID(), "crash.sqlite")) +} + +// TestConfiguredResourcePaths tests to see that when TrackerDbFilePath, BlockDbFilePath, StateproofDir, and CrashFilePath are set, underlying resources are created in the correct locations +func TestConfiguredResourcePaths(t *testing.T) { + partitiontest.PartitionTest(t) + + testDirectory := t.TempDir() + testDirHot := t.TempDir() + testDirCold := t.TempDir() + + // add a path for each resource now + trackerPath := filepath.Join(testDirectory, "custom_tracker") + blockPath := filepath.Join(testDirectory, "custom_block") + stateproofDir := filepath.Join(testDirectory, "custom_stateproof") + crashPath := filepath.Join(testDirectory, "custom_crash") + + genesis := bookkeeping.Genesis{ + SchemaID: "go-test-node-genesis", + Proto: protocol.ConsensusCurrentVersion, + Network: config.Devtestnet, + FeeSink: sinkAddr.String(), + RewardsPool: poolAddr.String(), + } + + cfg := config.GetDefaultLocal() + + cfg.HotDataDir = testDirHot + cfg.ColdDataDir = testDirCold + cfg.TrackerDBDir = trackerPath + cfg.BlockDBDir = blockPath + cfg.StateproofDir = stateproofDir + cfg.CrashDBDir = crashPath + + // the logger is set up by the server, so we don't test this here + log := logging.Base() + + n, err := MakeFull(log, testDirectory, cfg, []string{}, genesis) + require.NoError(t, err) + + n.Start() + defer n.Stop() + + // confirm hot data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirHot, genesis.ID())) + + // the tracker shouldn't be in the hot data dir, but rather the custom path's genesis dir + require.NoFileExists(t, filepath.Join(testDirHot, genesis.ID(), "ledger.tracker.sqlite")) + require.FileExists(t, filepath.Join(cfg.TrackerDBDir, genesis.ID(), "ledger.tracker.sqlite")) + + // same with stateproofs + require.NoFileExists(t, filepath.Join(testDirHot, genesis.ID(), "stateproof.sqlite")) + require.FileExists(t, filepath.Join(cfg.StateproofDir, genesis.ID(), "stateproof.sqlite")) + + // confirm cold data dir exists and contains a genesis dir + require.DirExists(t, filepath.Join(testDirCold, genesis.ID())) + + // block db shouldn't be in the cold data dir, but rather the custom path's genesis dir + require.NoFileExists(t, filepath.Join(testDirCold, genesis.ID(), "ledger.block.sqlite")) + require.FileExists(t, filepath.Join(cfg.BlockDBDir, genesis.ID(), "ledger.block.sqlite")) + + require.NoFileExists(t, filepath.Join(testDirCold, genesis.ID(), "crash.sqlite")) + require.FileExists(t, filepath.Join(cfg.CrashDBDir, genesis.ID(), "crash.sqlite")) +} + // TestOfflineOnlineClosedBitStatus a test that validates that the correct bits are being set func TestOfflineOnlineClosedBitStatus(t *testing.T) { partitiontest.PartitionTest(t) diff --git a/package-deploy.yaml b/package-deploy.yaml index 4ad27fb35f..871d6ef1f7 100644 --- a/package-deploy.yaml +++ b/package-deploy.yaml @@ -45,8 +45,8 @@ agents: workDir: $HOME/projects/go-algorand - name: rpm - dockerFilePath: docker/build/cicd.centos.Dockerfile - image: algorand/go-algorand-ci-linux-centos + dockerFilePath: docker/build/cicd.centos8.Dockerfile + image: algorand/go-algorand-ci-linux-centos8 version: scripts/configure_dev-deps.sh buildArgs: - GOLANG_VERSION=`./scripts/get_golang_version.sh` @@ -54,9 +54,10 @@ agents: - AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY - NETWORK=$NETWORK - - PACKAGES_DIR=$PACKAGES_DIR - NO_DEPLOY=$NO_DEPLOY + - PACKAGES_DIR=$PACKAGES_DIR - S3_SOURCE=$S3_SOURCE + - STAGING=$STAGING - VERSION=$VERSION volumes: - $XDG_RUNTIME_DIR/gnupg/S.gpg-agent:/root/.gnupg/S.gpg-agent diff --git a/protocol/codec.go b/protocol/codec.go index e0386eb9b2..62abaedad6 100644 --- a/protocol/codec.go +++ b/protocol/codec.go @@ -288,21 +288,41 @@ func (d *MsgpDecoderBytes) Remaining() int { // encodingPool holds temporary byte slice buffers used for encoding messages. var encodingPool = sync.Pool{ New: func() interface{} { - return []byte{} + return &EncodingBuf{b: make([]byte, 0)} }, } +// EncodingBuf is a wrapper for a byte slice that can be used for encoding +type EncodingBuf struct { + b []byte +} + +// Bytes returns the underlying byte slice +func (eb *EncodingBuf) Bytes() []byte { + return eb.b +} + +// Update updates the underlying byte slice to the given one if its capacity exceeds the current one. +func (eb *EncodingBuf) Update(v []byte) *EncodingBuf { + if cap(eb.b) < cap(v) { + eb.b = v + } + return eb +} + // GetEncodingBuf returns a byte slice that can be used for encoding a // temporary message. The byte slice has zero length but potentially // non-zero capacity. The caller gets full ownership of the byte slice, // but is encouraged to return it using PutEncodingBuf(). -func GetEncodingBuf() []byte { - return encodingPool.Get().([]byte)[:0] +func GetEncodingBuf() *EncodingBuf { + buf := encodingPool.Get().(*EncodingBuf) + buf.b = buf.b[:0] + return buf } // PutEncodingBuf places a byte slice into the pool of temporary buffers // for encoding. The caller gives up ownership of the byte slice when // passing it to PutEncodingBuf(). -func PutEncodingBuf(s []byte) { - encodingPool.Put(s) +func PutEncodingBuf(buf *EncodingBuf) { + encodingPool.Put(buf) } diff --git a/protocol/msgp_gen.go b/protocol/msgp_gen.go index f33c8e3553..d2c7c1a2e1 100644 --- a/protocol/msgp_gen.go +++ b/protocol/msgp_gen.go @@ -95,11 +95,11 @@ func (_ ConsensusVersion) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *ConsensusVersion) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string var zb0002 int @@ -165,11 +165,11 @@ func (_ Error) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Error) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -225,11 +225,11 @@ func (_ HashID) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *HashID) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -285,11 +285,11 @@ func (_ NetworkID) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *NetworkID) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -345,11 +345,11 @@ func (_ StateProofType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *StateProofType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 uint64 zb0001, bts, err = msgp.ReadUint64Bytes(bts) @@ -405,11 +405,11 @@ func (_ Tag) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *Tag) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string zb0001, bts, err = msgp.ReadStringBytes(bts) @@ -465,11 +465,11 @@ func (_ TxType) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *TxType) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- { var zb0001 string var zb0002 int diff --git a/protocol/test/msgp_gen.go b/protocol/test/msgp_gen.go index 9a66ba3e52..b7500acdd9 100644 --- a/protocol/test/msgp_gen.go +++ b/protocol/test/msgp_gen.go @@ -42,11 +42,11 @@ func (_ testSlice) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *testSlice) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var zb0002 int var zb0003 bool zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) diff --git a/rpcs/blockService_test.go b/rpcs/blockService_test.go index 6b275a2489..a0ba919c98 100644 --- a/rpcs/blockService_test.go +++ b/rpcs/blockService_test.go @@ -143,6 +143,9 @@ func TestRedirectFallbackArchiver(t *testing.T) { net2 := &httpTestPeerSource{} config := config.GetDefaultLocal() + // Need to enable block service fallbacks + config.EnableBlockServiceFallbackToArchiver = true + bs1 := MakeBlockService(log, config, ledger1, net1, "test-genesis-ID") bs2 := MakeBlockService(log, config, ledger2, net2, "test-genesis-ID") @@ -311,6 +314,8 @@ func TestRedirectOnFullCapacity(t *testing.T) { net2 := &httpTestPeerSource{} config := config.GetDefaultLocal() + // Need to enable block service fallbacks + config.EnableBlockServiceFallbackToArchiver = true bs1 := MakeBlockService(log1, config, ledger1, net1, "test-genesis-ID") bs2 := MakeBlockService(log2, config, ledger2, net2, "test-genesis-ID") // set the memory cap so that it can serve only 1 block at a time @@ -487,6 +492,9 @@ func TestRedirectExceptions(t *testing.T) { net1 := &httpTestPeerSource{} config := config.GetDefaultLocal() + // Need to enable block service fallbacks + config.EnableBlockServiceFallbackToArchiver = true + bs1 := MakeBlockService(log, config, ledger1, net1, "{genesisID}") nodeA := &basicRPCNode{} @@ -543,8 +551,9 @@ func makeLedger(t *testing.T, namePostfix string) *data.Ledger { cfg := config.GetDefaultLocal() const inMem = true + prefix := t.Name() + namePostfix ledger, err := data.LoadLedger( - log, t.Name()+namePostfix, inMem, protocol.ConsensusCurrentVersion, genBal, "", genHash, + log, prefix, inMem, protocol.ConsensusCurrentVersion, genBal, "", genHash, nil, cfg, ) require.NoError(t, err) diff --git a/rpcs/msgp_gen.go b/rpcs/msgp_gen.go index d31c619108..4e091781cd 100644 --- a/rpcs/msgp_gen.go +++ b/rpcs/msgp_gen.go @@ -41,11 +41,11 @@ func (_ *EncodedBlockCert) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *EncodedBlockCert) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int diff --git a/scripts/buildtools/versions b/scripts/buildtools/versions index f2f5401fe2..ba43b37f60 100644 --- a/scripts/buildtools/versions +++ b/scripts/buildtools/versions @@ -1,6 +1,6 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/tools v0.9.3 -github.com/algorand/msgp v1.1.55 +github.com/algorand/msgp v1.1.60 github.com/algorand/oapi-codegen v1.12.0-algorand.0 github.com/go-swagger/go-swagger v0.30.4 gotest.tools/gotestsum v1.10.0 diff --git a/scripts/check_deps.sh b/scripts/check_deps.sh index a296c11b93..4022bb544b 100755 --- a/scripts/check_deps.sh +++ b/scripts/check_deps.sh @@ -48,6 +48,7 @@ check_go_binary_version() { if [ "$expected_version" != "$actual_version" ]; then echo "$YELLOW_FG[WARNING]$END_FG_COLOR $binary_name version mismatch, expected $expected_version, but got $actual_version" + echo "Use 'install_buildtools.sh' to fix." fi } diff --git a/scripts/check_license.sh b/scripts/check_license.sh index 9e40983f93..51f83d7d4f 100755 --- a/scripts/check_license.sh +++ b/scripts/check_license.sh @@ -4,7 +4,7 @@ PROJECT_ROOT=$(git rev-parse --show-toplevel) LICENSE_LOCATION="$PROJECT_ROOT"/scripts/LICENSE_HEADER NUMLINES=$(< "$LICENSE_LOCATION" wc -l | tr -d ' ') LICENSE=$(sed "s/{DATE_Y}/$(date +"%Y")/" "$LICENSE_LOCATION") -VERSIONED_GO_FILES=$(git ls-tree --full-tree --name-only -r HEAD | grep "\.go$" | grep -v "^msgp/") +VERSIONED_GO_FILES=$(git ls-tree --full-tree --name-only -r HEAD | grep "\.go$") EXCLUDE=( "Code generated by" "David Lazar" diff --git a/scripts/configure_dev.sh b/scripts/configure_dev.sh index d40b551474..c7bd93a250 100755 --- a/scripts/configure_dev.sh +++ b/scripts/configure_dev.sh @@ -13,24 +13,30 @@ Options: FORCE=false while getopts ":sfh" opt; do - case ${opt} in - f ) FORCE=true - ;; - h ) echo "${HELP}" + case ${opt} in + f) + FORCE=true + ;; + h) + echo "${HELP}" exit 0 - ;; - \? ) echo "${HELP}" + ;; + \?) + echo "${HELP}" exit 2 - ;; - esac + ;; + esac done -SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" +SCRIPTPATH="$( + cd "$(dirname "$0")" + pwd -P +)" OS=$("$SCRIPTPATH"/ostype.sh) function install_or_upgrade { - if ${FORCE} ; then + if ${FORCE}; then BREW_FORCE="-f" fi if brew ls --versions "$1" >/dev/null; then @@ -43,30 +49,30 @@ function install_or_upgrade { function install_windows_shellcheck() { version="v0.7.1" if ! wget https://github.com/koalaman/shellcheck/releases/download/$version/shellcheck-$version.zip -O /tmp/shellcheck-$version.zip; then - rm /tmp/shellcheck-$version.zip &> /dev/null + rm /tmp/shellcheck-$version.zip &>/dev/null echo "Error downloading shellcheck $version" return 1 fi if ! unzip -o /tmp/shellcheck-$version.zip shellcheck-$version.exe -d /tmp; then - rm /tmp/shellcheck-$version.zip &> /dev/null + rm /tmp/shellcheck-$version.zip &>/dev/null echo "Unable to decompress shellcheck $version" return 1 fi if ! mv -f /tmp/shellcheck-$version.exe /usr/bin/shellcheck.exe; then - rm /tmp/shellcheck-$version.zip &> /dev/null + rm /tmp/shellcheck-$version.zip &>/dev/null echo "Unable to move shellcheck to /usr/bin" return 1 fi - rm /tmp/shellcheck-$version.zip &> /dev/null + rm /tmp/shellcheck-$version.zip &>/dev/null return 0 } if [ "${OS}" = "linux" ]; then - if ! which sudo > /dev/null; then + if ! which sudo >/dev/null; then "$SCRIPTPATH/install_linux_deps.sh" else sudo "$SCRIPTPATH/install_linux_deps.sh" @@ -74,7 +80,13 @@ if [ "${OS}" = "linux" ]; then elif [ "${OS}" = "darwin" ]; then if [ "${CIRCLECI}" != "true" ]; then brew update - brew tap homebrew/cask + brew_version=$(brew --version | head -1 | cut -d' ' -f2) + major_version=$(echo $brew_version | cut -d. -f1) + minor_version=$(echo $brew_version | cut -d. -f2) + version_decimal="$major_version.$minor_version" + if (($(echo "$version_decimal < 2.5" | bc -l))); then + brew tap homebrew/cask + fi fi install_or_upgrade pkg-config install_or_upgrade libtool diff --git a/scripts/release/mule/deploy/releases_page/generate_releases_page.py b/scripts/release/mule/deploy/releases_page/generate_releases_page.py index 88f70c6384..07d7ce70a0 100755 --- a/scripts/release/mule/deploy/releases_page/generate_releases_page.py +++ b/scripts/release/mule/deploy/releases_page/generate_releases_page.py @@ -213,12 +213,19 @@ def main(): # 'releases/beta/f9fa9a084_2.5.2' => [file_obj1, file_obj2, ...] release_sets = get_stage_release_set(staging_response) + release_contents = [] # List everything from the releases bucket s3://algorand-releases/ releases_response = s3.list_objects_v2(Bucket=releases_bucket) + release_contents.extend(releases_response["Contents"]) + + # If response was truncated, keep looping and appending + while releases_response["IsTruncated"] == True: + releases_response = s3.list_objects_v2(Bucket=releases_bucket, ContinuationToken=releases_response["NextContinuationToken"]) + release_contents.extend(releases_response["Contents"]) # Return dict keyed by filename of file_objs from # s3://algorand-releases/ - release_files = objects_by_fname(releases_response["Contents"]) + release_files = objects_by_fname(release_contents) table = [] diff --git a/scripts/release/mule/deploy/releases_page/html.tpl b/scripts/release/mule/deploy/releases_page/html.tpl index c52f342d45..3c8f78bc9b 100644 --- a/scripts/release/mule/deploy/releases_page/html.tpl +++ b/scripts/release/mule/deploy/releases_page/html.tpl @@ -11,7 +11,7 @@

See Algorand Developer Resources for instructions on installation and getting started

The Algorand public key to verify these files (except RPM**) is at https://releases.algorand.com/key.pub

The public key for verifying RPMs is https://releases.algorand.com/rpm/rpm_algorand.pub

-

** The RPM package for the 2.0.3 release was signed with the https://releases.algorand.com/key.pub. All other releases will have been signed with the RPM key as noted.

+

The public key for verifying binaries out of our CI builds is https://releases.algorand.com/dev_ci_build.pub


diff --git a/scripts/release/mule/deploy/rpm/deploy.sh b/scripts/release/mule/deploy/rpm/deploy.sh index 1e9719df55..f660f1d01b 100755 --- a/scripts/release/mule/deploy/rpm/deploy.sh +++ b/scripts/release/mule/deploy/rpm/deploy.sh @@ -17,25 +17,25 @@ VERSION=${VERSION:-$(./scripts/compute_build_number.sh -f)} NO_DEPLOY=${NO_DEPLOY:-false} OS_TYPE=$(./scripts/release/mule/common/ostype.sh) PACKAGES_DIR=${PACKAGES_DIR:-"./tmp/node_pkgs/$OS_TYPE/$ARCH_TYPE"} +STAGING=${STAGING:-"algorand-staging/releases"} if [ -n "$S3_SOURCE" ] then PREFIX="$S3_SOURCE/$CHANNEL/$VERSION" - - aws s3 cp "s3://$PREFIX/algorand-$VERSION-1.x86_64.rpm" /root - aws s3 cp "s3://$PREFIX/algorand-devtools-$VERSION-1.x86_64.rpm" /root + if [ "$CHANNEL" == "beta" ] + then + aws s3 cp "s3://$PREFIX/algorand-beta-$VERSION-1.x86_64.rpm" /root + aws s3 cp "s3://$PREFIX/algorand-devtools-beta-$VERSION-1.x86_64.rpm" /root + else + aws s3 cp "s3://$PREFIX/algorand-$VERSION-1.x86_64.rpm" /root + aws s3 cp "s3://$PREFIX/algorand-devtools-$VERSION-1.x86_64.rpm" /root + fi else cp "$PACKAGES_DIR"/*"$VERSION"*.rpm /root fi pushd /root -aws s3 cp s3://algorand-devops-misc/tools/gnupg2.2.9_centos7_amd64.tar.bz2 . -tar jxf gnupg*.tar.bz2 - -export PATH="/root/gnupg2/bin:$PATH" -export LD_LIBRARY_PATH=/root/gnupg2/lib - mkdir -p .gnupg chmod 400 .gnupg touch .gnupg/gpg.conf @@ -51,20 +51,14 @@ echo "wat" | gpg -u rpm@algorand.com --clearsign cat << EOF > .rpmmacros %_gpg_name Algorand RPM -%__gpg /root/gnupg2/bin/gpg +%__gpg /usr/bin/gpg2 %__gpg_check_password_cmd true EOF -cat << EOF > rpmsign.py -import rpm -import sys -rpm.addSign(sys.argv[1], '') -EOF - mkdir rpmrepo for rpm in $(ls *"$VERSION"*.rpm) do - python2 rpmsign.py "$rpm" + rpmsign --addsign "$rpm" cp -p "$rpm" rpmrepo done @@ -78,6 +72,8 @@ then cp -r /root/rpmrepo . else aws s3 sync rpmrepo "s3://algorand-releases/rpm/$CHANNEL/" + # sync signatures to releases so that the .sig files load from there + aws s3 sync s3://$STAGING/releases/$CHANNEL/ s3://algorand-releases/rpm/sigs/$CHANNEL/ --exclude='*' --include='*.rpm.sig' fi echo diff --git a/scripts/upload_config.sh b/scripts/upload_config.sh index 6ada497d2b..e075cccfdc 100755 --- a/scripts/upload_config.sh +++ b/scripts/upload_config.sh @@ -34,6 +34,9 @@ SRCPATH=${SCRIPTPATH}/.. export CHANNEL=$2 export FULLVERSION=$($SRCPATH/scripts/compute_build_number.sh -f) +# prevent ._* files from being included in the tarball +export COPYFILE_DISABLE=true + TEMPDIR=$(mktemp -d -t "upload_config.tmp.XXXXXX") TARFILE=${TEMPDIR}/config_${CHANNEL}_${FULLVERSION}.tar.gz diff --git a/stateproof/msgp_gen.go b/stateproof/msgp_gen.go index 0339ba3ea3..f7a04efecd 100644 --- a/stateproof/msgp_gen.go +++ b/stateproof/msgp_gen.go @@ -83,11 +83,11 @@ func (_ *sigFromAddr) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *sigFromAddr) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0001 int @@ -277,11 +277,11 @@ func (_ *spProver) CanMarshalMsg(z interface{}) bool { // UnmarshalMsg implements msgp.Unmarshaler func (z *spProver) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []byte, err error) { - if st.Depth == 0 { + if st.AllowableDepth == 0 { err = msgp.ErrMaxDepthExceeded{} return } - st.Depth-- + st.AllowableDepth-- var field []byte _ = field var zb0003 int diff --git a/test/e2e-go/features/catchup/basicCatchup_test.go b/test/e2e-go/features/catchup/basicCatchup_test.go index 7663e54b3e..212d4cac9b 100644 --- a/test/e2e-go/features/catchup/basicCatchup_test.go +++ b/test/e2e-go/features/catchup/basicCatchup_test.go @@ -17,6 +17,7 @@ package catchup import ( + "fmt" "os" "path/filepath" "testing" @@ -98,20 +99,23 @@ func TestCatchupOverGossip(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - t.Parallel() - syncTest := fixtures.SynchronizedTest(t) supportedVersions := network.SupportedProtocolVersions require.LessOrEqual(syncTest, len(supportedVersions), 3) + subTest := func(tt *testing.T, ledgerVer, fetcherVer string) { + tt.Run(fmt.Sprintf("ledger=%s,fetcher=%s", ledgerVer, fetcherVer), + func(t *testing.T) { runCatchupOverGossip(t, ledgerVer, fetcherVer) }) + } + // ledger node upgraded version, fetcher node upgraded version // Run with the default values. Instead of "", pass the default value // to exercise loading it from the config file. runCatchupOverGossip(syncTest, supportedVersions[0], supportedVersions[0]) for i := 1; i < len(supportedVersions); i++ { - runCatchupOverGossip(t, supportedVersions[i], "") - runCatchupOverGossip(t, "", supportedVersions[i]) - runCatchupOverGossip(t, supportedVersions[i], supportedVersions[i]) + subTest(t, supportedVersions[i], "") + subTest(t, "", supportedVersions[i]) + subTest(t, supportedVersions[i], supportedVersions[i]) } } diff --git a/test/e2e-go/features/catchup/catchpointCatchup_test.go b/test/e2e-go/features/catchup/catchpointCatchup_test.go index ecbecbdb87..f9f62b3d06 100644 --- a/test/e2e-go/features/catchup/catchpointCatchup_test.go +++ b/test/e2e-go/features/catchup/catchpointCatchup_test.go @@ -316,7 +316,7 @@ func TestCatchpointCatchupFailure(t *testing.T) { err = primaryNode.StopAlgod() a.NoError(err) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.ErrorContains(err, node.MakeStartCatchpointError(catchpointLabel, fmt.Errorf("")).Error()) } @@ -358,7 +358,7 @@ func TestBasicCatchpointCatchup(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) err = fixture.ClientWaitForRoundWithTimeout(usingNodeRestClient, uint64(targetCatchpointRound+1)) @@ -534,7 +534,7 @@ func TestNodeTxHandlerRestart(t *testing.T) { lastCatchpoint := waitForCatchpointGeneration(t, &fixture, relayClient, basics.Round(targetCatchpointRound)) // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) status1, err := client1.Status() @@ -647,7 +647,7 @@ func TestReadyEndpoint(t *testing.T) { // Then when the primary node is at target round, it should satisfy ready 200 condition // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) // The primary node is catching up with its previous catchpoint @@ -789,7 +789,7 @@ func TestNodeTxSyncRestart(t *testing.T) { _, err = fixture.StartNode(primaryNode.GetDataDir()) a.NoError(err) // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) // the transaction should not be confirmed yet diff --git a/test/e2e-go/features/catchup/stateproofsCatchup_test.go b/test/e2e-go/features/catchup/stateproofsCatchup_test.go index 7f11ca37f5..4d3140c8d0 100644 --- a/test/e2e-go/features/catchup/stateproofsCatchup_test.go +++ b/test/e2e-go/features/catchup/stateproofsCatchup_test.go @@ -93,7 +93,7 @@ func TestStateProofInReplayCatchpoint(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) // waiting for fastcatchup to start @@ -169,7 +169,7 @@ func TestStateProofAfterCatchpoint(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) roundAfterSPGeneration := targetCatchpointRound.RoundUpToMultipleOf(basics.Round(consensusParams.StateProofInterval)) + @@ -258,7 +258,7 @@ func TestSendSigsAfterCatchpointCatchup(t *testing.T) { targetCatchpointRound := getFirstCatchpointRound(&consensusParams) catchpointLabel := waitForCatchpointGeneration(t, &fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) err = fixture.ClientWaitForRoundWithTimeout(usingNodeRestClient, uint64(targetCatchpointRound)+1) diff --git a/test/e2e-go/features/p2p/p2p_basic_test.go b/test/e2e-go/features/p2p/p2p_basic_test.go new file mode 100644 index 0000000000..726a77cfe5 --- /dev/null +++ b/test/e2e-go/features/p2p/p2p_basic_test.go @@ -0,0 +1,63 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package p2p + +import ( + "path/filepath" + "testing" + "time" + + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/test/framework/fixtures" + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func testP2PWithConfig(t *testing.T, cfgname string) { + r := require.New(fixtures.SynchronizedTest(t)) + + var fixture fixtures.RestClientFixture + + // Make protocol faster for shorter tests + consensus := make(config.ConsensusProtocols) + fastProtocol := config.Consensus[protocol.ConsensusCurrentVersion] + fastProtocol.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} + fastProtocol.AgreementFilterTimeoutPeriod0 = 400 * time.Millisecond + fastProtocol.AgreementFilterTimeout = 400 * time.Millisecond + consensus[protocol.ConsensusCurrentVersion] = fastProtocol + fixture.SetConsensus(consensus) + + fixture.Setup(t, filepath.Join("nettemplates", cfgname)) + defer fixture.ShutdownImpl(true) // preserve logs in testdir + + _, err := fixture.NC.AlgodClient() + r.NoError(err) + + err = fixture.WaitForRound(10, 30*time.Second) + r.NoError(err) +} + +func TestP2PTwoNodes(t *testing.T) { + partitiontest.PartitionTest(t) + testP2PWithConfig(t, "TwoNodes50EachP2P.json") +} + +func TestP2PFiveNodes(t *testing.T) { + partitiontest.PartitionTest(t) + testP2PWithConfig(t, "FiveNodesP2P.json") +} diff --git a/test/e2e-go/features/privatenet/privatenet_test.go b/test/e2e-go/features/privatenet/privatenet_test.go new file mode 100644 index 0000000000..312abed618 --- /dev/null +++ b/test/e2e-go/features/privatenet/privatenet_test.go @@ -0,0 +1,62 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +// Check that private networks are started as designed. +package privatenet + +import ( + "testing" + + "github.com/algorand/go-algorand/test/framework/fixtures" + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +// TestPrivateNetworkImportKeys tests that part keys can be exported and +// imported when starting a private network. +func TestPrivateNetworkImportKeys(t *testing.T) { + partitiontest.PartitionTest(t) + + // This test takes 5~10 seconds. + if testing.Short() { + t.Skip() + } + + // First test that keys can be exported by using `goal network pregen ...` + // Don't start up network, just create genesis files. + var goalFixture fixtures.GoalFixture + tmpGenDir := t.TempDir() + tmpNetDir := t.TempDir() + defaultTemplate := "" // Use the default template by omitting the filepath. + + _, err := goalFixture.NetworkPregen(defaultTemplate, tmpGenDir) + require.NoError(t, err) + + // Check that if there is an existing directory with same name, test fails. + errStr, err := goalFixture.NetworkPregen(defaultTemplate, tmpGenDir) + require.Error(t, err) + require.Contains(t, errStr, "already exists and is not empty") + + // Then try importing files from same template. + err = goalFixture.NetworkCreate(tmpNetDir, "", defaultTemplate, tmpGenDir) + require.NoError(t, err) + + err = goalFixture.NetworkStart(tmpNetDir) + require.NoError(t, err) + + err = goalFixture.NetworkStop(tmpNetDir) + require.NoError(t, err) +} diff --git a/test/e2e-go/restAPI/helpers.go b/test/e2e-go/restAPI/helpers.go index b1067ecc93..3e85020e94 100644 --- a/test/e2e-go/restAPI/helpers.go +++ b/test/e2e-go/restAPI/helpers.go @@ -117,7 +117,7 @@ func WaitForRoundOne(t *testing.T, testClient libgoal.Client) { var errWaitForTransactionTimeout = errors.New("wait for transaction timed out") // WaitForTransaction waits for a transaction to be confirmed -func WaitForTransaction(t *testing.T, testClient libgoal.Client, fromAddress, txID string, timeout time.Duration) (tx v2.PreEncodedTxInfo, err error) { +func WaitForTransaction(t *testing.T, testClient libgoal.Client, txID string, timeout time.Duration) (tx v2.PreEncodedTxInfo, err error) { a := require.New(fixtures.SynchronizedTest(t)) rnd, err := testClient.Status() a.NoError(err) diff --git a/test/e2e-go/restAPI/other/appsRestAPI_test.go b/test/e2e-go/restAPI/other/appsRestAPI_test.go index 0fe44e3b28..8abe6a4dbc 100644 --- a/test/e2e-go/restAPI/other/appsRestAPI_test.go +++ b/test/e2e-go/restAPI/other/appsRestAPI_test.go @@ -101,7 +101,7 @@ return a.NoError(err) appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, appCreateTxID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) // get app ID @@ -115,7 +115,7 @@ return appFundTxn, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, createdAppID.Address().String(), 0, 1_000_000, nil, "", 0, 0) a.NoError(err) appFundTxID := appFundTxn.ID() - _, err = helper.WaitForTransaction(t, testClient, someAddress, appFundTxID.String(), 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appFundTxID.String(), 30*time.Second) a.NoError(err) // call app, which will issue an ASA create inner txn @@ -125,7 +125,7 @@ return a.NoError(err) appCallTxnTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCallTxn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, appCallTxnTxID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appCallTxnTxID, 30*time.Second) a.NoError(err) // verify pending txn info of outer txn @@ -240,7 +240,7 @@ end: a.NoError(err) appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, appCreateTxID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) // get app ID @@ -257,7 +257,7 @@ end: ) a.NoError(err) appFundTxID := appFundTxn.ID() - _, err = helper.WaitForTransaction(t, testClient, someAddress, appFundTxID.String(), 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appFundTxID.String(), 30*time.Second) a.NoError(err) createdBoxName := map[string]bool{} @@ -306,7 +306,7 @@ end: err = testClient.BroadcastTransactionGroup(stxns) if len(errPrefix) == 0 { a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, txns[0].ID().String(), 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txns[0].ID().String(), 30*time.Second) a.NoError(err) } else { a.ErrorContains(err, errPrefix[0]) @@ -545,7 +545,7 @@ end: a.NoError(err) appDeleteTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appDeleteTxn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, appDeleteTxID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appDeleteTxID, 30*time.Second) a.NoError(err) _, err = testClient.ApplicationInformation(uint64(createdAppID)) diff --git a/test/e2e-go/restAPI/restClient_test.go b/test/e2e-go/restAPI/restClient_test.go index 3929939c8b..65292563da 100644 --- a/test/e2e-go/restAPI/restClient_test.go +++ b/test/e2e-go/restAPI/restClient_test.go @@ -285,7 +285,7 @@ func TestClientCanSendAndGetNote(t *testing.T) { note := make([]byte, maxTxnNoteBytes) tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, note, "", 0, 0) a.NoError(err) - txStatus, err := WaitForTransaction(t, testClient, someAddress, tx.ID().String(), 30*time.Second) + txStatus, err := WaitForTransaction(t, testClient, tx.ID().String(), 30*time.Second) a.NoError(err) a.Equal(note, txStatus.Txn.Txn.Note) } @@ -311,7 +311,7 @@ func TestClientCanGetTransactionStatus(t *testing.T) { t.Log(string(protocol.EncodeJSON(tx))) a.NoError(err) t.Log(tx.ID().String()) - _, err = WaitForTransaction(t, testClient, someAddress, tx.ID().String(), 30*time.Second) + _, err = WaitForTransaction(t, testClient, tx.ID().String(), 30*time.Second) a.NoError(err) } @@ -336,7 +336,7 @@ func TestAccountBalance(t *testing.T) { a.NoError(err) tx, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, toAddress, 10000, 100000, nil, "", 0, 0) a.NoError(err) - _, err = WaitForTransaction(t, testClient, someAddress, tx.ID().String(), 30*time.Second) + _, err = WaitForTransaction(t, testClient, tx.ID().String(), 30*time.Second) a.NoError(err) account, err := testClient.AccountInformation(toAddress, false) @@ -403,7 +403,7 @@ func TestAccountParticipationInfo(t *testing.T) { } txID, err := testClient.SignAndBroadcastTransaction(wh, nil, tx) a.NoError(err) - _, err = WaitForTransaction(t, testClient, someAddress, txID, 30*time.Second) + _, err = WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) account, err := testClient.AccountInformation(someAddress, false) diff --git a/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go b/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go index b23fb4f83d..9f4b66d57b 100644 --- a/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go +++ b/test/e2e-go/restAPI/simulate/simulateRestAPI_test.go @@ -218,6 +218,149 @@ func TestSimulateTransaction(t *testing.T) { a.Zero(closeToBalance) } +func TestSimulateStartRound(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + if testing.Short() { + t.Skip() + } + t.Parallel() + a := require.New(fixtures.SynchronizedTest(t)) + + var fixture fixtures.RestClientFixture + fixture.Setup(t, filepath.Join("nettemplates", "TwoNodesFollower100Second.json")) + defer fixture.Shutdown() + + // Get controller for Primary node + nc, err := fixture.GetNodeController("Primary") + a.NoError(err) + + testClient := fixture.LibGoalClient + + wh, err := testClient.GetUnencryptedWalletHandle() + a.NoError(err) + addresses, err := testClient.ListAddresses(wh) + a.NoError(err) + _, senderAddress := helper.GetMaxBalAddr(t, testClient, addresses) + if senderAddress == "" { + t.Error("no addr with funds") + } + a.NoError(err) + + approvalSrc := `#pragma version 8 +global Round +itob +log +int 1` + clearStateSrc := `#pragma version 8 +int 1` + ops, err := logic.AssembleString(approvalSrc) + a.NoError(err) + approval := ops.Program + ops, err = logic.AssembleString(clearStateSrc) + a.NoError(err) + clearState := ops.Program + + txn, err := testClient.MakeUnsignedApplicationCallTx( + 0, nil, nil, nil, + nil, nil, transactions.NoOpOC, + approval, clearState, basics.StateSchema{}, basics.StateSchema{}, 0, + ) + a.NoError(err) + txn, err = testClient.FillUnsignedTxTemplate(senderAddress, 1, 1001, 0, txn) + a.NoError(err) + stxn, err := testClient.SignTransactionWithWallet(wh, nil, txn) + a.NoError(err) + + // Get controller for follower node + followControl, err := fixture.GetNodeController("Follower") + a.NoError(err) + followClient := fixture.GetAlgodClientForController(followControl) + + // Set sync round on follower + followerSyncRound := uint64(4) + err = followClient.SetSyncRound(followerSyncRound) + a.NoError(err) + + cfg, err := config.LoadConfigFromDisk(followControl.GetDataDir()) + a.NoError(err) + + // Let the primary node make some progress + primaryClient := fixture.GetAlgodClientForController(nc) + err = fixture.ClientWaitForRoundWithTimeout(primaryClient, followerSyncRound+uint64(cfg.MaxAcctLookback)) + a.NoError(err) + + // Let follower node progress as far as it can + err = fixture.ClientWaitForRoundWithTimeout(followClient, followerSyncRound+uint64(cfg.MaxAcctLookback)-1) + a.NoError(err) + + simulateRequest := v2.PreEncodedSimulateRequest{ + TxnGroups: []v2.PreEncodedSimulateRequestTransactionGroup{ + { + Txns: []transactions.SignedTxn{stxn}, + }, + }, + } + + // Simulate transactions against the follower node + simulateTransactions := func(request v2.PreEncodedSimulateRequest) (result v2.PreEncodedSimulateResponse, err error) { + encodedRequest := protocol.EncodeReflect(&request) + var resp []byte + resp, err = followClient.RawSimulateRawTransaction(encodedRequest) + if err != nil { + return + } + err = protocol.DecodeReflect(resp, &result) + return + } + + // Test default behavior (should use latest round available) + result, err := simulateTransactions(simulateRequest) + a.NoError(err) + a.Len(result.TxnGroups, 1) + a.Empty(result.TxnGroups[0].FailureMessage) + a.Len(result.TxnGroups[0].Txns, 1) + a.NotNil(result.TxnGroups[0].Txns[0].Txn.Logs) + a.Len(*result.TxnGroups[0].Txns[0].Txn.Logs, 1) + a.Equal(followerSyncRound+uint64(cfg.MaxAcctLookback), binary.BigEndian.Uint64((*result.TxnGroups[0].Txns[0].Txn.Logs)[0])) + + // Test with previous rounds + for i := uint64(0); i < cfg.MaxAcctLookback; i++ { + simulateRequest.Round = basics.Round(followerSyncRound + i) + result, err = simulateTransactions(simulateRequest) + a.NoError(err) + a.Len(result.TxnGroups, 1) + a.Empty(result.TxnGroups[0].FailureMessage) + a.Len(result.TxnGroups[0].Txns, 1) + a.NotNil(result.TxnGroups[0].Txns[0].Txn.Logs) + a.Len(*result.TxnGroups[0].Txns[0].Txn.Logs, 1) + a.LessOrEqual(followerSyncRound+i+1, binary.BigEndian.Uint64((*result.TxnGroups[0].Txns[0].Txn.Logs)[0])) + } + + // If the round is too far back, we should get an error saying so. + simulateRequest.Round = basics.Round(followerSyncRound - 3) + endTime := time.Now().Add(6 * time.Second) + for { + result, err = simulateTransactions(simulateRequest) + if err != nil || endTime.After(time.Now()) { + break + } + time.Sleep(500 * time.Millisecond) + } + if err == nil { + // NOTE: The ledger can have variability in when it commits rounds to the database. It's + // possible that older rounds are still available because of this. If so, let's bail on the + // test. + t.Logf("Still producing a result for round %d", simulateRequest.Round) + return + } + var httpErr client.HTTPError + a.ErrorAs(err, &httpErr) + a.Equal(http.StatusInternalServerError, httpErr.StatusCode) + a.Contains(httpErr.ErrorString, fmt.Sprintf("round %d before dbRound", simulateRequest.Round)) +} + func TestSimulateWithOptionalSignatures(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -338,7 +481,7 @@ int 1` // sign and broadcast appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, appCreateTxID, 30*time.Second) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) // get app ID @@ -353,7 +496,7 @@ int 1` ) a.NoError(err) appFundTxID := appFundTxn.ID() - _, err = helper.WaitForTransaction(t, testClient, senderAddress, appFundTxID.String(), 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appFundTxID.String(), 30*time.Second) a.NoError(err) // construct app call @@ -466,7 +609,7 @@ int 1` // sign and broadcast appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, appCreateTxID, 30*time.Second) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) // get app ID @@ -481,7 +624,7 @@ int 1` ) a.NoError(err) appFundTxID := appFundTxn.ID() - _, err = helper.WaitForTransaction(t, testClient, senderAddress, appFundTxID.String(), 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, appFundTxID.String(), 30*time.Second) a.NoError(err) // construct app call @@ -732,7 +875,7 @@ func TestMaxDepthAppWithPCandStackTrace(t *testing.T) { appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, appCreateTxID, 30*time.Second) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) futureAppID := basics.AppIndex(*submittedAppCreateTxn.ApplicationIndex) @@ -1580,7 +1723,7 @@ func TestSimulateScratchSlotChange(t *testing.T) { appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, appCreateTxID, 30*time.Second) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) futureAppID := basics.AppIndex(*submittedAppCreateTxn.ApplicationIndex) @@ -1774,7 +1917,7 @@ end: appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) a.NoError(err) - submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, appCreateTxID, 30*time.Second) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) a.NoError(err) futureAppID := basics.AppIndex(*submittedAppCreateTxn.ApplicationIndex) @@ -1964,6 +2107,340 @@ end: }, *resp.TxnGroups[0].Txns[2].TransactionTrace.ApprovalProgramTrace) } +func TestSimulateExecTraceAppInitialState(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + a := require.New(fixtures.SynchronizedTest(t)) + var localFixture fixtures.RestClientFixture + localFixture.SetupNoStart(t, filepath.Join("nettemplates", "OneNodeFuture.json")) + + // Get primary node + primaryNode, err := localFixture.GetNodeController("Primary") + a.NoError(err) + + localFixture.Start() + defer primaryNode.FullStop() + + // get lib goal client + testClient := localFixture.LibGoalFixture.GetLibGoalClientFromNodeController(primaryNode) + + _, err = testClient.WaitForRound(1) + a.NoError(err) + + wh, err := testClient.GetUnencryptedWalletHandle() + a.NoError(err) + addresses, err := testClient.ListAddresses(wh) + a.NoError(err) + _, senderAddress := helper.GetMaxBalAddr(t, testClient, addresses) + a.NotEmpty(senderAddress, "no addr with funds") + + addressDigest, err := basics.UnmarshalChecksumAddress(senderAddress) + a.NoError(err) + + ops, err := logic.AssembleString( + `#pragma version 8 +txn ApplicationID +bz end // Do nothing during create + +txn OnCompletion +int OptIn +== +bnz end // Always allow optin + +byte "local" +byte "global" +txn ApplicationArgs 0 +match local global +err // Unknown command + +local: + txn Sender + byte "local-int-key" + int 0xcafeb0ba + app_local_put + int 0 + byte "local-bytes-key" + byte "xqcL" + app_local_put + b end + +global: + byte "global-int-key" + int 0xdeadbeef + app_global_put + byte "global-bytes-key" + byte "welt am draht" + app_global_put + b end + +end: + int 1`) + a.NoError(err) + approval := ops.Program + + ops, err = logic.AssembleString("#pragma version 8\nint 1") + a.NoError(err) + clearState := ops.Program + + gl := basics.StateSchema{NumByteSlice: 1, NumUint: 1} + lc := basics.StateSchema{NumByteSlice: 1, NumUint: 1} + + MinFee := config.Consensus[protocol.ConsensusFuture].MinTxnFee + MinBalance := config.Consensus[protocol.ConsensusFuture].MinBalance + + // create app and get the application ID + appCreateTxn, err := testClient.MakeUnsignedAppCreateTx( + transactions.NoOpOC, approval, clearState, gl, + lc, nil, nil, nil, nil, nil, 0) + a.NoError(err) + appCreateTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, 0, appCreateTxn) + a.NoError(err) + + appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) + a.NoError(err) + submittedAppCreateTxn, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) + a.NoError(err) + futureAppID := basics.AppIndex(*submittedAppCreateTxn.ApplicationIndex) + + // fund app account + _, err = testClient.ConstructPayment( + senderAddress, futureAppID.Address().String(), + 0, MinBalance*2, nil, "", [32]byte{}, 0, 0, + ) + a.NoError(err) + + // construct app call "global" + appCallGlobalTxn, err := testClient.MakeUnsignedAppNoOpTx( + uint64(futureAppID), [][]byte{[]byte("global")}, nil, nil, nil, nil, + ) + a.NoError(err) + appCallGlobalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallGlobalTxn) + a.NoError(err) + // construct app optin + appOptInTxn, err := testClient.MakeUnsignedAppOptInTx(uint64(futureAppID), nil, nil, nil, nil, nil) + a.NoError(err) + appOptInTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appOptInTxn) + // construct app call "local" + appCallLocalTxn, err := testClient.MakeUnsignedAppNoOpTx( + uint64(futureAppID), [][]byte{[]byte("local")}, nil, nil, nil, nil, + ) + a.NoError(err) + appCallLocalTxn, err = testClient.FillUnsignedTxTemplate(senderAddress, 0, 0, MinFee, appCallLocalTxn) + a.NoError(err) + + gid, err := testClient.GroupID([]transactions.Transaction{appCallGlobalTxn, appOptInTxn, appCallLocalTxn}) + a.NoError(err) + appCallGlobalTxn.Group = gid + appOptInTxn.Group = gid + appCallLocalTxn.Group = gid + + appCallTxnGlobalSigned, err := testClient.SignTransactionWithWallet(wh, nil, appCallGlobalTxn) + a.NoError(err) + appOptInSigned, err := testClient.SignTransactionWithWallet(wh, nil, appOptInTxn) + a.NoError(err) + appCallTxnLocalSigned, err := testClient.SignTransactionWithWallet(wh, nil, appCallLocalTxn) + a.NoError(err) + + a.NoError(testClient.BroadcastTransactionGroup([]transactions.SignedTxn{ + appCallTxnGlobalSigned, + appOptInSigned, + appCallTxnLocalSigned, + })) + _, err = helper.WaitForTransaction(t, testClient, appCallTxnGlobalSigned.Txn.ID().String(), 30*time.Second) + a.NoError(err) + + // construct simulation request, with state change enabled + execTraceConfig := simulation.ExecTraceConfig{ + Enable: true, + State: true, + } + + appCallGlobalTxn.Note = []byte("note for global") + appCallGlobalTxn.Group = crypto.Digest{} + appCallLocalTxn.Note = []byte("note for local") + appCallLocalTxn.Group = crypto.Digest{} + + gid, err = testClient.GroupID([]transactions.Transaction{appCallGlobalTxn, appCallLocalTxn}) + a.NoError(err) + appCallGlobalTxn.Group = gid + appCallLocalTxn.Group = gid + + appCallTxnGlobalSigned, err = testClient.SignTransactionWithWallet(wh, nil, appCallGlobalTxn) + a.NoError(err) + appCallTxnLocalSigned, err = testClient.SignTransactionWithWallet(wh, nil, appCallLocalTxn) + a.NoError(err) + + simulateRequest := v2.PreEncodedSimulateRequest{ + TxnGroups: []v2.PreEncodedSimulateRequestTransactionGroup{ + {Txns: []transactions.SignedTxn{appCallTxnGlobalSigned, appCallTxnLocalSigned}}, + }, + ExecTraceConfig: execTraceConfig, + } + + // update the configuration file to enable EnableDeveloperAPI + err = primaryNode.FullStop() + a.NoError(err) + cfg, err := config.LoadConfigFromDisk(primaryNode.GetDataDir()) + a.NoError(err) + cfg.EnableDeveloperAPI = true + err = cfg.SaveToDisk(primaryNode.GetDataDir()) + require.NoError(t, err) + localFixture.Start() + + // start real simulating + resp, err := testClient.SimulateTransactions(simulateRequest) + a.NoError(err) + + // assertions + a.Len(resp.TxnGroups, 1) + a.Nil(resp.TxnGroups[0].FailureMessage) + a.Len(resp.TxnGroups[0].Txns, 2) + + a.Equal([]model.SimulationOpcodeTraceUnit{ + {Pc: 1}, + {Pc: 4}, + {Pc: 6}, + {Pc: 9}, + {Pc: 11}, + {Pc: 12}, + {Pc: 13}, + {Pc: 16}, + {Pc: 23}, + {Pc: 31}, + {Pc: 34}, + {Pc: 94}, + {Pc: 110}, + { + Pc: 116, + StateChanges: &[]model.ApplicationStateOperation{ + { + Operation: "w", + AppStateType: "g", + Key: []byte("global-int-key"), + NewValue: &model.AvmValue{ + Type: uint64(basics.TealUintType), + Uint: toPtr[uint64](0xdeadbeef), + }, + }, + }, + }, + {Pc: 117}, + {Pc: 135}, + { + Pc: 150, + StateChanges: &[]model.ApplicationStateOperation{ + { + Operation: "w", + AppStateType: "g", + Key: []byte("global-bytes-key"), + NewValue: &model.AvmValue{ + Type: uint64(basics.TealBytesType), + Bytes: toPtr([]byte("welt am draht")), + }, + }, + }, + }, + {Pc: 151}, + {Pc: 154}, + }, *resp.TxnGroups[0].Txns[0].TransactionTrace.ApprovalProgramTrace) + a.Equal([]model.SimulationOpcodeTraceUnit{ + {Pc: 1}, + {Pc: 4}, + {Pc: 6}, + {Pc: 9}, + {Pc: 11}, + {Pc: 12}, + {Pc: 13}, + {Pc: 16}, + {Pc: 23}, + {Pc: 31}, + {Pc: 34}, + {Pc: 41}, + {Pc: 43}, + {Pc: 58}, + { + Pc: 64, + StateChanges: &[]model.ApplicationStateOperation{ + { + Operation: "w", + AppStateType: "l", + Key: []byte("local-int-key"), + NewValue: &model.AvmValue{ + Type: uint64(basics.TealUintType), + Uint: toPtr[uint64](0xcafeb0ba), + }, + Account: toPtr(addressDigest.String()), + }, + }, + }, + {Pc: 65}, + {Pc: 67}, + {Pc: 84}, + { + Pc: 90, + StateChanges: &[]model.ApplicationStateOperation{ + { + Operation: "w", + AppStateType: "l", + Key: []byte("local-bytes-key"), + NewValue: &model.AvmValue{ + Type: uint64(basics.TealBytesType), + Bytes: toPtr([]byte("xqcL")), + }, + Account: toPtr(addressDigest.String()), + }, + }, + }, + {Pc: 91}, + {Pc: 154}, + }, *resp.TxnGroups[0].Txns[1].TransactionTrace.ApprovalProgramTrace) + + a.NotNil(resp.InitialStates) + a.Len(*resp.InitialStates.AppInitialStates, 1) + + a.Len((*resp.InitialStates.AppInitialStates)[0].AppGlobals.Kvs, 2) + + globalKVs := (*resp.InitialStates.AppInitialStates)[0].AppGlobals.Kvs + globalKVMap := make(map[string]model.AvmValue) + for _, kv := range globalKVs { + globalKVMap[string(kv.Key)] = kv.Value + } + expectedGlobalKVMap := map[string]model.AvmValue{ + "global-int-key": { + Type: 2, + Uint: toPtr[uint64](0xdeadbeef), + }, + "global-bytes-key": { + Type: 1, + Bytes: toPtr([]byte("welt am draht")), + }, + } + a.Equal(expectedGlobalKVMap, globalKVMap) + + a.Len(*(*resp.InitialStates.AppInitialStates)[0].AppLocals, 1) + + localKVs := (*(*resp.InitialStates.AppInitialStates)[0].AppLocals)[0] + a.NotNil(localKVs.Account) + a.Equal(senderAddress, *localKVs.Account) + + localKVMap := make(map[string]model.AvmValue) + for _, kv := range localKVs.Kvs { + localKVMap[string(kv.Key)] = kv.Value + } + expectedLocalKVMap := map[string]model.AvmValue{ + "local-int-key": { + Type: 2, + Uint: toPtr[uint64](0xcafeb0ba), + }, + "local-bytes-key": { + Type: 1, + Bytes: toPtr([]byte("xqcL")), + }, + } + a.Equal(expectedLocalKVMap, localKVMap) +} + func TestSimulateWithUnnamedResources(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -1995,7 +2472,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { ) a.NoError(err) txID := txn.ID().String() - _, err = helper.WaitForTransaction(t, testClient, senderAddress, txID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // create asset @@ -2006,7 +2483,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { // sign and broadcast txID, err = testClient.SignAndBroadcastTransaction(wh, nil, txn) a.NoError(err) - confirmedTxn, err := helper.WaitForTransaction(t, testClient, senderAddress, txID, 30*time.Second) + confirmedTxn, err := helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // get asset ID a.NotNil(confirmedTxn.AssetIndex) @@ -2021,7 +2498,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { // sign and broadcast txID, err = testClient.SignAndBroadcastTransaction(wh, nil, txn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, otherAddress, txID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // transfer asset @@ -2032,7 +2509,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { // sign and broadcast txID, err = testClient.SignAndBroadcastTransaction(wh, nil, txn) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, senderAddress, txID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) ops, err := logic.AssembleString("#pragma version 9\n int 1") @@ -2050,7 +2527,7 @@ func TestSimulateWithUnnamedResources(t *testing.T) { // sign and broadcast txID, err = testClient.SignAndBroadcastTransaction(wh, nil, txn) a.NoError(err) - confirmedTxn, err = helper.WaitForTransaction(t, testClient, otherAddress, txID, 30*time.Second) + confirmedTxn, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // get app ID a.NotNil(confirmedTxn.ApplicationIndex) @@ -2128,7 +2605,7 @@ int 1 // sign and broadcast txID, err = testClient.SignAndBroadcastTransaction(wh, nil, txn) a.NoError(err) - confirmedTxn, err = helper.WaitForTransaction(t, testClient, senderAddress, txID, 30*time.Second) + confirmedTxn, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // get app ID a.NotNil(confirmedTxn.ApplicationIndex) @@ -2142,7 +2619,7 @@ int 1 ) a.NoError(err) txID = txn.ID().String() - _, err = helper.WaitForTransaction(t, testClient, senderAddress, txID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) // construct app call diff --git a/test/e2e-go/restAPI/stateproof/stateproofRestAPI_test.go b/test/e2e-go/restAPI/stateproof/stateproofRestAPI_test.go index 17427ff4f6..497d128643 100644 --- a/test/e2e-go/restAPI/stateproof/stateproofRestAPI_test.go +++ b/test/e2e-go/restAPI/stateproof/stateproofRestAPI_test.go @@ -100,7 +100,7 @@ func TestStateProofInParticipationInfo(t *testing.T) { } txID, err := testClient.SignAndBroadcastTransaction(wh, nil, tx) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, txID, 120*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 120*time.Second) a.NoError(err) account, err := testClient.AccountInformation(someAddress, false) @@ -197,7 +197,7 @@ func TestNilStateProofInParticipationInfo(t *testing.T) { } txID, err := testClient.SignAndBroadcastTransaction(wh, nil, tx) a.NoError(err) - _, err = helper.WaitForTransaction(t, testClient, someAddress, txID, 30*time.Second) + _, err = helper.WaitForTransaction(t, testClient, txID, 30*time.Second) a.NoError(err) account, err := testClient.AccountInformation(someAddress, false) diff --git a/test/framework/fixtures/goalFixture.go b/test/framework/fixtures/goalFixture.go index ef52b51b63..69e43e784a 100644 --- a/test/framework/fixtures/goalFixture.go +++ b/test/framework/fixtures/goalFixture.go @@ -58,19 +58,27 @@ const ( nodeCmd = "node" startCmd = "start" stopCmd = "stop" + + networkCmd = "network" + pregenCmd = "pregen" + createCmd = "create" ) -func (f *GoalFixture) executeCommand(args ...string) (retStdout string, retStderr string, err error) { +func (f *GoalFixture) executeRawCommand(args ...string) (retStdout string, retStderr string, err error) { + // Executes a command without a specified data directory cmd := filepath.Join(f.binDir, goalCmd) - // We always execute goal against the PrimaryDataDir() instance - args = append(args, "-d", f.PrimaryDataDir()) retStdout, retStderr, err = util.ExecAndCaptureOutput(cmd, args...) retStdout = strings.TrimRight(retStdout, "\n") retStderr = strings.TrimRight(retStderr, "\n") - //fmt.Printf("command: %v %v\nret: %v\n", cmd, args, ret) return } +func (f *GoalFixture) executeCommand(args ...string) (retStdout string, retStderr string, err error) { + // We always execute goal against the PrimaryDataDir() instance + args = append(args, "-d", f.PrimaryDataDir()) + return f.executeRawCommand(args...) +} + // combine the error and the output so that we could return it as a single error object. func combineExecuteError(retStdout string, retStderr string, err error) error { if err == nil { @@ -227,3 +235,63 @@ func (f *GoalFixture) AccountImportRootKey(wallet string, createDefaultUnencrypt _, _, err = f.executeCommand(args...) return } + +// NetworkPregen exposes the `goal network pregen` command +func (f *GoalFixture) NetworkPregen(template, pregendir string) (stdErr string, err error) { + args := []string{ + networkCmd, + pregenCmd, + "-p", + pregendir, + } + if template != "" { + args = append(args, "-t", template) + } + _, stdErr, err = f.executeRawCommand(args...) + return +} + +// NetworkCreate exposes the `goal network create` command +func (f *GoalFixture) NetworkCreate(networkdir, networkName, template, pregendir string) (err error) { + args := []string{ + networkCmd, + createCmd, + "-r", + networkdir, + } + if networkName != "" { + args = append(args, "-n", networkName) + } + if template != "" { + args = append(args, "-t", template) + } + if pregendir != "" { + args = append(args, "-p", pregendir) + } + _, _, err = f.executeRawCommand(args...) + return +} + +// NetworkStart exposes the `goal network start` command +func (f *GoalFixture) NetworkStart(networkdir string) (err error) { + args := []string{ + networkCmd, + startCmd, + "-r", + networkdir, + } + _, _, err = f.executeRawCommand(args...) + return +} + +// NetworkStop exposes the `goal network stop` command +func (f *GoalFixture) NetworkStop(networkdir string) (err error) { + args := []string{ + networkCmd, + stopCmd, + "-r", + networkdir, + } + _, _, err = f.executeRawCommand(args...) + return +} diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index c0527fdd62..1cc0b24fb8 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -534,7 +534,7 @@ func (f *LibGoalFixture) TransactionProof(txid string, round uint64, hashType cr return model.TransactionProofResponse{}, merklearray.SingleLeafProof{}, err } - proof, err := merklearray.ProofDataToSingleLeafProof(string(proofResp.Hashtype), proofResp.Treedepth, proofResp.Proof) + proof, err := merklearray.ProofDataToSingleLeafProof(string(proofResp.Hashtype), proofResp.Proof) if err != nil { return model.TransactionProofResponse{}, merklearray.SingleLeafProof{}, err } @@ -550,7 +550,7 @@ func (f *LibGoalFixture) LightBlockHeaderProof(round uint64) (model.LightBlockHe return model.LightBlockHeaderProofResponse{}, merklearray.SingleLeafProof{}, err } - proof, err := merklearray.ProofDataToSingleLeafProof(crypto.Sha256.String(), proofResp.Treedepth, proofResp.Proof) + proof, err := merklearray.ProofDataToSingleLeafProof(crypto.Sha256.String(), proofResp.Proof) if err != nil { return model.LightBlockHeaderProofResponse{}, merklearray.SingleLeafProof{}, err } diff --git a/test/heapwatch/client_ram_report.py b/test/heapwatch/client_ram_report.py index 29642faf15..7833ababa3 100644 --- a/test/heapwatch/client_ram_report.py +++ b/test/heapwatch/client_ram_report.py @@ -181,8 +181,9 @@ def hostports_to_nicks(args, hostports, metrics=None): if not hit: hit = hp out.append(hit) + out.sort() if metrics: - return ['{}#{}'.format(hp, m) for hp in hostports for m in metrics] + return ['{}#{}'.format(hp, m) for hp in out for m in metrics] return out diff --git a/test/heapwatch/plot_crr_csv.py b/test/heapwatch/plot_crr_csv.py index d546aaff3a..00a0aa599b 100755 --- a/test/heapwatch/plot_crr_csv.py +++ b/test/heapwatch/plot_crr_csv.py @@ -118,7 +118,7 @@ def main(): xy = metrics[metric] ax.plot([p[0] for p in xy], [p[1] for p in xy], label=f'{k}/{metric}', color=lc, linestyle=plt_line_styles[i%len(plt_line_styles)]) - ax.legend(loc='upper left', ncol=2) + fig.legend(loc='outside upper left', ncol=4) plt.savefig(fname + '.svg', format='svg') plt.savefig(fname + '.png', format='png') #plt.show() diff --git a/test/scripts/e2e_subs/e2e-teal.sh b/test/scripts/e2e_subs/e2e-teal.sh index 48da729512..7a5975dc9d 100755 --- a/test/scripts/e2e_subs/e2e-teal.sh +++ b/test/scripts/e2e_subs/e2e-teal.sh @@ -157,7 +157,14 @@ printf '\x02' | dd of=${TEMPDIR}/true2.lsig bs=1 seek=0 count=1 conv=notrunc ${gcmd} clerk compile ${TEAL}/quine.teal -m trap 'rm ${TEAL}/quine.teal.*' EXIT if ! diff ${TEAL}/quine.map ${TEAL}/quine.teal.tok.map; then - echo "produced source maps do not match" + echo "produced source maps do not match: ${TEAL}/quine.map vs ${TEAL}/quine.teal.tok.map" + exit 1 +fi + +${gcmd} clerk compile ${TEAL}/sourcemap-test.teal -m +trap 'rm ${TEAL}/sourcemap-test.teal.*' EXIT +if ! diff ${TEAL}/sourcemap-test.map ${TEAL}/sourcemap-test.teal.tok.map; then + echo "produced source maps do not match: ${TEAL}/sourcemap-test.map vs ${TEAL}/sourcemap-test.teal.tok.map" exit 1 fi diff --git a/test/scripts/e2e_subs/tealprogs/quine.map b/test/scripts/e2e_subs/tealprogs/quine.map index 62ea5cc401..c53ca2b928 100644 --- a/test/scripts/e2e_subs/tealprogs/quine.map +++ b/test/scripts/e2e_subs/tealprogs/quine.map @@ -1 +1 @@ -{"version":3,"sources":["test/scripts/e2e_subs/tealprogs/quine.teal"],"names":[],"mappings":";AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA;AACA;;;AACA;;;AACA;AACA;;AACA;AACA;;;AACA;AACA;AACA;;AACA;;AACA;AACA;AACA"} \ No newline at end of file +{"version":3,"sources":["quine.teal"],"names":[],"mappings":";AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA;AACA;;;AACA;;;AACA;AACA;;AACA;AACA;;;AACA;AACA;AACA;;AACA;;AACA;AACA;AACA"} \ No newline at end of file diff --git a/test/scripts/e2e_subs/tealprogs/sourcemap-test.map b/test/scripts/e2e_subs/tealprogs/sourcemap-test.map new file mode 100644 index 0000000000..8ec0931089 --- /dev/null +++ b/test/scripts/e2e_subs/tealprogs/sourcemap-test.map @@ -0,0 +1 @@ +{"version":3,"sources":["sourcemap-test.teal"],"names":[],"mappings":";;;;AAGA;;AAAmB;;AAAO;AAAI;;;AAE9B;;AAAO;;AAAO;AACd;;AAAO;AAAO;AALO;AAAI;AASrB;AACA"} \ No newline at end of file diff --git a/test/scripts/e2e_subs/tealprogs/sourcemap-test.teal b/test/scripts/e2e_subs/tealprogs/sourcemap-test.teal new file mode 100644 index 0000000000..11a916d92a --- /dev/null +++ b/test/scripts/e2e_subs/tealprogs/sourcemap-test.teal @@ -0,0 +1,12 @@ +#pragma version 9 +#define assertEquals ==; assert + +txn ApplicationID; int 0; ==; bz create + +int 3; int 4; +; +int 6; int 1; +; +assertEquals + +create: + int 1 + return diff --git a/test/scripts/test_private_network.sh b/test/scripts/test_private_network.sh index f1adc7f62b..72f0bd9160 100755 --- a/test/scripts/test_private_network.sh +++ b/test/scripts/test_private_network.sh @@ -1,8 +1,10 @@ #!/usr/bin/env bash + +set -euf -o pipefail + echo "######################################################################" echo " test_private_network" echo "######################################################################" -set -e # Suppress telemetry reporting for tests export ALGOTEST=1 diff --git a/test/testdata/configs/config-v29.json b/test/testdata/configs/config-v29.json index 8522011ce6..f08b782bbe 100644 --- a/test/testdata/configs/config-v29.json +++ b/test/testdata/configs/config-v29.json @@ -48,6 +48,7 @@ "EnableLedgerService": false, "EnableMetricReporting": false, "EnableOutgoingNetworkMessageFiltering": true, + "EnableP2P": false, "EnablePingHandler": true, "EnableProcessBlockStats": false, "EnableProfiler": false, @@ -85,8 +86,7 @@ "OptimizeAccountsDatabaseOnStartup": false, "OutgoingMessageFilterBucketCount": 3, "OutgoingMessageFilterBucketSize": 128, - "P2PEnable": false, - "P2PPersistPeerID": true, + "P2PPersistPeerID": false, "P2PPrivateKeyLocation": "", "ParticipationKeysRefreshInterval": 60000000000, "PeerConnectionsUpdateInterval": 3600, diff --git a/test/testdata/configs/config-v30.json b/test/testdata/configs/config-v30.json index 094c7ca7df..5021c8fd40 100644 --- a/test/testdata/configs/config-v30.json +++ b/test/testdata/configs/config-v30.json @@ -87,7 +87,7 @@ "OutgoingMessageFilterBucketCount": 3, "OutgoingMessageFilterBucketSize": 128, "P2PEnable": false, - "P2PPersistPeerID": true, + "P2PPersistPeerID": false, "P2PPrivateKeyLocation": "", "ParticipationKeysRefreshInterval": 60000000000, "PeerConnectionsUpdateInterval": 3600, diff --git a/test/testdata/configs/config-v31.json b/test/testdata/configs/config-v31.json new file mode 100644 index 0000000000..fccf558c44 --- /dev/null +++ b/test/testdata/configs/config-v31.json @@ -0,0 +1,135 @@ +{ + "Version": 31, + "AccountUpdatesStatsInterval": 5000000000, + "AccountsRebuildSynchronousMode": 1, + "AgreementIncomingBundlesQueueLength": 15, + "AgreementIncomingProposalsQueueLength": 50, + "AgreementIncomingVotesQueueLength": 20000, + "AnnounceParticipationKey": true, + "Archival": false, + "BaseLoggerDebugLevel": 4, + "BlockDBDir": "", + "BlockServiceCustomFallbackEndpoints": "", + "BlockServiceMemCap": 500000000, + "BroadcastConnectionsLimit": -1, + "CadaverDirectory": "", + "CadaverSizeTarget": 0, + "CatchpointDir": "", + "CatchpointFileHistoryLength": 365, + "CatchpointInterval": 10000, + "CatchpointTracking": 0, + "CatchupBlockDownloadRetryAttempts": 1000, + "CatchupBlockValidateMode": 0, + "CatchupFailurePeerRefreshRate": 10, + "CatchupGossipBlockFetchTimeoutSec": 4, + "CatchupHTTPBlockFetchTimeoutSec": 4, + "CatchupLedgerDownloadRetryAttempts": 50, + "CatchupParallelBlocks": 16, + "ColdDataDir": "", + "ConnectionsRateLimitingCount": 60, + "ConnectionsRateLimitingWindowSeconds": 1, + "CrashDBDir": "", + "DNSBootstrapID": ".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)", + "DNSSecurityFlags": 1, + "DeadlockDetection": 0, + "DeadlockDetectionThreshold": 30, + "DisableAPIAuth": false, + "DisableLedgerLRUCache": false, + "DisableLocalhostConnectionRateLimit": true, + "DisableNetworking": false, + "DisableOutgoingConnectionThrottling": false, + "EnableAccountUpdatesStats": false, + "EnableAgreementReporting": false, + "EnableAgreementTimeMetrics": false, + "EnableAssembleStats": false, + "EnableBlockService": false, + "EnableBlockServiceFallbackToArchiver": false, + "EnableCatchupFromArchiveServers": false, + "EnableDeveloperAPI": false, + "EnableExperimentalAPI": false, + "EnableFollowMode": false, + "EnableGossipBlockService": true, + "EnableIncomingMessageFilter": false, + "EnableLedgerService": false, + "EnableMetricReporting": false, + "EnableOutgoingNetworkMessageFiltering": true, + "EnableP2P": false, + "EnablePingHandler": true, + "EnableProcessBlockStats": false, + "EnableProfiler": false, + "EnableRequestLogger": false, + "EnableRuntimeMetrics": false, + "EnableTopAccountsReporting": false, + "EnableTxBacklogRateLimiting": true, + "EnableTxnEvalTracer": false, + "EnableUsageLog": false, + "EnableVerbosedTransactionSyncLogging": false, + "EndpointAddress": "127.0.0.1:0", + "FallbackDNSResolverAddress": "", + "ForceFetchTransactions": false, + "ForceRelayMessages": false, + "GossipFanout": 4, + "HeartbeatUpdateInterval": 600, + "HotDataDir": "", + "IncomingConnectionsLimit": 2400, + "IncomingMessageFilterBucketCount": 5, + "IncomingMessageFilterBucketSize": 512, + "LedgerSynchronousMode": 2, + "LogArchiveDir": "", + "LogArchiveMaxAge": "", + "LogArchiveName": "node.archive.log", + "LogFileDir": "", + "LogSizeLimit": 1073741824, + "MaxAPIBoxPerApplication": 100000, + "MaxAPIResourcesPerAccount": 100000, + "MaxAcctLookback": 4, + "MaxBlockHistoryLookback": 0, + "MaxCatchpointDownloadDuration": 43200000000000, + "MaxConnectionsPerIP": 15, + "MinCatchpointFileDownloadBytesPerSecond": 20480, + "NetAddress": "", + "NetworkMessageTraceServer": "", + "NetworkProtocolVersion": "", + "NodeExporterListenAddress": ":9100", + "NodeExporterPath": "./node_exporter", + "OptimizeAccountsDatabaseOnStartup": false, + "OutgoingMessageFilterBucketCount": 3, + "OutgoingMessageFilterBucketSize": 128, + "P2PPersistPeerID": false, + "P2PPrivateKeyLocation": "", + "ParticipationKeysRefreshInterval": 60000000000, + "PeerConnectionsUpdateInterval": 3600, + "PeerPingPeriodSeconds": 0, + "PriorityPeers": {}, + "ProposalAssemblyTime": 500000000, + "PublicAddress": "", + "ReconnectTime": 60000000000, + "ReservedFDs": 256, + "RestConnectionsHardLimit": 2048, + "RestConnectionsSoftLimit": 1024, + "RestReadTimeoutSeconds": 15, + "RestWriteTimeoutSeconds": 120, + "RunHosted": false, + "StateproofDir": "", + "StorageEngine": "sqlite", + "SuggestedFeeBlockHistory": 3, + "SuggestedFeeSlidingWindowSize": 50, + "TLSCertFile": "", + "TLSKeyFile": "", + "TelemetryToLog": true, + "TrackerDBDir": "", + "TransactionSyncDataExchangeRate": 0, + "TransactionSyncSignificantMessageThreshold": 0, + "TxBacklogReservedCapacityPerPeer": 20, + "TxBacklogServiceRateWindowSeconds": 10, + "TxBacklogSize": 26000, + "TxIncomingFilterMaxSize": 500000, + "TxIncomingFilteringFlags": 1, + "TxPoolExponentialIncreaseFactor": 2, + "TxPoolSize": 75000, + "TxSyncIntervalSeconds": 60, + "TxSyncServeResponseSize": 1000000, + "TxSyncTimeoutSeconds": 30, + "UseXForwardedForAddressField": "", + "VerifiedTranscationsCacheSize": 150000 +} diff --git a/test/testdata/nettemplates/FiveNodesP2P.json b/test/testdata/nettemplates/FiveNodesP2P.json new file mode 100644 index 0000000000..db77120d48 --- /dev/null +++ b/test/testdata/nettemplates/FiveNodesP2P.json @@ -0,0 +1,61 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "LastPartKeyRound": 5000, + "Wallets": [ + { + "Name": "LargeWallet", + "Stake": 85, + "Online": true + }, + { + "Name": "SmallWallet", + "Stake": 10, + "Online": true + }, + { + "Name": "NonPartWallet", + "Stake": 5, + "Online": true + } + ] + }, + "Nodes": [ + { + "Name": "Relay1", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "IsRelay": true + }, + { + "Name": "Relay2", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "IsRelay": true + }, + { + "Name": "PartNode1", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [{ + "Name": "LargeWallet", + "ParticipationOnly": true + }], + "PeerList": "Relay1;Relay2" + }, + { + "Name": "PartNode2", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [{ + "Name": "SmallWallet", + "ParticipationOnly": true + }], + "PeerList": "Relay2" + }, + { + "Name": "NonPartNode", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [{ + "Name": "NonPartWallet" + }], + "PeerList": "Relay1" + } + ] +} diff --git a/test/testdata/nettemplates/ThreeNodesEvenDistP2P.json b/test/testdata/nettemplates/ThreeNodesEvenDistP2P.json new file mode 100644 index 0000000000..f6cd8d3cda --- /dev/null +++ b/test/testdata/nettemplates/ThreeNodesEvenDistP2P.json @@ -0,0 +1,50 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "LastPartKeyRound": 3000, + "Wallets": [ + { + "Name": "Wallet1", + "Stake": 33, + "Online": true + }, + { + "Name": "Wallet2", + "Stake": 33, + "Online": true + }, + { + "Name": "Wallet3", + "Stake": 34, + "Online": true + } + ] + }, + "Nodes": [ + { + "Name": "Primary", + "IsRelay": true, + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [ + { "Name": "Wallet1", + "ParticipationOnly": false } + ] + }, + { + "Name": "Node1", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [ + { "Name": "Wallet2", + "ParticipationOnly": false } + ] + }, + { + "Name": "Node2", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [ + { "Name": "Wallet3", + "ParticipationOnly": false } + ] + } + ] +} diff --git a/test/testdata/nettemplates/TwoNodes50EachP2P.json b/test/testdata/nettemplates/TwoNodes50EachP2P.json new file mode 100644 index 0000000000..b5a76f6123 --- /dev/null +++ b/test/testdata/nettemplates/TwoNodes50EachP2P.json @@ -0,0 +1,37 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "LastPartKeyRound": 3000, + "Wallets": [ + { + "Name": "Wallet1", + "Stake": 50, + "Online": true + }, + { + "Name": "Wallet2", + "Stake": 50, + "Online": true + } + ] + }, + "Nodes": [ + { + "Name": "Primary", + "IsRelay": true, + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [ + { "Name": "Wallet1", + "ParticipationOnly": false } + ] + }, + { + "Name": "Node", + "ConfigJSONOverride": "{\"EnableP2P\":true}", + "Wallets": [ + { "Name": "Wallet2", + "ParticipationOnly": false } + ] + } + ] +} diff --git a/tools/block-generator/Makefile b/tools/block-generator/Makefile index fdb5754210..8134a2e517 100644 --- a/tools/block-generator/Makefile +++ b/tools/block-generator/Makefile @@ -1,9 +1,19 @@ -SCENARIO = scenarios/config.allmixed.small.yml -SKIP = --skip-runner +# The following variables are primarily useful for tuning dev testing and +# appear in the targets pg-up, pg-enter, pg-down, pg-query, +# run-runner, run-file-exporter, and benchmark-blocks-export: +SCENARIO = scenarios/benchmarks/stress.50000.yml RESETDB = --reset-db +TIMES = 1 REPORTS = ../../tmp/RUN_RUNNER_OUTPUTS DURATION = 30s VERBOSE = --verbose +CONDUIT = ./conduit +TEMPLATE = # --template file-exporter (default postgres-exporter) +PGUSER = algorand +PGDB = generator_db +PGCONT = "generator-test-container" +PGCONN = "host=localhost user=$(PGUSER) password=algorand dbname=$(PGDB) port=15432 sslmode=disable" +PGRUNNER = --postgres-connection-string $(PGCONN) block-generator: clean-generator go build @@ -11,31 +21,38 @@ block-generator: clean-generator clean-generator: rm -f block-generator -debug-blockgen: - python scripts/run_runner.py \ - --conduit-binary ./conduit \ - --scenario $(SCENARIO) \ - --report-directory $(REPORTS) \ - --keep-alive $(SKIP) \ - --test-duration $(DURATION) \ - $(RESETDB) +pg-up: + docker run --name $(PGCONT) -p 15432:5432 -e POSTGRES_USER=$(PGUSER) -e POSTGRES_PASSWORD=algorand -e POSTGRES_DB=$(PGDB) -d postgres + +pg-enter: + docker exec -it $(PGCONT) psql -U $(PGUSER) -d $(PGDB) -enter-pg: - docker exec -it generator-test-container psql -U algorand -d generator_db +QUERY := -c "select count(*) from txn;" +pg-query: + psql $(PGCONN) $(QUERY) -clean-docker: - docker rm -f generator-test-container +pg-down: + docker rm -f $(PGCONT) run-runner: block-generator - ./block-generator runner --conduit-binary ./conduit \ + ./block-generator runner --conduit-binary $(CONDUIT) \ --keep-data-dir \ --test-duration $(DURATION) \ --conduit-log-level trace \ - --postgres-connection-string "host=localhost user=algorand password=algorand dbname=generator_db port=15432 sslmode=disable" \ + $(TEMPLATE) \ + $(PGRUNNER) \ --scenario $(SCENARIO) \ $(RESETDB) \ $(VERBOSE) \ --report-directory $(REPORTS) + --times $(TIMES) + +run-file-exporter: + make run-runner TEMPLATE="--template file-exporter" TIMES=1 RESETDB= PGRUNNER= + +BENCHMARK = "organic.25000" +benchmark-blocks-export: block-generator + make run-file-exporter DURATION=60s SCENARIO=scenarios/benchmarks/$(BENCHMARK).yml REPORTS=$(BENCHMARK) clean-reports: rm -rf $(REPORTS) diff --git a/tools/block-generator/README.md b/tools/block-generator/README.md index a26328ec43..3893c81913 100644 --- a/tools/block-generator/README.md +++ b/tools/block-generator/README.md @@ -9,6 +9,7 @@ Several scenarios were designed to mimic different block traffic patterns. Scena ### Organic Traffic Simulate the current mainnet traffic pattern. Approximately: + * 15% payment transactions * 10% application transactions * 75% asset transactions @@ -33,7 +34,7 @@ Block generator uses a YAML config file to describe the composition of each rand The block generator supports **payment**, **asset**, and **application** transactions. The settings are hopefully, more or less, obvious. Distributions are specified as fractions of 1.0, and the sum of all options must add up to ~1.0. -Here is an example which uses all of the current options. Notice that the synthetic blocks are not required to follow algod limits, in this case the block size is specified as 99,999: +Here is an example which uses all of the current options. Notice that the synthetic blocks are not required to follow algod limits, and that in this case the block size is specified as 99,999: ```yml name: "Mixed (99,999)" @@ -104,6 +105,7 @@ Flags: -c, --config string Specify the block configuration yaml file. -h, --help help for daemon -p, --port uint Port to start the server at. (default 4010) + -v, --verbose If set the daemon will print debugging information from the generator and ledger. ``` ### runner @@ -143,7 +145,7 @@ final_overall_transactions_per_second:8493.40 final_uptime_seconds:3600.06 ``` -Here is the help output for **runner**: +We recommend printing out the help information for the **runner**: ```bash ~$ ./block-generator runner -h @@ -152,36 +154,19 @@ Run an automated test suite using the block-generator daemon and a provided cond Usage: block-generator runner [flags] -Flags: - -i, --conduit-binary string Path to conduit binary. - -l, --conduit-log-level string LogLevel to use when starting Conduit. [panic, fatal, error, warn, info, debug, trace] (default "error") - --cpuprofile string Path where Conduit writes its CPU profile. - -f, --genesis-file string file path to the genesis associated with the db snapshot - -h, --help help for runner - -k, --keep-data-dir If set the validator will not delete the data directory after tests complete. - -p, --metrics-port uint Port to start the metrics server at. (default 9999) - -c, --postgres-connection-string string Postgres connection string. - -r, --report-directory string Location to place test reports. - --reset-db If set database will be deleted before running tests. - --reset-report-dir If set any existing report directory will be deleted before running tests. - -s, --scenario string Directory containing scenarios, or specific scenario file. - -d, --test-duration duration Duration to use for each scenario. (default 5m0s) - --validate If set the validator will run after test-duration has elapsed to verify data is correct. An extra line in each report indicates validator success or failure. - -v, --verbose If set the runner will print debugging information from the generator and ledger. - ``` - -## Example Run using Conduit and Postgres +... etc ... +``` + +## Example Runs using Conduit A typical **runner** scenario involves: -* a [scenario configuration](#scenario-configuration) file, e.g. [test_config.yml](./test_config.yml) -* access to a `conduit` binary to query the block generator's mock Algod endpoint and ingest the synthetic blocks +* a [scenario configuration](#scenario-configuration) file, e.g. [config.asset.xfer.yml](./scenarios/config.asset.xfer.yml) or for the example below [test_scenario.yml](./generator/test_scenario.yml) +* access to a `conduit` binary to query the block generator's mock Algod endpoint and ingest the synthetic blocks (below it's assumed to be set in the `CONDUIT_BINARY` environment variable) * a datastore -such as a postgres database- to collect `conduit`'s output * a `conduit` config file to define its import/export behavior -The `block-generator runner` subcommand has a number of options to configure behavion. - -### Sample Run +### Sample Run with Postgres First you'll need to get a `conduit` binary. For example you can follow the [developer portal's instructions](https://developer.algorand.org/docs/get-details/conduit/GettingStarted/#installation) or run `go build .` inside of the directory `cmd/conduit` after downloading the `conduit` repo. @@ -204,5 +189,133 @@ block-generator runner \ ### Scenario Report -If all goes well, the run will generate a directory named reports. +If all goes well, the run will generate a directory named `reports` +in the same directory in which the command was run. In that directory you can see the statistics of the run in the file ending with `.report`. + +The `block-generator runner` subcommand has a number of options to configure behavior. + +## Sample Run with the File Exporter + +It's possible to save the generated blocks to the file system. +This enables running benchmarks and stress tests at a later time and without +needing a live block generator. The setup is very similar to the previous Postgres example. The main change compared to the previous is to _**specify a different conduit configuration**_ template. + +The `block-generator runner` command in this case would look like: + +```sh +block-generator runner \ + --conduit-binary "$CONDUIT_BINARY" \ + --report-directory reports \ + --test-duration 30s \ + --conduit-log-level trace \ + --template file-exporter \ + --keep-data-dir \ + --scenario generator/test_scenario.yml +``` + +### Generated Blocks + +If all goes well, the run will generate a directory named `reports` +in the same directory in which the command was run. +In addition to the statistical report and run logs, +there will be a directory ending with `_data` - this is conduit's +data directory (which is saved thanks to the `--keep-data-dir` flag). +In that directory under `exporter_file_writer/` +the generated blocks and a genesis file will be saved. + +## Scenario Distribution - Configuration vs. Reality + +This section follows up on the [Scenario Configuration](#scenario-configuration) section to detail how each kind of transaction is actually chosen. +Note that -especially for early rounds- there is no guarantee that the +percentages of transaction types will resemble the configured distribution. + +For example consider the [Organic 25,000](scenarios/benchmarks/organic.25000.yml) scenario: + +```yml +name: "Organic (25000)" +genesis_accounts: 10000 +genesis_account_balance: 1000000000000 +tx_per_block: 25000 + +# transaction distribution +tx_pay_fraction: 0.05 +tx_asset_fraction: 0.75 +tx_app_fraction: 0.20 + +# payment config +pay_acct_create_fraction: 0.10 +pay_xfer_fraction: 0.90 + +# asset config +asset_create_fraction: 0.001 +asset_optin_fraction: 0.1 +asset_close_fraction: 0.05 +asset_xfer_fraction: 0.849 +asset_delete_fraction: 0 + +# app kind config +app_boxes_fraction: 1.0 +app_swap_fraction: 0.0 + +# app boxes config +app_boxes_create_fraction: 0.01 +app_boxes_optin_fraction: 0.1 +app_boxes_call_fraction: 0.89 +``` + +We are _actually_ asking the generator for the following distribution: + +* `pay_acct_create_fraction = 0.005 (= 0.05 * 0.10)` +* `pay_xfer_fraction = 0.045 (= 0.05 * 0.90)` +* `asset_create_fraction = 0.00075 (= 0.75 * 0.001)` +* `asset_optin_fraction = 0.075 (= 0.75 * 0.1)` +* `asset_close_fraction = 0.0375 (= 0.75 * 0.05)` +* `asset_xfer_fraction = 0.63675 (= 0.75 * 0.849)` +* `asset_delete_fraction = 0` +* `app_boxes_create_fraction = 0.002 (= 0.20 * 1.0 * 0.01)` +* `app_boxes_optin_fraction = 0.02 (= 0.20 * 1.0 * 0.1)` +* `app_boxes_call_fraction = 0.178 (= 0.20 * 1.0 * 0.89)` + +The block generator randomly chooses + +1. the transaction type (pay, asset, or app) according to the `transaction distribution` +2. based on the type: + + a. for payments and assets, the specific type based on the `payment config` and `asset config` distributions + + b. for apps, the app kind (boxes or swaps) based on the `app kind config` distribution + +3. For _apps only_: the specific app call based on the `app boxes config` (and perhaps in the future `app swap config`) + +As each of the steps above is itself random, we only expect _approximate matching_ to the configured distribution. + +Furthermore, for certain asset and app transactions there may be a substitution that occurs based on the type. In particular: + +* for **assets**: + * when a requested asset txn is **create**, it is never substituted + * when there are no assets, an **asset create** is always substituted + * when a requested asset txn is **delete** but the creator doesn't hold all asset funds, an **asset close** is substitued (which itself may be substituted using the **close** rule below) + * when a requested asset txn is **opt in** but all accounts are already opted in, an **asset close** is substituted (which itself may be substituted using the **close** rule below) + * when a requested asset txn is **transfer** but there is only one account holding it, an **asset opt in** is substituted (which itself may be substituted using the **asset opt in** rule above) + * when a requested asset txn is **close** but there is only one account holding it, an **asset opt in** is substituted (which itself may be substituted using the **asset opt in** rule above) +* for **apps**: + * when a requested app txn is **create**, it is never substituted + * when a requested app txn is **opt in**: + * if the sender is already opted in, an **app call** is substituted + * otherwise, if the sender's opt-in is pending for the round, an **app create** is substituted + * when a requested app txn is **call** but it's not opted into, an **app opt in** is attempted to be substituted (but this may itself be substituted for given the **app opt in** rule above) + +Over time, we expect the state of the generator to stabilize so that very few substitutions occur. However, especially for the first few rounds, there may be drastic differences between the config distribution and observed percentages. + +In particular: + +* for Round 1, all app transactions are replaced by **app create** +* for Round 2, all **app call** transactions are replaced by **app opt in** + +Therefore, for scenarios involving a variety of app transactions, only for Round 3 and higher do we expect to see distributions comparable to those configured. + +> NOTE: Even in the steady state, we still expect fundamental deviations +> from the configured distributions in the cases of apps. This is because +> an app call may have associated group and inner transactions. For example, +> if an app call requires 1 sibling asset call in its group and has 2 inner payments, this single app call will generate 1 additional asset txn and 2 payment txns. diff --git a/tools/block-generator/generator/daemon.go b/tools/block-generator/generator/daemon.go index fb4f52bab3..0a1371a1bc 100644 --- a/tools/block-generator/generator/daemon.go +++ b/tools/block-generator/generator/daemon.go @@ -48,7 +48,7 @@ func init() { DaemonCmd.Flags().StringVarP(&configFile, "config", "c", "", "Specify the block configuration yaml file.") DaemonCmd.Flags().Uint64VarP(&port, "port", "p", 4010, "Port to start the server at.") - DaemonCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "If set the runner will print debugging information from the generator and ledger.") + DaemonCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "If set the daemon will print debugging information from the generator and ledger.") DaemonCmd.MarkFlagRequired("config") } diff --git a/tools/block-generator/generator/server.go b/tools/block-generator/generator/server.go index edfe470f3d..5b170c504e 100644 --- a/tools/block-generator/generator/server.go +++ b/tools/block-generator/generator/server.go @@ -75,28 +75,30 @@ func help(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Use /v2/blocks/:blocknum: to get a block.") } -func maybeWriteError(w http.ResponseWriter, err error) { +func maybeWriteError(handler string, w http.ResponseWriter, err error) { if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + msg := fmt.Sprintf("%s handler: error encountered while writing response for: %v\n", handler, err) + fmt.Println(msg) + http.Error(w, msg, http.StatusInternalServerError) return } } func getReportHandler(gen Generator) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - maybeWriteError(w, gen.WriteReport(w)) + maybeWriteError("report", w, gen.WriteReport(w)) } } func getStatusWaitHandler(gen Generator) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - maybeWriteError(w, gen.WriteStatus(w)) + maybeWriteError("status wait", w, gen.WriteStatus(w)) } } func getGenesisHandler(gen Generator) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - maybeWriteError(w, gen.WriteGenesis(w)) + maybeWriteError("genesis", w, gen.WriteGenesis(w)) } } @@ -113,7 +115,7 @@ func getBlockHandler(gen Generator) func(w http.ResponseWriter, r *http.Request) http.Error(w, err.Error(), http.StatusBadRequest) return } - maybeWriteError(w, gen.WriteBlock(w, round)) + maybeWriteError("block", w, gen.WriteBlock(w, round)) } } @@ -125,7 +127,7 @@ func getAccountHandler(gen Generator) func(w http.ResponseWriter, r *http.Reques http.Error(w, err.Error(), http.StatusBadRequest) return } - maybeWriteError(w, gen.WriteAccount(w, account)) + maybeWriteError("account", w, gen.WriteAccount(w, account)) } } @@ -141,7 +143,7 @@ func getDeltasHandler(gen Generator) func(w http.ResponseWriter, r *http.Request http.Error(w, err.Error(), http.StatusBadRequest) return } - maybeWriteError(w, gen.WriteDeltas(w, round)) + maybeWriteError("deltas", w, gen.WriteDeltas(w, round)) } } diff --git a/tools/block-generator/go.mod b/tools/block-generator/go.mod index b0bae0f24e..79a485c24f 100644 --- a/tools/block-generator/go.mod +++ b/tools/block-generator/go.mod @@ -1,7 +1,6 @@ module github.com/algorand/go-algorand/tools/block-generator replace github.com/algorand/go-algorand => ../.. -replace github.com/algorand/msgp => ../../msgp go 1.20 @@ -9,7 +8,7 @@ require ( github.com/algorand/avm-abi v0.2.0 github.com/algorand/go-algorand v0.0.0 github.com/algorand/go-codec/codec v1.1.10 - github.com/algorand/go-deadlock v0.2.2 + github.com/algorand/go-deadlock v0.2.3 github.com/lib/pq v1.10.9 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 @@ -20,12 +19,14 @@ require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/algorand/falcon v0.1.0 // indirect github.com/algorand/go-sumhash v0.1.0 // indirect - github.com/algorand/msgp v1.1.55 // indirect + github.com/algorand/msgp v1.1.60 // indirect github.com/algorand/oapi-codegen v1.12.0-algorand.0 // indirect github.com/algorand/sortition v1.0.0 // indirect github.com/algorand/websocket v1.4.6 // indirect - github.com/aws/aws-sdk-go v1.33.0 // indirect + github.com/aws/aws-sdk-go v1.34.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/errors v1.8.1 // indirect github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect @@ -33,27 +34,62 @@ require ( github.com/cockroachdb/redact v1.0.8 // indirect github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6 // indirect - github.com/consensys/gnark-crypto v0.7.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.0 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/dchest/siphash v1.2.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.2 // indirect + github.com/flynn/noise v1.0.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect + github.com/huin/goupnp v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/kr/pretty v0.2.1 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.1.0 // indirect + github.com/libp2p/go-libp2p v0.29.1 // indirect + github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect + github.com/libp2p/go-libp2p-pubsub v0.9.3 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-nat v0.2.0 // indirect + github.com/libp2p/go-netroute v0.2.1 // indirect + github.com/libp2p/go-reuseport v0.3.0 // indirect + github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.55 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -61,10 +97,16 @@ require ( github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr v0.10.1 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.4.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/olivere/elastic v6.2.14+incompatible // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect + github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -72,16 +114,31 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-19 v0.3.3 // indirect + github.com/quic-go/qtls-go1-20 v0.2.3 // indirect + github.com/quic-go/quic-go v0.36.3 // indirect + github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.11.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/fx v1.20.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sys v0.10.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.11.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 // indirect lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/tools/block-generator/go.sum b/tools/block-generator/go.sum index 5d94a58140..d4620db58c 100644 --- a/tools/block-generator/go.sum +++ b/tools/block-generator/go.sum @@ -1,5 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -30,7 +32,12 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -53,26 +60,35 @@ github.com/algorand/falcon v0.1.0 h1:xl832kfZ7hHG6B4p90DQynjfKFGbIUgUOnsRiMZXfAo github.com/algorand/falcon v0.1.0/go.mod h1:OkQyHlGvS0kLNcIWbC21/uQcnbfwSOQm+wiqWwBG9pQ= 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/go-deadlock v0.2.2 h1:L7AKATSUCzoeVuOgpTipfCEjdUu5ECmlje8R7lP9DOY= -github.com/algorand/go-deadlock v0.2.2/go.mod h1:Hat1OXKqKNUcN/iv74FjGhF4hsOE2l7gOgQ9ZVIq6Fk= +github.com/algorand/go-deadlock v0.2.3 h1:ek9rjUyUF1HhUm0I2DyaCN8+3S850ONJNl5jQr9kZOA= +github.com/algorand/go-deadlock v0.2.3/go.mod h1:Gli2d0Cb7kgXzSpJLC4Vn0DCLgjNVi6fNldY/mOtO/U= github.com/algorand/go-sumhash v0.1.0 h1:b/QRhyLuF//vOcicBIxBXYW8bERNoeLxieht/dUYpVg= github.com/algorand/go-sumhash v0.1.0/go.mod h1:OOe7jdDWUhLkuP1XytkK5gnLu9entAviN5DfDZh6XAc= -github.com/algorand/msgp v1.1.55 h1:kWc9Xc08xtxCTWUiq1cRW5XGF+DFcfSGihYf0IZ/ivs= -github.com/algorand/msgp v1.1.55/go.mod h1:RqZQBzAFDWpwh5TlabzZkWy+6kwL9cvXfLbU0gD99EA= +github.com/algorand/msgp v1.1.60 h1:+IVUC34+tSj1P2M1mkYtl4GLyfzdzXfBLSw6TDT19M8= +github.com/algorand/msgp v1.1.60/go.mod h1:RqZQBzAFDWpwh5TlabzZkWy+6kwL9cvXfLbU0gD99EA= github.com/algorand/oapi-codegen v1.12.0-algorand.0 h1:W9PvED+wAJc+9EeXPONnA+0zE9UhynEqoDs4OgAxKhk= github.com/algorand/oapi-codegen v1.12.0-algorand.0/go.mod h1:tIWJ9K/qrLDVDt5A1p82UmxZIEGxv2X+uoujdhEAL48= github.com/algorand/sortition v1.0.0 h1:PJiZtdSTBm4nArQrZXBnhlljHXhuyAXRJBqVWowQu3E= github.com/algorand/sortition v1.0.0/go.mod h1:23CZwAbTWPv0bBsq+Php/2J6Y/iXDyzlfcZyepeY5Fo= github.com/algorand/websocket v1.4.6 h1:I0kV4EYwatuUrKtNiwzYYgojgwh6pksDmlqntKG2Woc= github.com/algorand/websocket v1.4.6/go.mod h1:HJmdGzFtnlUQ4nTzZP6WrT29oGYf1t6Ybi64vROcT+M= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aws/aws-sdk-go v1.33.0 h1:Bq5Y6VTLbfnJp1IV8EL/qUU5qO1DYHda/zis/sqevkY= -github.com/aws/aws-sdk-go v1.33.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.0 h1:brux2dRrlwCF5JhTL7MUT3WUwo9zfDHZZp3+g3Mvlmo= +github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= +github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -82,6 +98,7 @@ github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz7 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= @@ -100,12 +117,23 @@ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6 h1:DJK8W/iB+s/qkTtmXSrHA49lp5O3OsR7E6z4byOLy34= github.com/cockroachdb/tokenbucket v0.0.0-20230613231145-182959a1fad6/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/gnark-crypto v0.7.0 h1:rwdy8+ssmLYRqKp+ryRRgQJl/rCq2uv+n83cOydm5UE= -github.com/consensys/gnark-crypto v0.7.0/go.mod h1:KPSuJzyxkJA8xZ/+CV47tyqkr9MmpZA3PXivK4VPrVg= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.0 h1:1OnSpOykNkUIBIBJKdhwy2p0JlW5o+Az02ICzZmvvdg= +github.com/consensys/gnark-crypto v0.12.0/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -115,11 +143,20 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -128,12 +165,19 @@ github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHj github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -148,12 +192,19 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -165,6 +216,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -172,6 +224,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -202,11 +256,15 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -216,21 +274,33 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= +github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= @@ -239,10 +309,19 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -277,11 +356,15 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -290,13 +373,43 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= +github.com/libp2p/go-libp2p v0.29.1 h1:yNeg6XgP8gbdc4YSrwiIt5T1TGOrVjH8dzl8h0GIOfQ= +github.com/libp2p/go-libp2p v0.29.1/go.mod h1:20El+LLy3/YhdUYIvGbLnvVJN32nMdqY6KXBENRAfLY= +github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= +github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= +github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= +github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= +github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= +github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= +github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -305,10 +418,17 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= @@ -331,16 +451,23 @@ github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aG github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= @@ -349,25 +476,37 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/olivere/elastic v6.2.14+incompatible h1:k+KadwNP/dkXE0/eu+T6otk1+5fe0tEpPyQJ4XVm5i8= github.com/olivere/elastic v6.2.14+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -381,12 +520,14 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -394,20 +535,61 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= +github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= +github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.36.3 h1:f+yOqeGhMoRX7/M3wmEw/djhzKWr15FtQysox85/834= +github.com/quic-go/quic-go v0.36.3/go.mod h1:qxQumdeKw5GmWs1OsTZZnOxzSI+RJWuhf1O8FN35L2o= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -427,15 +609,21 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -448,15 +636,36 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= +go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -464,9 +673,10 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -481,6 +691,7 @@ golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xpp golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -499,17 +710,21 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -536,20 +751,25 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -562,14 +782,18 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -589,6 +813,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -608,15 +833,19 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -626,11 +855,15 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -676,6 +909,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= @@ -683,6 +918,9 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -700,6 +938,8 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -707,6 +947,10 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -736,6 +980,9 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -767,10 +1014,12 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 h1:q/fZgS8MMadqFFGa8WL4Oyz+TmjiZfi8UrzWhTl8d5w= gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009/go.mod h1:O0bY1e/dSoxMYZYTHP0SWKxG5EWLEvKR9/cOjWPPMKU= @@ -779,10 +1028,16 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -796,3 +1051,7 @@ pgregory.net/rapid v0.6.2 h1:ErW5sL+UKtfBfUTsWHDCoeB+eZKLKMxrSd1VJY6W4bw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/tools/block-generator/runner/run.go b/tools/block-generator/runner/run.go index 6cd8d2398a..f2e1c1fc1f 100644 --- a/tools/block-generator/runner/run.go +++ b/tools/block-generator/runner/run.go @@ -43,8 +43,11 @@ import ( "github.com/algorand/go-algorand/tools/block-generator/util" ) -//go:embed template/conduit.yml.tmpl -var conduitConfigTmpl string +//go:embed template/conduit_pg_exporter.tmpl +var conduitPostgresConfigTmpl string + +//go:embed template/conduit_file_exporter.tmpl +var conduitFileExporterConfigTmpl string const pad = " " @@ -54,6 +57,7 @@ type Args struct { Path string ConduitBinary string MetricsPort uint64 + Template string PostgresConnectionString string CPUProfilePath string RunDuration time.Duration @@ -153,20 +157,29 @@ func (r *Args) run(reportDirectory string) error { // get next db round var nextRound uint64 var err error - if r.ResetDB { - fmt.Printf("%sPostgreSQL resetting.\n", pad) - if err = util.EmptyDB(r.PostgresConnectionString); err != nil { - return fmt.Errorf("emptyDB err: %w", err) - } - nextRound = 0 - } else { - nextRound, err = util.GetNextRound(r.PostgresConnectionString) - if err != nil && err == util.ErrorNotInitialized { + switch r.Template { + case "file-exporter": + fmt.Printf("%sUsing File Exporter to persist blocks.\n", pad) + case "postgres-exporter": + fmt.Printf("%sUsing PostgreSQL Exporter to persist blocks.\n", pad) + if r.ResetDB { + fmt.Printf("%sPostgreSQL resetting.\n", pad) + if err = util.EmptyDB(r.PostgresConnectionString); err != nil { + return fmt.Errorf("emptyDB err: %w", err) + } nextRound = 0 - } else if err != nil { - return fmt.Errorf("getNextRound err: %w", err) + } else { + nextRound, err = util.GetNextRound(r.PostgresConnectionString) + if err != nil && err == util.ErrorNotInitialized { + nextRound = 0 + } else if err != nil { + return fmt.Errorf("getNextRound err: %w", err) + } + fmt.Printf("%sPostgreSQL next round: %d\n", pad, nextRound) } - fmt.Printf("%sPostgreSQL next round: %d\n", pad, nextRound) + default: + // TODO: the default case should attempt to read the supplied template name as a file under ./template/ + return fmt.Errorf("unknown template type: %s", r.Template) } if r.StartDelay > 0 { @@ -188,6 +201,16 @@ func (r *Args) run(reportDirectory string) error { }() // create conduit config from template + var conduitConfigTmpl string + switch r.Template { + case "file-exporter": + conduitConfigTmpl = conduitFileExporterConfigTmpl + case "postgres-exporter": + conduitConfigTmpl = conduitPostgresConfigTmpl + default: + return fmt.Errorf("unknown template type: %s", r.Template) + } + t, err := template.New("conduit").Parse(conduitConfigTmpl) if err != nil { return fmt.Errorf("unable to parse conduit config template: %w", err) @@ -296,14 +319,14 @@ func recordDataToWriter(start time.Time, entry Entry, prefix string, out io.Writ tps := totalTxn / importTimeS key := "overall_transactions_per_second" msg := fmt.Sprintf("%s_%s:%.2f\n", prefix, key, tps) - if _, err := fmt.Fprintf(out, msg); err != nil { + if _, err := fmt.Fprint(out, msg); err != nil { return fmt.Errorf("unable to write metric '%s': %w", key, err) } // Uptime key = "uptime_seconds" msg = fmt.Sprintf("%s_%s:%.2f\n", prefix, key, time.Since(start).Seconds()) - if _, err := fmt.Fprintf(out, msg); err != nil { + if _, err := fmt.Fprint(out, msg); err != nil { return fmt.Errorf("unable to write metric '%s': %w", key, err) } @@ -323,7 +346,7 @@ func recordMetricToWriter(entry Entry, outputKey, metricSuffix string, t metricT msg = fmt.Sprintf("%s:%.2f\n", outputKey, value) } - if _, err := fmt.Fprintf(out, msg); err != nil { + if _, err := fmt.Fprint(out, msg); err != nil { return fmt.Errorf("unable to write metric '%s': %w", outputKey, err) } @@ -383,7 +406,7 @@ func getMetric(entry Entry, suffix string, rateMetric bool) (float64, error) { func writeReport(w io.Writer, scenario string, start time.Time, runDuration time.Duration, generatorReport generator.Report, collector *MetricsCollector) error { write := func(pattern string, parts ...any) error { str := fmt.Sprintf(pattern, parts...) - if _, err := fmt.Fprintf(w, str); err != nil { + if _, err := fmt.Fprint(w, str); err != nil { return fmt.Errorf("unable to write '%s': %w", str, err) } return nil @@ -426,12 +449,12 @@ func writeReport(w io.Writer, scenario string, start time.Time, runDuration time txCount := effects[metric] allTxns += txCount str := fmt.Sprintf("transaction_%s_total:%d\n", metric, txCount) - if _, err := fmt.Fprintf(w, str); err != nil { + if _, err := fmt.Fprint(w, str); err != nil { return fmt.Errorf("unable to write '%s' metric: %w", str, err) } } str := fmt.Sprintf("transaction_%s_total:%d\n", "ALL", allTxns) - if _, err := fmt.Fprintf(w, str); err != nil { + if _, err := fmt.Fprint(w, str); err != nil { return fmt.Errorf("unable to write '%s' metric: %w", str, err) } diff --git a/tools/block-generator/runner/runner.go b/tools/block-generator/runner/runner.go index ebb19a738e..4c11fde59c 100644 --- a/tools/block-generator/runner/runner.go +++ b/tools/block-generator/runner/runner.go @@ -35,17 +35,21 @@ func init() { Use: "runner", Short: "Run test suite and collect results.", Long: "Run an automated test suite using the block-generator daemon and a provided conduit binary. Results are captured to a specified output directory.", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error{ fmt.Printf("starting block-generator runner with args: %+v\n", runnerArgs) - if err := Run(runnerArgs); err != nil { - fmt.Println(err) + + if runnerArgs.Template == "postgres-exporter" && runnerArgs.PostgresConnectionString == "" { + return fmt.Errorf("exporting to postgres requires that postgres-connection-string to be set") } + + return Run(runnerArgs) }, } RunnerCmd.Flags().StringVarP(&runnerArgs.Path, "scenario", "s", "", "Directory containing scenarios, or specific scenario file.") RunnerCmd.Flags().StringVarP(&runnerArgs.ConduitBinary, "conduit-binary", "i", "", "Path to conduit binary.") RunnerCmd.Flags().Uint64VarP(&runnerArgs.MetricsPort, "metrics-port", "p", 9999, "Port to start the metrics server at.") + RunnerCmd.Flags().StringVarP(&runnerArgs.Template, "template", "", "postgres-exporter", "Specify the conduit template to use. Choices are: file-exporter or postgres-exporter.") RunnerCmd.Flags().StringVarP(&runnerArgs.PostgresConnectionString, "postgres-connection-string", "c", "", "Postgres connection string.") RunnerCmd.Flags().DurationVarP(&runnerArgs.RunDuration, "test-duration", "d", 5*time.Minute, "Duration to use for each scenario.") RunnerCmd.Flags().StringVarP(&runnerArgs.BaseReportDirectory, "report-directory", "r", "", "Location to place test reports. If --times is used, this is the prefix for multiple report directories.") @@ -62,6 +66,5 @@ func init() { RunnerCmd.MarkFlagRequired("scenario") RunnerCmd.MarkFlagRequired("conduit-binary") - RunnerCmd.MarkFlagRequired("postgres-connection-string") RunnerCmd.MarkFlagRequired("report-directory") } diff --git a/tools/block-generator/runner/template/conduit_file_exporter.tmpl b/tools/block-generator/runner/template/conduit_file_exporter.tmpl new file mode 100644 index 0000000000..f675022096 --- /dev/null +++ b/tools/block-generator/runner/template/conduit_file_exporter.tmpl @@ -0,0 +1,65 @@ +# Log verbosity: PANIC, FATAL, ERROR, WARN, INFO, DEBUG, TRACE +log-level: {{.LogLevel}} + +# If no log file is provided logs are written to stdout. +log-file: {{.LogFile}} + +# Number of retries to perform after a pipeline plugin error. +retry-count: 120 + +# Time duration to wait between retry attempts. +retry-delay: "1s" + +# Optional filepath to use for pidfile. +#pid-filepath: /path/to/pidfile + +# Whether or not to print the conduit banner on startup. +hide-banner: false + +# When enabled prometheus metrics are available on '/metrics' +metrics: + mode: ON + addr: "{{.MetricsPort}}" + prefix: "conduit" + +# The importer is typically an algod follower node. +importer: + name: algod + config: + # The mode of operation, either "archival" or "follower". + # * archival mode allows you to start processing on any round but does not + # contain the ledger state delta objects required for the postgres writer. + # * follower mode allows you to use a lightweight non-archival node as the + # data source. In addition, it will provide ledger state delta objects to + # the processors and exporter. + mode: "follower" + + # Algod API address. + netaddr: "{{.AlgodNet}}" + + # Algod API token. + token: "" + + +# Zero or more processors may be defined to manipulate what data +# reaches the exporter. +processors: + +# An exporter is defined to do something with the data. +exporter: + name: "file_writer" + config: + # BlocksDir is the path to a directory where block data should be stored. + # The directory is created if it doesn't exist. If no directory is provided + # blocks are written to the Conduit data directory. + #block-dir: "/path/to/block/files" + + # FilenamePattern is the format used to write block files. It uses go + # string formatting and should accept one number for the round. + # If the file has a '.gz' extension, blocks will be gzipped. + # Default: "%[1]d_block.msgp.gz" + filename-pattern: "%[1]d_block.msgp.gz" + + # DropCertificate is used to remove the vote certificate from the block data before writing files. + drop-certificate: false + diff --git a/tools/block-generator/runner/template/conduit.yml.tmpl b/tools/block-generator/runner/template/conduit_pg_exporter.tmpl similarity index 100% rename from tools/block-generator/runner/template/conduit.yml.tmpl rename to tools/block-generator/runner/template/conduit_pg_exporter.tmpl diff --git a/tools/block-generator/scripts/run_runner.py b/tools/block-generator/scripts/run_runner.py deleted file mode 100644 index 5f0753930b..0000000000 --- a/tools/block-generator/scripts/run_runner.py +++ /dev/null @@ -1,201 +0,0 @@ -import argparse -import os -from pathlib import Path -import shlex -import subprocess -import sys -import time - - -POSTGRES_CONTAINER = "generator-test-container" -POSTGRES_PORT = 15432 -POSTGRES_DATABASE = "generator_db" - -REPORT_DIRECTORY = "../../tmp/OUTPUT_RUN_RUNNER_TEST" - -CWD = Path.cwd() - -NL = "\n" -BS = "\\" -DBS = BS * 2 -Q = '"' -SQ = ' "' - - -def run_cmd(cmd): - print(f"Running command: {cmd}") - process = subprocess.Popen( - shlex.split(cmd.replace("\\\n", " ")), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - stdout, stderr = process.communicate() - if (rcode := process.returncode) != 0: - print(f"Error executing command: {cmd}") - print(stderr.decode()) - sys.exit(rcode) - return stdout.decode() - - -def up(args): - run_cmd(f"docker rm -f {args.pg_container}") - run_cmd( - f"docker run -d --name {args.pg_container} -e POSTGRES_USER=algorand -e POSTGRES_PASSWORD=algorand -p {args.pg_port}:5432 postgres" - ) - time.sleep(5) - - run_cmd( - f'docker exec -it {args.pg_container} psql -Ualgorand -c "create database {args.pg_database}"' - ) - - -def down(args): - run_cmd(f"docker rm -f {args.pg_container}") - - -def launch_json_args(cmd: str): - def tighten(x): - return x.replace(" \\", "\\") - - def wrap(x): - return tighten(x) if x.startswith('"') else f'"{tighten(x)}"' - - newlines = [] - lines = cmd.splitlines() - for i, line in enumerate(lines): - if i == 0: - continue - if not line.startswith("--"): - aline = wrap(line.replace(" ", "")) - else: - aline = ", ".join(map(wrap, line.split(" ", maxsplit=1))) - - if i < len(lines) - 1: - aline += "," - - newlines.append(aline) - return f"[{(NL.join(newlines)).replace(BS, '')}]" - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("--conduit-binary", help="Path to conduit binary") - parser.add_argument( - "--scenario", - default=(default := CWD.parents[1] / "test_scenario.yml"), - help=f"Scenario configuration file ({default=!s})", - ) - parser.add_argument( - "--reset-db", - action="store_true", - default=False, - help="Reset the DB and start at round 0 (default=False)", - ) - parser.add_argument( - "--purge", - action="store_true", - default=False, - help="Shutdown container that has been kept alive (default=False)", - ) - parser.add_argument( - "--keep-alive", - action="store_true", - default=False, - help="Keep postgres container alive at end of run (default=False)", - ) - parser.add_argument( - "--pg-container", - default=(default := POSTGRES_CONTAINER), - help=f"Name of postgres container ({default=})", - ) - parser.add_argument( - "--pg-port", - default=(default := POSTGRES_PORT), - help=f"Postgres port ({default=})", - ) - parser.add_argument( - "--pg-database", - default=(default := POSTGRES_DATABASE), - help=f"Postgres database ({default=})", - ) - parser.add_argument( - "--report-directory", - default=(default := REPORT_DIRECTORY), - help=f"Report directory ({default=})", - ) - parser.add_argument( - "--build-generator", - action="store_true", - default=False, - help="Build the generator binary (default=False)", - ) - parser.add_argument( - "--skip-runner", - action="store_true", - default=False, - help="Skip running the generator (default=False)", - ) - parser.add_argument( - "--test-duration", - default=(default := "30s"), - help=f"Test duration ({default=})", - ) - - args = parser.parse_args() - print(args) - return args - - -def main(): - args = parse_args() - - try: - if not args.purge: - print(f"Using scenario file: {args.scenario}") - print(f"!!! rm -rf {args.report_directory} !!!") - run_cmd(f"rm -rf {args.report_directory}") - - if args.build_generator: - print("Building generator.") - os.chdir(CWD) - run_cmd("go build") - os.chdir("..") - else: - print("Skipping generator build.") - - print("Starting postgres container.") - up(args) - - SLNL = "\\\n" - generator_cmd = f"""{CWD}/block-generator \\ -runner \\ ---conduit-binary "{args.conduit_binary}" \\ ---report-directory {args.report_directory} \\ ---test-duration {args.test_duration} \\ ---conduit-log-level trace \\ ---postgres-connection-string "host=localhost user=algorand password=algorand dbname={args.pg_database} port={args.pg_port} sslmode=disable" \\ ---scenario {args.scenario} {DBS + NL + '--reset-db' if args.reset_db else ''}""" - if args.skip_runner: - print("Skipping test runner.") - print(f"Run it yourself:\n{generator_cmd}") - print( - f"""`launch.json` args: -{launch_json_args(generator_cmd)}""" - ) - else: - print("Starting test runner") - run_cmd(generator_cmd) - else: - print("Purging postgres container - NO OTHER ACTION TAKEN") - down(args) - finally: - if not args.keep_alive: - print("Stopping postgres container.") - down(args) - else: - print(f"Keeping postgres container alive: {args.pg_container}") - print(f"Also, not removing report directory: {args.report_directory}") - - -if __name__ == "__main__": - main() diff --git a/tools/debug/transplanter/main.go b/tools/debug/transplanter/main.go index 1c7a3a6b41..1ee2a9c84e 100644 --- a/tools/debug/transplanter/main.go +++ b/tools/debug/transplanter/main.go @@ -381,7 +381,6 @@ func main() { l.Close() fmt.Printf("Catching up from %d to %d\n", latest, *roundStart) - followerNode, err = node.MakeFollower(log, rootPath, cfg, []string{}, genesis) if err != nil { fmt.Fprintf(os.Stderr, "Cannot init follower node: %v", err) diff --git a/tools/network/dnssec/config_windows.go b/tools/network/dnssec/config_windows.go index 36c0aaf377..318f8f7a49 100644 --- a/tools/network/dnssec/config_windows.go +++ b/tools/network/dnssec/config_windows.go @@ -21,7 +21,6 @@ package dnssec import ( "fmt" - "runtime/debug" "time" "unsafe" @@ -94,25 +93,31 @@ type fixedInfoWithOverlay struct { // See GetNetworkParams for details: // https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getnetworkparams func SystemConfig() (servers []ResolverAddress, timeout time.Duration, err error) { - // disable GC to prevent fi collection earlier than lookups in fi completed - pct := debug.SetGCPercent(-1) - defer debug.SetGCPercent(pct) + ulSize := uint32(unsafe.Sizeof(fixedInfoWithOverlay{})) + + buf, err := windows.LocalAlloc(windows.LMEM_FIXED|windows.LMEM_ZEROINIT, ulSize) + if err != nil { + err = fmt.Errorf("GetNetworkParams failed to allocate %d bytes of memory for fixedInfoWithOverlay", ulSize) + return + } + + defer windows.LocalFree(windows.Handle(buf)) - var fi fixedInfoWithOverlay - var ulSize uint32 = uint32(unsafe.Sizeof(fi)) ret, _, _ := networkParamsProc.Call( - uintptr(unsafe.Pointer(&fi)), + buf, uintptr(unsafe.Pointer(&ulSize)), ) if ret != 0 { if windows.Errno(ret) == windows.ERROR_BUFFER_OVERFLOW { - err = fmt.Errorf("GetNetworkParams requested %d bytes of memory, max supported is %d. Error code is %x", ulSize, unsafe.Sizeof(fi), ret) + err = fmt.Errorf("GetNetworkParams requested %d bytes of memory, max supported is %d. Error code is %x", ulSize, unsafe.Sizeof(fixedInfoWithOverlay{}), ret) return } err = fmt.Errorf("GetNetworkParams failed with code is %x", ret) return } + fi := (*fixedInfoWithOverlay)(unsafe.Pointer(buf)) + var p *ipAddrString = &fi.DnsServerList for { ip := make([]byte, ip_size) diff --git a/tools/x-repo-types/go.mod b/tools/x-repo-types/go.mod index 7427bc676e..df9ddf0114 100644 --- a/tools/x-repo-types/go.mod +++ b/tools/x-repo-types/go.mod @@ -3,7 +3,6 @@ module github.com/algorand/go-algorand/tools/x-repo-types go 1.20 replace github.com/algorand/go-algorand => ../.. -replace github.com/algorand/msgp => ../../msgp require ( github.com/algorand/go-algorand v0.0.0 diff --git a/tools/x-repo-types/go.sum b/tools/x-repo-types/go.sum index 61d48cf887..7bc3b497d6 100644 --- a/tools/x-repo-types/go.sum +++ b/tools/x-repo-types/go.sum @@ -3,8 +3,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -14,7 +12,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ledger/persistedkvs.go b/util/list.go similarity index 59% rename from ledger/persistedkvs.go rename to util/list.go index b97234f343..be52459f7c 100644 --- a/ledger/persistedkvs.go +++ b/util/list.go @@ -14,47 +14,50 @@ // You should have received a copy of the GNU Affero General Public License // along with go-algorand. If not, see . -package ledger +package util -// persistedKVDataList represents a doubly linked list. -// must initiate with newPersistedKVList. -type persistedKVDataList struct { - root persistedKVDataListNode // sentinel list element, only &root, root.prev, and root.next are used - freeList *persistedKVDataListNode // preallocated nodes location +// List represents a doubly linked list. +// must initiate with NewList. +type List[T any] struct { + root ListNode[T] // sentinel list element, only &root, root.prev, and root.next are used + freeList *ListNode[T] // preallocated nodes location } -type persistedKVDataListNode struct { +// ListNode represent a list node holding next/prev pointers and a value of type T. +type ListNode[T any] struct { // Next and previous pointers in the doubly-linked list of elements. // To simplify the implementation, internally a list l is implemented // as a ring, such that &l.root is both the next element of the last // list element (l.Back()) and the previous element of the first list // element (l.Front()). - next, prev *persistedKVDataListNode + next, prev *ListNode[T] - Value *cachedKVData + Value T } -func newPersistedKVList() *persistedKVDataList { - l := new(persistedKVDataList) +// NewList creates a new list for storing values of type T. +func NewList[T any]() *List[T] { + l := new(List[T]) l.root.next = &l.root l.root.prev = &l.root // used as a helper but does not store value - l.freeList = new(persistedKVDataListNode) + l.freeList = new(ListNode[T]) return l } -func (l *persistedKVDataList) insertNodeToFreeList(otherNode *persistedKVDataListNode) { +func (l *List[T]) insertNodeToFreeList(otherNode *ListNode[T]) { otherNode.next = l.freeList.next otherNode.prev = nil - otherNode.Value = nil + var empty T + otherNode.Value = empty l.freeList.next = otherNode } -func (l *persistedKVDataList) getNewNode() *persistedKVDataListNode { +func (l *List[T]) getNewNode() *ListNode[T] { if l.freeList.next == nil { - return new(persistedKVDataListNode) + return new(ListNode[T]) } newNode := l.freeList.next l.freeList.next = newNode.next @@ -62,20 +65,21 @@ func (l *persistedKVDataList) getNewNode() *persistedKVDataListNode { return newNode } -func (l *persistedKVDataList) allocateFreeNodes(numAllocs int) *persistedKVDataList { +// AllocateFreeNodes adds N nodes to the free list +func (l *List[T]) AllocateFreeNodes(numAllocs int) *List[T] { if l.freeList == nil { return l } for i := 0; i < numAllocs; i++ { - l.insertNodeToFreeList(new(persistedKVDataListNode)) + l.insertNodeToFreeList(new(ListNode[T])) } return l } // Back returns the last element of list l or nil if the list is empty. -func (l *persistedKVDataList) back() *persistedKVDataListNode { - isEmpty := func(list *persistedKVDataList) bool { +func (l *List[T]) Back() *ListNode[T] { + isEmpty := func(list *List[T]) bool { // assumes we are inserting correctly to the list - using pushFront. return list.root.next == &list.root } @@ -85,10 +89,9 @@ func (l *persistedKVDataList) back() *persistedKVDataListNode { return l.root.prev } -// remove removes e from l if e is an element of list l. -// It returns the element value e.Value. +// Remove removes e from l if e is an element of list l. // The element must not be nil. -func (l *persistedKVDataList) remove(e *persistedKVDataListNode) { +func (l *List[T]) Remove(e *ListNode[T]) { e.prev.next = e.next e.next.prev = e.prev e.next = nil // avoid memory leaks @@ -97,15 +100,15 @@ func (l *persistedKVDataList) remove(e *persistedKVDataListNode) { l.insertNodeToFreeList(e) } -// pushFront inserts a new element e with value v at the front of list l and returns e. -func (l *persistedKVDataList) pushFront(v *cachedKVData) *persistedKVDataListNode { +// PushFront inserts a new element e with value v at the front of list l and returns e. +func (l *List[T]) PushFront(v T) *ListNode[T] { newNode := l.getNewNode() newNode.Value = v return l.insertValue(newNode, &l.root) } // insertValue inserts e after at, increments l.len, and returns e. -func (l *persistedKVDataList) insertValue(newNode *persistedKVDataListNode, at *persistedKVDataListNode) *persistedKVDataListNode { +func (l *List[T]) insertValue(newNode *ListNode[T], at *ListNode[T]) *ListNode[T] { n := at.next at.next = newNode newNode.prev = at @@ -115,10 +118,10 @@ func (l *persistedKVDataList) insertValue(newNode *persistedKVDataListNode, at * return newNode } -// moveToFront moves element e to the front of list l. +// MoveToFront moves element e to the front of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. -func (l *persistedKVDataList) moveToFront(e *persistedKVDataListNode) { +func (l *List[T]) MoveToFront(e *ListNode[T]) { if l.root.next == e { return } @@ -126,7 +129,7 @@ func (l *persistedKVDataList) moveToFront(e *persistedKVDataListNode) { } // move moves e to next to at and returns e. -func (l *persistedKVDataList) move(e, at *persistedKVDataListNode) *persistedKVDataListNode { +func (l *List[T]) move(e, at *ListNode[T]) *ListNode[T] { if e == at { return e } diff --git a/util/list_test.go b/util/list_test.go new file mode 100644 index 0000000000..4b87bef745 --- /dev/null +++ b/util/list_test.go @@ -0,0 +1,276 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/algorand/go-algorand/test/partitiontest" +) + +func checkLen[T any](list *List[T]) int { + if list.root.next == &list.root { + return 0 + } + + return countListSize(&list.root) +} + +func countListSize[T any](head *ListNode[T]) (counter int) { + for i := head.next; i != head && i != nil; i = i.next { + counter++ + } + return counter +} + +func checkListLen[T any](t *testing.T, l *List[T], len int) bool { + if n := checkLen(l); n != len { + t.Errorf("l.Len() = %d, want %d", n, len) + return true + } + return false +} + +func checkListPointers[T any](t *testing.T, l *List[T], es []*ListNode[T]) { + root := &l.root + + if failed := checkListLen(t, l, len(es)); failed { + return + } + + if failed := zeroListInspection(t, l, len(es), root); failed { + return + } + + pointerInspection(t, es, root) +} + +func zeroListInspection[T any](t *testing.T, l *List[T], len int, root *ListNode[T]) bool { + // zero length lists must be the zero value or properly initialized (sentinel circle) + if len == 0 { + if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root { + t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root) + } + return true + } + return false +} + +func pointerInspection[T any](t *testing.T, es []*ListNode[T], root *ListNode[T]) { + // check internal and external prev/next connections + for i, e := range es { + prev := root + if i > 0 { + prev = es[i-1] + } + if p := e.prev; p != prev { + t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev) + } + + next := root + if i < len(es)-1 { + next = es[i+1] + } + if n := e.next; n != next { + t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next) + } + } +} + +type testVal struct { + val int +} + +func TestList_RemoveFromList(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[*testVal]() + e1 := l.PushFront(&testVal{1}) + e2 := l.PushFront(&testVal{2}) + e3 := l.PushFront(&testVal{3}) + checkListPointers(t, l, []*ListNode[*testVal]{e3, e2, e1}) + + l.Remove(e2) + checkListPointers(t, l, []*ListNode[*testVal]{e3, e1}) + l.Remove(e3) + checkListPointers(t, l, []*ListNode[*testVal]{e1}) +} + +func TestList_AddingNewNodeWithAllocatedFreeListPtr(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[*testVal]().AllocateFreeNodes(10) + checkListPointers(t, l, []*ListNode[*testVal]{}) + if countListSize(l.freeList) != 10 { + t.Errorf("free list did not allocate nodes") + return + } + // test elements + e1 := l.PushFront(&testVal{1}) + checkListPointers(t, l, []*ListNode[*testVal]{e1}) + + if countListSize(l.freeList) != 9 { + t.Errorf("free list did not provide a node on new list entry") + return + } +} + +func TestList_AddingNewNodeWithAllocatedFreeListValue(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[testVal]().AllocateFreeNodes(10) + checkListPointers(t, l, []*ListNode[testVal]{}) + if countListSize(l.freeList) != 10 { + t.Errorf("free list did not allocate nodes") + return + } + // test elements + e1 := l.PushFront(testVal{1}) + checkListPointers(t, l, []*ListNode[testVal]{e1}) + + if countListSize(l.freeList) != 9 { + t.Errorf("free list did not provide a node on new list entry") + return + } +} + +func TestList_MultiElementListPositioning(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[*testVal]() + checkListPointers(t, l, []*ListNode[*testVal]{}) + // test elements + e2 := l.PushFront(&testVal{2}) + e1 := l.PushFront(&testVal{1}) + e3 := l.PushFront(&testVal{3}) + e4 := l.PushFront(&testVal{4}) + e5 := l.PushFront(&testVal{5}) + checkListPointers(t, l, []*ListNode[*testVal]{e5, e4, e3, e1, e2}) + + l.move(e4, e1) + checkListPointers(t, l, []*ListNode[*testVal]{e5, e3, e1, e4, e2}) + + l.Remove(e5) + checkListPointers(t, l, []*ListNode[*testVal]{e3, e1, e4, e2}) + + l.move(e1, e4) // swap in middle + checkListPointers(t, l, []*ListNode[*testVal]{e3, e4, e1, e2}) + + l.MoveToFront(e4) + checkListPointers(t, l, []*ListNode[*testVal]{e4, e3, e1, e2}) + + l.Remove(e2) + checkListPointers(t, l, []*ListNode[*testVal]{e4, e3, e1}) + + l.MoveToFront(e3) // move from middle + checkListPointers(t, l, []*ListNode[*testVal]{e3, e4, e1}) + + l.MoveToFront(e1) // move from end + checkListPointers(t, l, []*ListNode[*testVal]{e1, e3, e4}) + + l.MoveToFront(e1) // no movement + checkListPointers(t, l, []*ListNode[*testVal]{e1, e3, e4}) + + e2 = l.PushFront(&testVal{2}) + checkListPointers(t, l, []*ListNode[*testVal]{e2, e1, e3, e4}) + + l.Remove(e3) // removing from middle + checkListPointers(t, l, []*ListNode[*testVal]{e2, e1, e4}) + + l.Remove(e4) // removing from end + checkListPointers(t, l, []*ListNode[*testVal]{e2, e1}) + + l.move(e2, e1) // swapping between two elements + checkListPointers(t, l, []*ListNode[*testVal]{e1, e2}) + + l.Remove(e1) // removing front + checkListPointers(t, l, []*ListNode[*testVal]{e2}) + + l.move(e2, l.Back()) // swapping element with itself. + checkListPointers(t, l, []*ListNode[*testVal]{e2}) + + l.Remove(e2) // remove last one + checkListPointers(t, l, []*ListNode[*testVal]{}) +} + +func TestList_SingleElementListPositioning(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[*testVal]() + checkListPointers(t, l, []*ListNode[*testVal]{}) + e := l.PushFront(&testVal{1}) + checkListPointers(t, l, []*ListNode[*testVal]{e}) + l.MoveToFront(e) + checkListPointers(t, l, []*ListNode[*testVal]{e}) + l.Remove(e) + checkListPointers(t, l, []*ListNode[*testVal]{}) +} + +func TestList_RemovedNodeShouldBeMovedToFreeList(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[*testVal]() + e1 := l.PushFront(&testVal{1}) + e2 := l.PushFront(&testVal{2}) + + checkListPointers(t, l, []*ListNode[*testVal]{e2, e1}) + + e := l.Back() + l.Remove(e) + + for i := l.freeList.next; i != nil; i = i.next { + if i == e { + // stopping the test with good results: + return + } + } + t.Error("expected the removed node to appear at the freelist") +} + +func TestList_PushMoveBackRemove(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + l := NewList[testVal]().AllocateFreeNodes(4) + e1 := l.PushFront(testVal{1}) + e2 := l.PushFront(testVal{2}) + checkListPointers(t, l, []*ListNode[testVal]{e2, e1}) + + l.MoveToFront(e1) + checkListPointers(t, l, []*ListNode[testVal]{e1, e2}) + + e := l.Back() + require.Equal(t, e, e2) + l.Remove(e) + checkListPointers(t, l, []*ListNode[testVal]{e1}) + + e = l.Back() + require.Equal(t, e, e1) + l.Remove(e) + checkListPointers(t, l, []*ListNode[testVal]{}) + + e = l.Back() + require.Nil(t, e) +} diff --git a/util/set.go b/util/set.go new file mode 100644 index 0000000000..a23f543dd6 --- /dev/null +++ b/util/set.go @@ -0,0 +1,42 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package util + +// Set is a type alias for map with empty struct{}, where keys are comparable +// We don't attempt to move even forward for the generics, +// for keys being comparable should be sufficient for most cases. +// (Though we actually want compare byte slices, but seems not achievable at this moment) +type Set[T comparable] map[T]struct{} + +// Add adds variate number of elements to the set. +func (s Set[T]) Add(elems ...T) Set[T] { + for _, elem := range elems { + s[elem] = struct{}{} + } + return s +} + +// MakeSet constructs a set instance directly from elements. +func MakeSet[T comparable](elems ...T) Set[T] { + return make(Set[T]).Add(elems...) +} + +// Contains checks the membership of an element in the set. +func (s Set[T]) Contains(elem T) (exists bool) { + _, exists = s[elem] + return +}