diff --git a/blockchain/chain.go b/blockchain/chain.go index 9a6ec216..385d96e9 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -36,8 +36,9 @@ const ( // from the block being located. // // For example, assume a block chain with a side chain as depicted below: -// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 -// \-> 16a -> 17a +// +// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 +// \-> 16a -> 17a // // The block locator for block 17a would be the hashes of blocks: // [17a 16a 15 14 13 12 11 10 9 8 7 6 4 genesis] @@ -487,7 +488,7 @@ func (b *BlockChain) calcSequenceLock(node *blockNode, tx *btcutil.Tx, utxoView // LockTimeToSequence converts the passed relative locktime to a sequence // number in accordance to BIP-68. // See: https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki -// * (Compatibility) +// - (Compatibility) func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 { // If we're expressing the relative lock time in blocks, then the // corresponding sequence number is simply the desired input age. @@ -1106,8 +1107,8 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error // a reorganization to become the main chain). // // The flags modify the behavior of this function as follows: -// - BFFastAdd: Avoids several expensive transaction validation operations. -// This is useful when using checkpoints. +// - BFFastAdd: Avoids several expensive transaction validation operations. +// This is useful when using checkpoints. // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) { @@ -1203,6 +1204,10 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla block.Hash()) } + return b.handleSideChainBetter(node, parentHash) +} + +func (b *BlockChain) handleSideChainBetter(node *blockNode, parentHash *chainhash.Hash) (bool, error) { // We're extending (or creating) a side chain, but the cumulative // work for this new side chain is not enough to make it the new chain. if node.workSum.Cmp(b.bestChain.Tip().workSum) <= 0 { @@ -1248,8 +1253,8 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla // isCurrent returns whether or not the chain believes it is current. Several // factors are used to guess, but the key factors that allow the chain to // believe it is current are: -// - Latest block height is after the latest checkpoint (if enabled) -// - Latest block has a timestamp newer than ~6 hours ago (as LBRY block time is one fourth of bitcoin) +// - Latest block height is after the latest checkpoint (if enabled) +// - Latest block has a timestamp newer than ~6 hours ago (as LBRY block time is one fourth of bitcoin) // // This function MUST be called with the chain state lock held (for reads). func (b *BlockChain) isCurrent() bool { @@ -1272,8 +1277,8 @@ func (b *BlockChain) isCurrent() bool { // IsCurrent returns whether or not the chain believes it is current. Several // factors are used to guess, but the key factors that allow the chain to // believe it is current are: -// - Latest block height is after the latest checkpoint (if enabled) -// - Latest block has a timestamp newer than 24 hours ago +// - Latest block height is after the latest checkpoint (if enabled) +// - Latest block has a timestamp newer than 24 hours ago // // This function is safe for concurrent access. func (b *BlockChain) IsCurrent() bool { @@ -1508,11 +1513,11 @@ func (b *BlockChain) IntervalBlockHashes(endHash *chainhash.Hash, interval int, // // In addition, there are two special cases: // -// - When no locators are provided, the stop hash is treated as a request for -// that block, so it will either return the node associated with the stop hash -// if it is known, or nil if it is unknown -// - When locators are provided, but none of them are known, nodes starting -// after the genesis block will be returned +// - When no locators are provided, the stop hash is treated as a request for +// that block, so it will either return the node associated with the stop hash +// if it is known, or nil if it is unknown +// - When locators are provided, but none of them are known, nodes starting +// after the genesis block will be returned // // This is primarily a helper function for the locateBlocks and locateHeaders // functions. @@ -1596,11 +1601,11 @@ func (b *BlockChain) locateBlocks(locator BlockLocator, hashStop *chainhash.Hash // // In addition, there are two special cases: // -// - When no locators are provided, the stop hash is treated as a request for -// that block, so it will either return the stop hash itself if it is known, -// or nil if it is unknown -// - When locators are provided, but none of them are known, hashes starting -// after the genesis block will be returned +// - When no locators are provided, the stop hash is treated as a request for +// that block, so it will either return the stop hash itself if it is known, +// or nil if it is unknown +// - When locators are provided, but none of them are known, hashes starting +// after the genesis block will be returned // // This function is safe for concurrent access. func (b *BlockChain) LocateBlocks(locator BlockLocator, hashStop *chainhash.Hash, maxHashes uint32) []chainhash.Hash { @@ -1641,11 +1646,11 @@ func (b *BlockChain) locateHeaders(locator BlockLocator, hashStop *chainhash.Has // // In addition, there are two special cases: // -// - When no locators are provided, the stop hash is treated as a request for -// that header, so it will either return the header for the stop hash itself -// if it is known, or nil if it is unknown -// - When locators are provided, but none of them are known, headers starting -// after the genesis block will be returned +// - When no locators are provided, the stop hash is treated as a request for +// that header, so it will either return the header for the stop hash itself +// if it is known, or nil if it is unknown +// - When locators are provided, but none of them are known, headers starting +// after the genesis block will be returned // // This function is safe for concurrent access. func (b *BlockChain) LocateHeaders(locator BlockLocator, hashStop *chainhash.Hash) []wire.BlockHeader { @@ -1672,7 +1677,7 @@ func (b *BlockChain) invalidateBlock(hash *chainhash.Hash) error { return err } - // No need to invalidate if its already invalid. + // No need to invalidate if it's already invalid. if node.status.KnownInvalid() { err := fmt.Errorf("block %s is already invalid", hash) return err diff --git a/blockchain/merkle.go b/blockchain/merkle.go index 92b59c5e..8b713ff1 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -9,6 +9,8 @@ import ( "fmt" "math" + "github.com/lbryio/lbcd/wire" + "github.com/lbryio/lbcd/chaincfg/chainhash" "github.com/lbryio/lbcd/txscript" btcutil "github.com/lbryio/lbcutil" @@ -86,7 +88,7 @@ func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash. // // The above stored as a linear array is as follows: // -// [h1 h2 h3 h4 h12 h34 root] +// [h1 h2 h3 h4 h12 h34 root] // // As the above shows, the merkle root is always the last element in the array. // @@ -223,6 +225,16 @@ func ValidateWitnessCommitment(blk *btcutil.Block) error { return nil } + if len(coinbaseTx.MsgTx().TxIn[0].Witness) == 0 { + str := fmt.Sprintf("pre-BIP0141 coinbase transaction detected. "+ + "Height: %d", blk.Height()) + log.Info(str) + + var witnessNonce [CoinbaseWitnessDataLen]byte + coinbaseTx.MsgTx().TxIn[0].Witness = wire.TxWitness{witnessNonce[:]} + blk.MsgBlock().Transactions[0].TxIn[0].Witness = wire.TxWitness{witnessNonce[:]} + } + // At this point the block contains a witness commitment, so the // coinbase transaction MUST have exactly one witness element within // its witness data and that element must be exactly diff --git a/claimtrie/node/noderepo/sqlite_impl.go b/claimtrie/node/noderepo/sqlite_impl.go new file mode 100644 index 00000000..56de2e03 --- /dev/null +++ b/claimtrie/node/noderepo/sqlite_impl.go @@ -0,0 +1,162 @@ +package noderepo + +import ( + "bytes" + "strconv" + + "zombiezen.com/go/sqlite" + "zombiezen.com/go/sqlite/sqlitex" + + "github.com/lbryio/lbcd/claimtrie/change" + "github.com/pkg/errors" +) + +type Sqlite struct { + conn *sqlite.Conn +} + +func NewSqlite(path string) (*Sqlite, error) { + + conn, err := sqlite.OpenConn(path, sqlite.OpenReadWrite) + if err != nil { + err = ensureTables(conn) + } + repo := &Sqlite{conn: conn} + return repo, errors.Wrapf(err, "unable to open %s", path) +} + +func ensureTables(conn *sqlite.Conn) error { + query := "CREATE TABLE IF NOT EXISTS changes (name BLOB NOT NULL, height INTEGER NOT NULL, offset INTEGER NOT NULL, " + + "type INTEGER NOT NULL, active_height INTEGER NOT NULL, visible_height INTEGER NOT NULL, " + + "output BLOB NOT NULL, index INTEGER NOT NULL, claimID BLOB NOT NULL, amount INTEGER NOT NULL, " + + "children_offsets TEXT NOT NULL, children BLOB NOT NULL, PRIMARY KEY(name, height, offset))" + return sqlitex.Execute(conn, query, nil) +} + +func (repo *Sqlite) AppendChanges(changes []change.Change) error { + + batch := repo.conn.Prep("INSERT INTO changes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") // may just prep this in ensureTables? + + children_lengths := bytes.NewBuffer(nil) + children := bytes.NewBuffer(nil) + + for i, chg := range changes { + batch.BindBytes(0, chg.Name) + batch.BindInt64(1, int64(chg.Height)) + batch.BindInt64(2, int64(i)) + batch.BindInt64(3, int64(chg.Type)) + batch.BindInt64(4, int64(chg.ActiveHeight)) + batch.BindInt64(5, int64(chg.VisibleHeight)) + batch.BindBytes(6, chg.OutPoint.Hash[:]) + batch.BindInt64(7, int64(chg.OutPoint.Index)) + batch.BindBytes(8, chg.ClaimID[:]) + batch.BindInt64(9, chg.Amount) + if chg.SpentChildren != nil { + children_lengths.Reset() + children.Reset() + for key, _ := range chg.SpentChildren { + children_lengths.WriteString(strconv.Itoa(len(key))) + children_lengths.WriteRune(':') + children.Write([]byte(key)) + } + batch.BindText(10, children_lengths.String()) + batch.BindBytes(11, children.Bytes()) + } + _, err := batch.Step() + if err != nil { + batch.Finalize() + return errors.Wrap(err, "in step") + } + } + return errors.Wrap(batch.Finalize(), "in commit") +} + +func (repo *Sqlite) LoadChanges(name []byte) ([]change.Change, error) { + + batch := repo.conn.Prep("SELECT * FROM changes WHERE name = ?") + batch.BindBytes(0, name) + + var changes []change.Change + for { + row, err := batch.Step() + if err != nil { + return blah + } + if !row { + break + } + var chg change.Change + chg.Name = name + chg.Height = batch.ColumnInt32(1) + chg.Type = change.ChangeType(batch.ColumnInt(3)) + chg. + changes = append(changes, chg) + } + + return changes, errors.Wrap(batch.Finalize(),) +} + +func (repo *Sqlite) DropChanges(name []byte, finalHeight int32) error { + args := sqlitex.ExecOptions{Args:[]interface{}{name, finalHeight}} + err := sqlitex.Execute(repo.conn, "DELETE FROM changes WHERE name = ? and height > ?", &args) + return errors.Wrapf(err, "in set at %s", name) +} + +func (repo *Sqlite) IterateChildren(name []byte, f func(changes []change.Change) bool) error { + start := make([]byte, len(name)+1) // zeros that last byte; need a constant len for stack alloc? + copy(start, name) + + end := make([]byte, len(name)) // max name length is 255 + copy(end, name) + validEnd := false + for i := len(name) - 1; i >= 0; i-- { + end[i]++ + if end[i] != 0 { + validEnd = true + break + } + } + if !validEnd { + end = nil // uh, we think this means run to the end of the table + } + + prefixIterOptions := &Sqlite.IterOptions{ + LowerBound: start, + UpperBound: end, + } + + iter := repo.db.NewIter(prefixIterOptions) + defer iter.Close() + + for iter.First(); iter.Valid(); iter.Next() { + // NOTE! iter.Key() is ephemeral! + changes, err := unmarshalChanges(iter.Key(), iter.Value()) + if err != nil { + return errors.Wrapf(err, "from unmarshaller at %s", iter.Key()) + } + if !f(changes) { + break + } + } + return nil +} + +func (repo *Sqlite) IterateAll(predicate func(name []byte) bool) { + // SELECT DISTINCT name FROM changes ORDER BY name + iter := repo.db.NewIter(nil) + defer iter.Close() + + for iter.First(); iter.Valid(); iter.Next() { + if !predicate(iter.Key()) { + break + } + } +} + +func (repo *Sqlite) Close() error { + return errors.Wrap(repo.conn.Close(), "on close") +} + +func (repo *Sqlite) Flush() error { + return errors.Wrap(repo.conn., "on flush") +} diff --git a/go.mod b/go.mod index 852c0428..464a8cff 100644 --- a/go.mod +++ b/go.mod @@ -36,14 +36,17 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kkdai/bstream v1.0.0 // indirect github.com/klauspost/compress v1.15.4 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect @@ -53,4 +56,9 @@ require ( golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + modernc.org/libc v1.16.7 // indirect + modernc.org/mathutil v1.4.1 // indirect + modernc.org/memory v1.1.1 // indirect + modernc.org/sqlite v1.17.3 // indirect + zombiezen.com/go/sqlite v0.10.0 // indirect ) diff --git a/go.sum b/go.sum index 9079426b..c8331051 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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= @@ -178,6 +179,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/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.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -193,6 +195,8 @@ github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1 h1:K4bn56FHdjFCfjSo3w github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 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= @@ -259,6 +263,7 @@ github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2 github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -300,7 +305,9 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd 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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= @@ -365,6 +372,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -613,6 +622,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -692,4 +702,31 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA= +modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= +modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +zombiezen.com/go/sqlite v0.10.0 h1:hegW0Y8c/fSJ2VjbjBeiKJaQOISNr4EUTx1VZx94Q9Y= +zombiezen.com/go/sqlite v0.10.0/go.mod h1:tOd9u3peffVYnXOedepSJmX92n/mbqf594wcJ+29jf8=