", 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/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..c11c106c33 100755
--- a/scripts/release/mule/deploy/rpm/deploy.sh
+++ b/scripts/release/mule/deploy/rpm/deploy.sh
@@ -78,6 +78,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://$S3_SOURCE/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
+}