From ec280e030f4cd3061d40f2979fdb833912073b7b Mon Sep 17 00:00:00 2001 From: Martin HS Date: Fri, 15 Nov 2024 07:59:06 +0100 Subject: [PATCH 1/3] core/state: tests on the binary iterator (#30754) Fixes an error in the binary iterator, adds additional testcases --------- Co-authored-by: Gary Rong --- core/state/snapshot/iterator_binary.go | 91 +++++++++------ core/state/snapshot/iterator_test.go | 155 +++++++++++++++++++------ 2 files changed, 172 insertions(+), 74 deletions(-) diff --git a/core/state/snapshot/iterator_binary.go b/core/state/snapshot/iterator_binary.go index edf471213f..523c7b9946 100644 --- a/core/state/snapshot/iterator_binary.go +++ b/core/state/snapshot/iterator_binary.go @@ -39,12 +39,12 @@ type binaryIterator struct { // initBinaryAccountIterator creates a simplistic iterator to step over all the // accounts in a slow, but easily verifiable way. Note this function is used for // initialization, use `newBinaryAccountIterator` as the API. -func (dl *diffLayer) initBinaryAccountIterator() Iterator { +func (dl *diffLayer) initBinaryAccountIterator(seek common.Hash) Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { l := &binaryIterator{ - a: dl.AccountIterator(common.Hash{}), - b: dl.Parent().AccountIterator(common.Hash{}), + a: dl.AccountIterator(seek), + b: dl.Parent().AccountIterator(seek), accountIterator: true, } l.aDone = !l.a.Next() @@ -52,8 +52,8 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator { return l } l := &binaryIterator{ - a: dl.AccountIterator(common.Hash{}), - b: parent.initBinaryAccountIterator(), + a: dl.AccountIterator(seek), + b: parent.initBinaryAccountIterator(seek), accountIterator: true, } l.aDone = !l.a.Next() @@ -64,12 +64,12 @@ func (dl *diffLayer) initBinaryAccountIterator() Iterator { // initBinaryStorageIterator creates a simplistic iterator to step over all the // storage slots in a slow, but easily verifiable way. Note this function is used // for initialization, use `newBinaryStorageIterator` as the API. -func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { +func (dl *diffLayer) initBinaryStorageIterator(account, seek common.Hash) Iterator { parent, ok := dl.parent.(*diffLayer) if !ok { // If the storage in this layer is already destructed, discard all // deeper layers but still return a valid single-branch iterator. - a, destructed := dl.StorageIterator(account, common.Hash{}) + a, destructed := dl.StorageIterator(account, seek) if destructed { l := &binaryIterator{ a: a, @@ -81,7 +81,7 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { } // The parent is disk layer, don't need to take care "destructed" // anymore. - b, _ := dl.Parent().StorageIterator(account, common.Hash{}) + b, _ := dl.Parent().StorageIterator(account, seek) l := &binaryIterator{ a: a, b: b, @@ -93,7 +93,7 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { } // If the storage in this layer is already destructed, discard all // deeper layers but still return a valid single-branch iterator. - a, destructed := dl.StorageIterator(account, common.Hash{}) + a, destructed := dl.StorageIterator(account, seek) if destructed { l := &binaryIterator{ a: a, @@ -105,7 +105,7 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { } l := &binaryIterator{ a: a, - b: parent.initBinaryStorageIterator(account), + b: parent.initBinaryStorageIterator(account, seek), account: account, } l.aDone = !l.a.Next() @@ -117,33 +117,50 @@ func (dl *diffLayer) initBinaryStorageIterator(account common.Hash) Iterator { // or an error if iteration failed for some reason (e.g. root being iterated // becomes stale and garbage collected). func (it *binaryIterator) Next() bool { + for { + if !it.next() { + return false + } + if len(it.Account()) != 0 || len(it.Slot()) != 0 { + return true + } + // it.fail might be set if error occurs by calling + // it.Account() or it.Slot(), stop iteration if so. + if it.fail != nil { + return false + } + } +} + +func (it *binaryIterator) next() bool { if it.aDone && it.bDone { return false } -first: - if it.aDone { - it.k = it.b.Hash() + for { + if it.aDone { + it.k = it.b.Hash() + it.bDone = !it.b.Next() + return true + } + if it.bDone { + it.k = it.a.Hash() + it.aDone = !it.a.Next() + return true + } + nextA, nextB := it.a.Hash(), it.b.Hash() + if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 { + it.aDone = !it.a.Next() + it.k = nextA + return true + } else if diff == 0 { + // Now we need to advance one of them + it.aDone = !it.a.Next() + continue + } it.bDone = !it.b.Next() + it.k = nextB return true } - if it.bDone { - it.k = it.a.Hash() - it.aDone = !it.a.Next() - return true - } - nextA, nextB := it.a.Hash(), it.b.Hash() - if diff := bytes.Compare(nextA[:], nextB[:]); diff < 0 { - it.aDone = !it.a.Next() - it.k = nextA - return true - } else if diff == 0 { - // Now we need to advance one of them - it.aDone = !it.a.Next() - goto first - } - it.bDone = !it.b.Next() - it.k = nextB - return true } // Error returns any failure that occurred during iteration, which might have @@ -195,19 +212,21 @@ func (it *binaryIterator) Slot() []byte { // Release recursively releases all the iterators in the stack. func (it *binaryIterator) Release() { it.a.Release() - it.b.Release() + if it.b != nil { + it.b.Release() + } } // newBinaryAccountIterator creates a simplistic account iterator to step over // all the accounts in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryAccountIterator() AccountIterator { - iter := dl.initBinaryAccountIterator() +func (dl *diffLayer) newBinaryAccountIterator(seek common.Hash) AccountIterator { + iter := dl.initBinaryAccountIterator(seek) return iter.(AccountIterator) } // newBinaryStorageIterator creates a simplistic account iterator to step over // all the storage slots in a slow, but easily verifiable way. -func (dl *diffLayer) newBinaryStorageIterator(account common.Hash) StorageIterator { - iter := dl.initBinaryStorageIterator(account) +func (dl *diffLayer) newBinaryStorageIterator(account, seek common.Hash) StorageIterator { + iter := dl.initBinaryStorageIterator(account, seek) return iter.(StorageIterator) } diff --git a/core/state/snapshot/iterator_test.go b/core/state/snapshot/iterator_test.go index daa8cdcc54..f2f4d19f83 100644 --- a/core/state/snapshot/iterator_test.go +++ b/core/state/snapshot/iterator_test.go @@ -58,6 +58,9 @@ func TestAccountIteratorBasics(t *testing.T) { it := diffLayer.AccountIterator(common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + it = diffLayer.newBinaryAccountIterator(common.Hash{}) + verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator + diskLayer := diffToDisk(diffLayer) it = diskLayer.AccountIterator(common.Hash{}) verifyIterator(t, 100, it, verifyNothing) // Nil is allowed for single layer iterator @@ -235,7 +238,7 @@ func TestAccountIteratorTraversal(t *testing.T) { head := snaps.Snapshot(common.HexToHash("0x04")) verifyIterator(t, 3, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing) - verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) verifyIterator(t, 7, it, verifyAccount) @@ -249,7 +252,7 @@ func TestAccountIteratorTraversal(t *testing.T) { }() aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x04"), 2) - verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 7, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) verifyIterator(t, 7, it, verifyAccount) @@ -283,7 +286,7 @@ func TestStorageIteratorTraversal(t *testing.T) { diffIter, _ := head.(snapshot).StorageIterator(common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 3, diffIter, verifyNothing) - verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) it, _ := snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 6, it, verifyStorage) @@ -297,7 +300,7 @@ func TestStorageIteratorTraversal(t *testing.T) { }() aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x04"), 2) - verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + verifyIterator(t, 6, head.(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), verifyStorage) it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 6, it, verifyStorage) @@ -528,7 +531,7 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { // Iterate the entire stack and ensure everything is hit only once head := snaps.Snapshot(common.HexToHash("0x80")) verifyIterator(t, 200, head.(snapshot).AccountIterator(common.Hash{}), verifyNothing) - verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ := snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{}) verifyIterator(t, 200, it, verifyAccount) @@ -543,7 +546,7 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { aggregatorMemoryLimit = 0 // Force pushing the bottom-most layer into disk snaps.Cap(common.HexToHash("0x80"), 2) - verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(), verifyAccount) + verifyIterator(t, 200, head.(*diffLayer).newBinaryAccountIterator(common.Hash{}), verifyAccount) it, _ = snaps.AccountIterator(common.HexToHash("0x80"), common.Hash{}) verifyIterator(t, 200, it, verifyAccount) @@ -555,6 +558,20 @@ func TestAccountIteratorLargeTraversal(t *testing.T) { // - flattens C2 all the way into CN // - continues iterating func TestAccountIteratorFlattening(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorFlattening(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + }) + }) +} + +func testAccountIteratorFlattening(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -577,7 +594,7 @@ func TestAccountIteratorFlattening(t *testing.T) { randomAccountSet("0xcc", "0xf0", "0xff"), nil) // Create an iterator and flatten the data from underneath it - it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + it := newIterator(snaps, common.HexToHash("0x04"), common.Hash{}) defer it.Release() if err := snaps.Cap(common.HexToHash("0x04"), 1); err != nil { @@ -587,6 +604,21 @@ func TestAccountIteratorFlattening(t *testing.T) { } func TestAccountIteratorSeek(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorSeek(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it := snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + return it + }) + }) +} + +func testAccountIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -612,44 +644,58 @@ func TestAccountIteratorSeek(t *testing.T) { // 03: aa, bb, dd, ee, f0 (, f0), ff // 04: aa, bb, cc, dd, ee, f0 (, f0), ff (, ff) // Construct various iterators and ensure their traversal is correct - it, _ := snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xdd")) + it := newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xdd")) defer it.Release() verifyIterator(t, 3, it, verifyAccount) // expected: ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xaa")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa")) defer it.Release() verifyIterator(t, 4, it, verifyAccount) // expected: aa, ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xff")) defer it.Release() verifyIterator(t, 1, it, verifyAccount) // expected: ff - it, _ = snaps.AccountIterator(common.HexToHash("0x02"), common.HexToHash("0xff1")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xff1")) defer it.Release() verifyIterator(t, 0, it, verifyAccount) // expected: nothing - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xbb")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xbb")) defer it.Release() verifyIterator(t, 6, it, verifyAccount) // expected: bb, cc, dd, ee, f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xef")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xef")) defer it.Release() verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xf0")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xf0")) defer it.Release() verifyIterator(t, 2, it, verifyAccount) // expected: f0, ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xff")) defer it.Release() verifyIterator(t, 1, it, verifyAccount) // expected: ff - it, _ = snaps.AccountIterator(common.HexToHash("0x04"), common.HexToHash("0xff1")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xff1")) defer it.Release() verifyIterator(t, 0, it, verifyAccount) // expected: nothing } func TestStorageIteratorSeek(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + it, _ := snaps.StorageIterator(root, account, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testStorageIteratorSeek(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) + }) + }) +} + +func testStorageIteratorSeek(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { // Create a snapshot stack with some initial data base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -676,35 +722,35 @@ func TestStorageIteratorSeek(t *testing.T) { // 03: 01, 02, 03, 05 (, 05), 06 // 04: 01(, 01), 02, 03, 05(, 05, 05), 06, 08 // Construct various iterators and ensure their traversal is correct - it, _ := snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + it := newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x01")) defer it.Release() verifyIterator(t, 3, it, verifyStorage) // expected: 01, 03, 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x02")) defer it.Release() verifyIterator(t, 2, it, verifyStorage) // expected: 03, 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x5")) defer it.Release() verifyIterator(t, 1, it, verifyStorage) // expected: 05 - it, _ = snaps.StorageIterator(common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6")) + it = newIterator(snaps, common.HexToHash("0x02"), common.HexToHash("0xaa"), common.HexToHash("0x6")) defer it.Release() verifyIterator(t, 0, it, verifyStorage) // expected: nothing - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x01")) defer it.Release() verifyIterator(t, 6, it, verifyStorage) // expected: 01, 02, 03, 05, 06, 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x05")) defer it.Release() verifyIterator(t, 3, it, verifyStorage) // expected: 05, 06, 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x08")) defer it.Release() verifyIterator(t, 1, it, verifyStorage) // expected: 08 - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09")) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.HexToHash("0x09")) defer it.Release() verifyIterator(t, 0, it, verifyStorage) // expected: nothing } @@ -713,6 +759,20 @@ func TestStorageIteratorSeek(t *testing.T) { // deleted accounts (where the Account() value is nil). The iterator // should not output any accounts or nil-values for those cases. func TestAccountIteratorDeletions(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + it, _ := snaps.AccountIterator(root, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testAccountIteratorDeletions(t, func(snaps *Tree, root, seek common.Hash) AccountIterator { + return snaps.layers[root].(*diffLayer).newBinaryAccountIterator(seek) + }) + }) +} + +func testAccountIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, seek common.Hash) AccountIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -739,7 +799,8 @@ func TestAccountIteratorDeletions(t *testing.T) { nil, randomAccountSet("0x33", "0x44", "0x55"), nil) // The output should be 11,33,44,55 - it, _ := snaps.AccountIterator(common.HexToHash("0x04"), common.Hash{}) + it := newIterator(snaps, common.HexToHash("0x04"), (common.Hash{})) + // Do a quick check verifyIterator(t, 4, it, verifyAccount) it.Release() @@ -759,6 +820,20 @@ func TestAccountIteratorDeletions(t *testing.T) { } func TestStorageIteratorDeletions(t *testing.T) { + t.Run("fast", func(t *testing.T) { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + it, _ := snaps.StorageIterator(root, account, seek) + return it + }) + }) + t.Run("binary", func(t *testing.T) { + testStorageIteratorDeletions(t, func(snaps *Tree, root, account, seek common.Hash) StorageIterator { + return snaps.layers[root].(*diffLayer).newBinaryStorageIterator(account, seek) + }) + }) +} + +func testStorageIteratorDeletions(t *testing.T, newIterator func(snaps *Tree, root, account, seek common.Hash) StorageIterator) { // Create an empty base layer and a snapshot tree out of it base := &diskLayer{ diskdb: rawdb.NewMemoryDatabase(), @@ -778,12 +853,12 @@ func TestStorageIteratorDeletions(t *testing.T) { randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x02", "0x04", "0x06"}}, [][]string{{"0x01", "0x03"}})) // The output should be 02,04,05,06 - it, _ := snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{}) + it := newIterator(snaps, common.HexToHash("0x03"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 4, it, verifyStorage) it.Release() // The output should be 04,05,06 - it, _ = snaps.StorageIterator(common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03")) + it = newIterator(snaps, common.HexToHash("0x03"), common.HexToHash("0xaa"), common.HexToHash("0x03")) verifyIterator(t, 3, it, verifyStorage) it.Release() @@ -793,7 +868,7 @@ func TestStorageIteratorDeletions(t *testing.T) { } snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), destructed, nil, nil) - it, _ = snaps.StorageIterator(common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) + it = newIterator(snaps, common.HexToHash("0x04"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 0, it, verifyStorage) it.Release() @@ -802,17 +877,21 @@ func TestStorageIteratorDeletions(t *testing.T) { randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x07", "0x08", "0x09"}}, nil)) // The output should be 07,08,09 - it, _ = snaps.StorageIterator(common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{}) + + it = newIterator(snaps, common.HexToHash("0x05"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 3, it, verifyStorage) it.Release() // Destruct the whole storage but re-create the account in the same layer snaps.Update(common.HexToHash("0x06"), common.HexToHash("0x05"), destructed, randomAccountSet("0xaa"), randomStorageSet([]string{"0xaa"}, [][]string{{"0x11", "0x12"}}, nil)) - it, _ = snaps.StorageIterator(common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{}) + it = newIterator(snaps, common.HexToHash("0x06"), common.HexToHash("0xaa"), common.Hash{}) verifyIterator(t, 2, it, verifyStorage) // The output should be 11,12 it.Release() - verifyIterator(t, 2, snaps.Snapshot(common.HexToHash("0x06")).(*diffLayer).newBinaryStorageIterator(common.HexToHash("0xaa")), verifyStorage) + verifyIterator(t, 2, snaps.Snapshot( + common.HexToHash("0x06")).(*diffLayer). + newBinaryStorageIterator(common.HexToHash("0xaa"), common.Hash{}), + verifyStorage) } // BenchmarkAccountIteratorTraversal is a bit notorious -- all layers contain the @@ -854,12 +933,12 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { // We call this once before the benchmark, so the creation of // sorted accountlists are not included in the results. head := snaps.Snapshot(common.HexToHash("0x65")) - head.(*diffLayer).newBinaryAccountIterator() + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator keys", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ } @@ -871,7 +950,7 @@ func BenchmarkAccountIteratorTraversal(b *testing.B) { b.Run("binary iterator values", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ head.(*diffLayer).accountRLP(it.Hash(), 0) @@ -951,12 +1030,12 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { // We call this once before the benchmark, so the creation of // sorted accountlists are not included in the results. head := snaps.Snapshot(common.HexToHash("0x65")) - head.(*diffLayer).newBinaryAccountIterator() + head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) b.Run("binary iterator (keys)", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ } @@ -968,7 +1047,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { b.Run("binary iterator (values)", func(b *testing.B) { for i := 0; i < b.N; i++ { got := 0 - it := head.(*diffLayer).newBinaryAccountIterator() + it := head.(*diffLayer).newBinaryAccountIterator(common.Hash{}) for it.Next() { got++ v := it.Hash() @@ -1013,7 +1092,7 @@ func BenchmarkAccountIteratorLargeBaselayer(b *testing.B) { /* func BenchmarkBinaryAccountIteration(b *testing.B) { benchmarkAccountIteration(b, func(snap snapshot) AccountIterator { - return snap.(*diffLayer).newBinaryAccountIterator() + return snap.(*diffLayer).newBinaryAccountIterator(common.Hash{}) }) } From a5f00018451e7f5eab156785d93de18244a27b66 Mon Sep 17 00:00:00 2001 From: Martin HS Date: Fri, 15 Nov 2024 10:15:15 +0100 Subject: [PATCH 2/3] cmd/geth: remove unlock commandline flag (#30737) This is one further step towards removing account management from `geth`. This PR deprecates the flag `unlock`, and makes the flag moot: unlock via geth is no longer possible. --- accounts/keystore/account_cache.go | 3 +- accounts/keystore/testdata/dupes/1 | 1 - accounts/keystore/testdata/dupes/2 | 1 - accounts/keystore/testdata/dupes/foo | 1 - accounts/manager.go | 14 +-- cmd/geth/accountcmd.go | 113 ++++++++--------- cmd/geth/accountcmd_test.go | 176 +-------------------------- cmd/geth/main.go | 40 +----- cmd/utils/flags.go | 71 ++--------- cmd/utils/flags_legacy.go | 11 ++ cmd/utils/prompt.go | 15 --- cmd/utils/prompt_test.go | 76 ------------ internal/ethapi/api_test.go | 2 +- node/config.go | 2 +- node/node.go | 2 +- signer/core/api.go | 4 +- 16 files changed, 82 insertions(+), 450 deletions(-) delete mode 100644 accounts/keystore/testdata/dupes/1 delete mode 100644 accounts/keystore/testdata/dupes/2 delete mode 100644 accounts/keystore/testdata/dupes/foo delete mode 100644 cmd/utils/prompt_test.go diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index f7cf688e62..d3a98850c7 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -44,8 +44,7 @@ func byURL(a, b accounts.Account) int { return a.URL.Cmp(b.URL) } -// AmbiguousAddrError is returned when attempting to unlock -// an address for which more than one file exists. +// AmbiguousAddrError is returned when an address matches multiple files. type AmbiguousAddrError struct { Addr common.Address Matches []accounts.Account diff --git a/accounts/keystore/testdata/dupes/1 b/accounts/keystore/testdata/dupes/1 deleted file mode 100644 index a3868ec6d5..0000000000 --- a/accounts/keystore/testdata/dupes/1 +++ /dev/null @@ -1 +0,0 @@ -{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/accounts/keystore/testdata/dupes/2 b/accounts/keystore/testdata/dupes/2 deleted file mode 100644 index a3868ec6d5..0000000000 --- a/accounts/keystore/testdata/dupes/2 +++ /dev/null @@ -1 +0,0 @@ -{"address":"f466859ead1932d743d622cb74fc058882e8648a","crypto":{"cipher":"aes-128-ctr","ciphertext":"cb664472deacb41a2e995fa7f96fe29ce744471deb8d146a0e43c7898c9ddd4d","cipherparams":{"iv":"dfd9ee70812add5f4b8f89d0811c9158"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"0d6769bf016d45c479213990d6a08d938469c4adad8a02ce507b4a4e7b7739f1"},"mac":"bac9af994b15a45dd39669fc66f9aa8a3b9dd8c22cb16e4d8d7ea089d0f1a1a9"},"id":"472e8b3d-afb6-45b5-8111-72c89895099a","version":3} \ No newline at end of file diff --git a/accounts/keystore/testdata/dupes/foo b/accounts/keystore/testdata/dupes/foo deleted file mode 100644 index c57060aea0..0000000000 --- a/accounts/keystore/testdata/dupes/foo +++ /dev/null @@ -1 +0,0 @@ -{"address":"7ef5a6135f1fd6a02593eedc869c6d41d934aef8","crypto":{"cipher":"aes-128-ctr","ciphertext":"1d0839166e7a15b9c1333fc865d69858b22df26815ccf601b28219b6192974e1","cipherparams":{"iv":"8df6caa7ff1b00c4e871f002cb7921ed"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":8,"p":16,"r":8,"salt":"e5e6ef3f4ea695f496b643ebd3f75c0aa58ef4070e90c80c5d3fb0241bf1595c"},"mac":"6d16dfde774845e4585357f24bce530528bc69f4f84e1e22880d34fa45c273e5"},"id":"950077c7-71e3-4c44-a4a1-143919141ed4","version":3} \ No newline at end of file diff --git a/accounts/manager.go b/accounts/manager.go index cbe4f7c79d..ac21ecd985 100644 --- a/accounts/manager.go +++ b/accounts/manager.go @@ -29,12 +29,9 @@ import ( // the manager will buffer in its channel. const managerSubBufferSize = 50 -// Config contains the settings of the global account manager. -// -// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management -// is removed in favor of Clef. +// Config is a legacy struct which is not used type Config struct { - InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed + InsecureUnlockAllowed bool // Unused legacy-parameter } // newBackendEvent lets the manager know it should @@ -47,7 +44,6 @@ type newBackendEvent struct { // Manager is an overarching account manager that can communicate with various // backends for signing transactions. type Manager struct { - config *Config // Global account manager configurations backends map[reflect.Type][]Backend // Index of backends currently registered updaters []event.Subscription // Wallet update subscriptions for all backends updates chan WalletEvent // Subscription sink for backend wallet changes @@ -78,7 +74,6 @@ func NewManager(config *Config, backends ...Backend) *Manager { } // Assemble the account manager and return am := &Manager{ - config: config, backends: make(map[reflect.Type][]Backend), updaters: subs, updates: updates, @@ -106,11 +101,6 @@ func (am *Manager) Close() error { return <-errc } -// Config returns the configuration of account manager. -func (am *Manager) Config() *Config { - return am.config -} - // AddBackend starts the tracking of an additional backend for wallet updates. // cmd/geth assumes once this func returns the backends have been already integrated. func (am *Manager) AddBackend(backend Backend) { diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index cc22684e0b..b564fa3b57 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -17,14 +17,16 @@ package main import ( + "errors" "fmt" "os" + "strings" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2" ) @@ -191,7 +193,7 @@ nodes. // makeAccountManager creates an account manager with backends func makeAccountManager(ctx *cli.Context) *accounts.Manager { cfg := loadBaseConfig(ctx) - am := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: cfg.Node.InsecureUnlockAllowed}) + am := accounts.NewManager(nil) keydir, isEphemeral, err := cfg.Node.GetKeyStoreDir() if err != nil { utils.Fatalf("Failed to get the keystore directory: %v", err) @@ -219,60 +221,22 @@ func accountList(ctx *cli.Context) error { return nil } -// tries unlocking the specified account a few times. -func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) { - account, err := utils.MakeAddress(ks, address) +// readPasswordFromFile reads the first line of the given file, trims line endings, +// and returns the password and whether the reading was successful. +func readPasswordFromFile(path string) (string, bool) { + if path == "" { + return "", false + } + text, err := os.ReadFile(path) if err != nil { - utils.Fatalf("Could not list accounts: %v", err) + utils.Fatalf("Failed to read password file: %v", err) } - for trials := 0; trials < 3; trials++ { - prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3) - password := utils.GetPassPhraseWithList(prompt, false, i, passwords) - err = ks.Unlock(account, password) - if err == nil { - log.Info("Unlocked account", "address", account.Address.Hex()) - return account, password - } - if err, ok := err.(*keystore.AmbiguousAddrError); ok { - log.Info("Unlocked account", "address", account.Address.Hex()) - return ambiguousAddrRecovery(ks, err, password), password - } - if err != keystore.ErrDecrypt { - // No need to prompt again if the error is not decryption-related. - break - } + lines := strings.Split(string(text), "\n") + if len(lines) == 0 { + return "", false } - // All trials expended to unlock account, bail out - utils.Fatalf("Failed to unlock account %s (%v)", address, err) - - return accounts.Account{}, "" -} - -func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account { - fmt.Printf("Multiple key files exist for address %x:\n", err.Addr) - for _, a := range err.Matches { - fmt.Println(" ", a.URL) - } - fmt.Println("Testing your password against all of them...") - var match *accounts.Account - for i, a := range err.Matches { - if e := ks.Unlock(a, auth); e == nil { - match = &err.Matches[i] - break - } - } - if match == nil { - utils.Fatalf("None of the listed files could be unlocked.") - return accounts.Account{} - } - fmt.Printf("Your password unlocked %s\n", match.URL) - fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") - for _, a := range err.Matches { - if a != *match { - fmt.Println(" ", a.URL) - } - } - return *match + // Sanitise DOS line endings. + return strings.TrimRight(lines[0], "\r"), true } // accountCreate creates a new account into the keystore defined by the CLI flags. @@ -292,8 +256,10 @@ func accountCreate(ctx *cli.Context) error { scryptP = keystore.LightScryptP } - password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) + } account, err := keystore.StoreKey(keydir, password, scryptN, scryptP) if err != nil { @@ -323,10 +289,23 @@ func accountUpdate(ctx *cli.Context) error { ks := backends[0].(*keystore.KeyStore) for _, addr := range ctx.Args().Slice() { - account, oldPassword := unlockAccount(ks, addr, 0, nil) - newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil) - if err := ks.Update(account, oldPassword, newPassword); err != nil { - utils.Fatalf("Could not update the account: %v", err) + if !common.IsHexAddress(addr) { + return errors.New("address must be specified in hexadecimal form") + } + account := accounts.Account{Address: common.HexToAddress(addr)} + newPassword := utils.GetPassPhrase("Please give a NEW password. Do not forget this password.", true) + updateFn := func(attempt int) error { + prompt := fmt.Sprintf("Please provide the OLD password for account %s | Attempt %d/%d", addr, attempt+1, 3) + password := utils.GetPassPhrase(prompt, false) + return ks.Update(account, password, newPassword) + } + // let user attempt unlock thrice. + err := updateFn(0) + for attempts := 1; attempts < 3 && errors.Is(err, keystore.ErrDecrypt); attempts++ { + err = updateFn(attempts) + } + if err != nil { + return fmt.Errorf("could not update account: %w", err) } } return nil @@ -347,10 +326,12 @@ func importWallet(ctx *cli.Context) error { if len(backends) == 0 { utils.Fatalf("Keystore is not available") } + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("", false) + } ks := backends[0].(*keystore.KeyStore) - passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx)) - - acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) + acct, err := ks.ImportPreSaleKey(keyJSON, password) if err != nil { utils.Fatalf("%v", err) } @@ -373,9 +354,11 @@ func accountImport(ctx *cli.Context) error { utils.Fatalf("Keystore is not available") } ks := backends[0].(*keystore.KeyStore) - passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx)) - - acct, err := ks.ImportECDSA(key, passphrase) + password, ok := readPasswordFromFile(ctx.Path(utils.PasswordFileFlag.Name)) + if !ok { + password = utils.GetPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) + } + acct, err := ks.ImportECDSA(key, password) if err != nil { utils.Fatalf("Could not create the account: %v", err) } diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go index 8416eb40ef..ab093b54ff 100644 --- a/cmd/geth/accountcmd_test.go +++ b/cmd/geth/accountcmd_test.go @@ -20,7 +20,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "testing" "github.com/cespare/cp" @@ -171,12 +170,12 @@ func TestAccountUpdate(t *testing.T) { "f466859ead1932d743d622cb74fc058882e8648a") defer geth.ExpectExit() geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +Please give a NEW password. Do not forget this password. !! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Please give a new password. Do not forget this password. Password: {{.InputLine "foobar2"}} Repeat password: {{.InputLine "foobar2"}} +Please provide the OLD password for account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 +Password: {{.InputLine "foobar"}} `) } @@ -206,172 +205,3 @@ Password: {{.InputLine "wrong"}} Fatal: could not decrypt key with given password `) } - -func TestUnlockFlag(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')") - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagWrongPassword(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')") - - defer geth.ExpectExit() - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "wrong1"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3 -Password: {{.InputLine "wrong2"}} -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3 -Password: {{.InputLine "wrong3"}} -Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password) -`) -} - -// https://github.com/ethereum/go-ethereum/issues/1785 -func TestUnlockFlagMultiIndex(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')") - - geth.Expect(` -Unlocking account 0 | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Unlocking account 2 | Attempt 1/3 -Password: {{.InputLine "foobar"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFile(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", "testdata/passwords.txt", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')") - - geth.Expect(` -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8", - "=0x289d485D9771714CCe91D3393D764E1311907ACc", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) { - t.Parallel() - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", - "testdata/wrong-passwords.txt", "--unlock", "0,2") - defer geth.ExpectExit() - geth.Expect(` -Fatal: Failed to unlock account 0 (could not decrypt key with given password) -`) -} - -func TestUnlockFlagAmbiguous(t *testing.T) { - t.Parallel() - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore", - store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", - "console", "--exec", "loadScript('testdata/empty.js')") - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "foobar"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your password against all of them... -Your password unlocked keystore://{{keypath "1"}} -In order to avoid this warning, you need to remove the following duplicate key files: - keystore://{{keypath "2"}} -undefined -`) - geth.ExpectExit() - - wantMessages := []string{ - "Unlocked account", - "=0xf466859eAD1932D743d622CB74FC058882E8648A", - } - for _, m := range wantMessages { - if !strings.Contains(geth.StderrText(), m) { - t.Errorf("stderr text does not contain %q", m) - } - } -} - -func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) { - t.Parallel() - store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes") - geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t), - "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore", - store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a") - - defer geth.ExpectExit() - - // Helper for the expect template, returns absolute keystore path. - geth.SetTemplateFunc("keypath", func(file string) string { - abs, _ := filepath.Abs(filepath.Join(store, file)) - return abs - }) - geth.Expect(` -Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 -!! Unsupported terminal, password will be echoed. -Password: {{.InputLine "wrong"}} -Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: - keystore://{{keypath "1"}} - keystore://{{keypath "2"}} -Testing your password against all of them... -Fatal: None of the listed files could be unlocked. -`) - geth.ExpectExit() -} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 10d4052737..1527e180fb 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -23,11 +23,9 @@ import ( "slices" "sort" "strconv" - "strings" "time" "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console/prompt" @@ -353,14 +351,14 @@ func geth(ctx *cli.Context) error { } // startNode boots up the system node and all registered protocols, after which -// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the -// miner. +// it starts the RPC/IPC interfaces and the miner. func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { // Start up the node itself utils.StartNode(ctx, stack, isConsole) - // Unlock any account specifically requested - unlockAccounts(ctx, stack) + if ctx.IsSet(utils.UnlockedAccountFlag.Name) { + log.Warn(`The "unlock" flag has been deprecated and has no effect`) + } // Register wallet event handlers to open and auto-derive wallets events := make(chan accounts.WalletEvent, 16) @@ -427,33 +425,3 @@ func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { }() } } - -// unlockAccounts unlocks any account specifically requested. -func unlockAccounts(ctx *cli.Context, stack *node.Node) { - var unlocks []string - inputs := strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",") - for _, input := range inputs { - if trimmed := strings.TrimSpace(input); trimmed != "" { - unlocks = append(unlocks, trimmed) - } - } - // Short circuit if there is no account to unlock. - if len(unlocks) == 0 { - return - } - // If insecure account unlocking is not allowed if node's APIs are exposed to external. - // Print warning log to user and skip unlocking. - if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() { - utils.Fatalf("Account unlock with HTTP access is forbidden!") - } - backends := stack.AccountManager().Backends(keystore.KeyStoreType) - if len(backends) == 0 { - log.Warn("Failed to unlock accounts, keystore is not available") - return - } - ks := backends[0].(*keystore.KeyStore) - passwords := utils.MakePasswordList(ctx) - for i, account := range unlocks { - unlockAccount(ks, account, i, passwords) - } -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 4eef66ebc6..e635dd89c3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -499,12 +499,6 @@ var ( } // Account settings - UnlockedAccountFlag = &cli.StringFlag{ - Name: "unlock", - Usage: "Comma separated list of accounts to unlock", - Value: "", - Category: flags.AccountCategory, - } PasswordFileFlag = &cli.PathFlag{ Name: "password", Usage: "Password file to use for non-interactive password input", @@ -517,12 +511,6 @@ var ( Value: "", Category: flags.AccountCategory, } - InsecureUnlockAllowedFlag = &cli.BoolFlag{ - Name: "allow-insecure-unlock", - Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http", - Category: flags.AccountCategory, - } - // EVM settings VMEnableDebugFlag = &cli.BoolFlag{ Name: "vmdebug", @@ -1268,31 +1256,6 @@ func MakeDatabaseHandles(max int) int { return int(raised / 2) // Leave half for networking and other stuff } -// MakeAddress converts an account specified directly as a hex encoded string or -// a key index in the key store to an internal account representation. -func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error) { - // If the specified account is a valid address, return it - if common.IsHexAddress(account) { - return accounts.Account{Address: common.HexToAddress(account)}, nil - } - // Otherwise try to interpret the account as a keystore index - index, err := strconv.Atoi(account) - if err != nil || index < 0 { - return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) - } - log.Warn("-------------------------------------------------------------------") - log.Warn("Referring to accounts by order in the keystore folder is dangerous!") - log.Warn("This functionality is deprecated and will be removed in the future!") - log.Warn("Please use explicit addresses! (can search via `geth account list`)") - log.Warn("-------------------------------------------------------------------") - - accs := ks.Accounts() - if len(accs) <= index { - return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) - } - return accs[index], nil -} - // setEtherbase retrieves the etherbase from the directly specified command line flags. func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.IsSet(MinerEtherbaseFlag.Name) { @@ -1313,24 +1276,6 @@ func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) { cfg.Miner.PendingFeeRecipient = common.BytesToAddress(b) } -// MakePasswordList reads password lines from the file specified by the global --password flag. -func MakePasswordList(ctx *cli.Context) []string { - path := ctx.Path(PasswordFileFlag.Name) - if path == "" { - return nil - } - text, err := os.ReadFile(path) - if err != nil { - Fatalf("Failed to read password file: %v", err) - } - lines := strings.Split(string(text), "\n") - // Sanitise DOS line endings. - for i := range lines { - lines[i] = strings.TrimRight(lines[i], "\r") - } - return lines -} - func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { setNodeKey(ctx, cfg) setNAT(ctx, cfg) @@ -1412,7 +1357,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { cfg.USB = ctx.Bool(USBFlag.Name) } if ctx.IsSet(InsecureUnlockAllowedFlag.Name) { - cfg.InsecureUnlockAllowed = ctx.Bool(InsecureUnlockAllowedFlag.Name) + log.Warn(fmt.Sprintf("Option %q is deprecated and has no effect", InsecureUnlockAllowedFlag.Name)) } if ctx.IsSet(DBEngineFlag.Name) { dbEngine := ctx.String(DBEngineFlag.Name) @@ -1805,13 +1750,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { passphrase string err error ) - if list := MakePasswordList(ctx); len(list) > 0 { - // Just take the first value. Although the function returns a possible multiple values and - // some usages iterate through them as attempts, that doesn't make sense in this setting, - // when we're definitely concerned with only one account. - passphrase = list[0] + if path := ctx.Path(PasswordFileFlag.Name); path != "" { + if text, err := os.ReadFile(path); err != nil { + Fatalf("Failed to read password file: %v", err) + } else { + if lines := strings.Split(string(text), "\n"); len(lines) > 0 { + passphrase = strings.TrimRight(lines[0], "\r") // Sanitise DOS line endings. + } + } } - // Unlock the developer account by local keystore. var ks *keystore.KeyStore if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index 6209516e05..ff63dd5685 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -159,6 +159,17 @@ var ( Usage: "This used to enable the 'personal' namespace.", Category: flags.DeprecatedCategory, } + UnlockedAccountFlag = &cli.StringFlag{ + Name: "unlock", + Usage: "Comma separated list of accounts to unlock (deprecated)", + Value: "", + Category: flags.DeprecatedCategory, + } + InsecureUnlockAllowedFlag = &cli.BoolFlag{ + Name: "allow-insecure-unlock", + Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http (deprecated)", + Category: flags.DeprecatedCategory, + } ) // showDeprecated displays deprecated flags that will be soon removed from the codebase. diff --git a/cmd/utils/prompt.go b/cmd/utils/prompt.go index f513e38188..9419b77010 100644 --- a/cmd/utils/prompt.go +++ b/cmd/utils/prompt.go @@ -45,18 +45,3 @@ func GetPassPhrase(text string, confirmation bool) string { } return password } - -// GetPassPhraseWithList retrieves the password associated with an account, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -func GetPassPhraseWithList(text string, confirmation bool, index int, passwords []string) string { - // If a list of passwords was supplied, retrieve from them - if len(passwords) > 0 { - if index < len(passwords) { - return passwords[index] - } - return passwords[len(passwords)-1] - } - // Otherwise prompt the user for the password - password := GetPassPhrase(text, confirmation) - return password -} diff --git a/cmd/utils/prompt_test.go b/cmd/utils/prompt_test.go deleted file mode 100644 index 236353a7cc..0000000000 --- a/cmd/utils/prompt_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -// Package utils contains internal helper functions for go-ethereum commands. -package utils - -import ( - "testing" -) - -func TestGetPassPhraseWithList(t *testing.T) { - t.Parallel() - type args struct { - text string - confirmation bool - index int - passwords []string - } - tests := []struct { - name string - args args - want string - }{ - { - "test1", - args{ - "text1", - false, - 0, - []string{"zero", "one", "two"}, - }, - "zero", - }, - { - "test2", - args{ - "text2", - false, - 5, - []string{"zero", "one", "two"}, - }, - "two", - }, - { - "test3", - args{ - "text3", - true, - 1, - []string{"zero", "one", "two"}, - }, - "one", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - if got := GetPassPhraseWithList(tt.args.text, tt.args.confirmation, tt.args.index, tt.args.passwords); got != tt.want { - t.Errorf("GetPassPhraseWithList() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 758638cb16..f570c5dc4c 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -416,7 +416,7 @@ func allBlobTxs(addr common.Address, config *params.ChainConfig) []txData { func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) { var ( dir = t.TempDir() - am = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true}) + am = accounts.NewManager(nil) b = keystore.NewKeyStore(dir, 2, 1) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") ) diff --git a/node/config.go b/node/config.go index 949db887e4..dc436876cc 100644 --- a/node/config.go +++ b/node/config.go @@ -83,7 +83,7 @@ type Config struct { // scrypt KDF at the expense of security. UseLightweightKDF bool `toml:",omitempty"` - // InsecureUnlockAllowed allows user to unlock accounts in unsafe http environment. + // InsecureUnlockAllowed is a deprecated option to allow users to accounts in unsafe http environment. InsecureUnlockAllowed bool `toml:",omitempty"` // NoUSB disables hardware wallet monitoring and connectivity. diff --git a/node/node.go b/node/node.go index 92c0c35607..ec7382e725 100644 --- a/node/node.go +++ b/node/node.go @@ -130,7 +130,7 @@ func New(conf *Config) (*Node, error) { node.keyDirTemp = isEphem // Creates an empty AccountManager with no backends. Callers (e.g. cmd/geth) // are required to add the backends later on. - node.accman = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed}) + node.accman = accounts.NewManager(nil) // Initialize the p2p server. This creates the node key and discovery databases. node.server.Config.PrivateKey = node.config.NodeKey() diff --git a/signer/core/api.go b/signer/core/api.go index 23ddcd0a20..def2d6041f 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -184,9 +184,7 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool, scpath str } } } - - // Clef doesn't allow insecure http account unlock. - return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) + return accounts.NewManager(nil, backends...) } // MetadataFromContext extracts Metadata from a given context.Context From 7c0ff056850928166c0db3d975309ece00b34cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Kj=C3=A6rstad?= Date: Fri, 15 Nov 2024 14:05:23 +0100 Subject: [PATCH 3/3] build: upgrade -dlgo version to Go 1.23.3 (#30742) New release: https://groups.google.com/g/golang-announce/c/X5KodEJYuqI --- build/checksums.txt | 98 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 3da5d00dee..b835215850 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -5,56 +5,56 @@ # https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/ ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz -# version:golang 1.23.2 +# version:golang 1.23.3 # https://go.dev/dl/ -36930162a93df417d90bd22c6e14daff4705baac2b02418edda671cdfa9cd07f go1.23.2.src.tar.gz -025d77f1780906142023a364c31a572afd7d56d3a3be1e4e562e367ca88d3267 go1.23.2.freebsd-amd64.tar.gz -0d50bade977b84e173cb350946087f5de8c75f8df19456c3b60c5d58e186089d go1.23.2.windows-arm64.zip -0edd985dbd6de64d9c88dbc8835bae21203c58444bf26fce0739cbec4eb1b610 go1.23.2.windows-arm64.msi -2283d12dfe7c8c8a46a41bbf7d11fe007434e7590cd1b89e221e478640b7ee3a go1.23.2.linux-mips64le.tar.gz -2293c5c3ffc595418308b4059ce214b99f0383cba83232e47a1a8c3b710c24e8 go1.23.2.linux-loong64.tar.gz -23b93144e754bbcf5eda700e9decbdbd44d29ceedb1bf1de75f95e8a6ea986bb go1.23.2.openbsd-arm64.tar.gz -2734a5b54905cea45f136c28249e626d0241b865b0637fa1db64bf533d9d843e go1.23.2.netbsd-amd64.tar.gz -28af3c40687afdda6b33b300833b6d662716cc2d624fb9fd61a49bdad44cd869 go1.23.2.freebsd-arm.tar.gz -367d522b47c7ce7761a671efcb8b12c8af8f509db1cd6160c91f410ef3201987 go1.23.2.windows-arm.msi -36b7228bae235eee6c8193f5a956e1a9a17874955affb86b3564709b0fab5874 go1.23.2.linux-mipsle.tar.gz -3bd1130a08195d23960b154d2e6eaa80ac7325ebd9d01d74c58b6d12580e6b12 go1.23.2.linux-mips.tar.gz -3bf66879b38a233c5cbb5d2eb982004117f05d6bf06279e886e087d7c504427d go1.23.2.openbsd-riscv64.tar.gz -3e80b943d70c7e1633822b42c1aa7234e61da14f13ff8efff7ee6e1347f37648 go1.23.2.netbsd-arm64.tar.gz -40c0b61971a1a74fd4566c536f682c9d4976fa71d40d9daabc875c06113d0fee go1.23.2.darwin-amd64.pkg -445c0ef19d8692283f4c3a92052cc0568f5a048f4e546105f58e991d4aea54f5 go1.23.2.darwin-amd64.tar.gz -542d3c1705f1c6a1c5a80d5dc62e2e45171af291e755d591c5e6531ef63b454e go1.23.2.linux-amd64.tar.gz -560aff7fe1eeadc32248db35ed5c0a81e190d171b6ecec404cf46d808c13e92f go1.23.2.aix-ppc64.tar.gz -5611cd648f5100b73a7d6fd85589a481af18fdbaf9c153a92de9a8e39a6e061f go1.23.2.darwin-arm64.pkg -695aac64532da8d9a243601ffa0411cd763be891fcf7fd2e857eea4ab10b8bcc go1.23.2.plan9-386.tar.gz -69b31edcd3d4f7d8bbf9aee2b25cafba30b444ef19bc7a033e15026f7d0cc5c2 go1.23.2.netbsd-arm.tar.gz -6ffa4ac1f4368a3121a032917577a4e0a3feaf696c3e98f213b74ac04c318bc4 go1.23.2.plan9-arm.tar.gz -72a6def70300cc804c70073d8b579603d9b39b39b02b3b5d340968d9e7e0e9d4 go1.23.2.windows-386.msi -791ca685ee5ca0f6fe849dc078145cb1323d0ea9dd308e9cca9ba2e7186dbb3d go1.23.2.linux-ppc64.tar.gz -86b5de91fdf7bd9b52c77c62f8762518cf3fc256fe912af9bbff1d073054aa5b go1.23.2.plan9-amd64.tar.gz -8734c7cd464a0620f6605bd3f9256bed062f262d0d58e4f45099c329a08ed966 go1.23.2.openbsd-amd64.tar.gz -980ceb889915695d94b166ca1300250dba76fa37a2d41eca2c5e7727dcb4fb7f go1.23.2.openbsd-arm.tar.gz -a0cf25f236a0fa0a465816fe7f5c930f3b0b90c5c247b09c43a6adeff654e6ae go1.23.2.linux-mips64.tar.gz -a13cc0d621af4f35afd90b886c60b1bf66f771939d226dc36fa61a337d90eb30 go1.23.2.openbsd-ppc64.tar.gz -b29ff163b34cb4943c521fcfc1d956eaa6286561089042051a3fab22e79e9283 go1.23.2.windows-arm.zip -bc28fe3002cd65cec65d0e4f6000584dacb8c71bfaff8801dfb532855ca42513 go1.23.2.windows-amd64.zip -c164ce7d894b10fd861d7d7b96f1dbea3f993663d9f0c30bc4f8ae3915db8b0c go1.23.2.linux-ppc64le.tar.gz -c4ae1087dce4daf45a837f5fca36ac0e29a02ada9addf857f1c426e60bce6f21 go1.23.2.netbsd-386.tar.gz -c80cbc5e66d6fb8b0c3300b0dda1fe925c429e199954d3327da2933d9870b041 go1.23.2.windows-amd64.msi -cb1ed4410f68d8be1156cee0a74fcfbdcd9bca377c83db3a9e1b07eebc6d71ef go1.23.2.linux-386.tar.gz -d1fde255843fec1f7f0611d468effd98e1f4309f589ac13037db07b032f9da35 go1.23.2.openbsd-386.tar.gz -d47e40366cd6c6b6ee14b811554cd7dde0351309f4a8a4569ec5ba2bd7689437 go1.23.2.illumos-amd64.tar.gz -d87031194fe3e01abdcaf3c7302148ade97a7add6eac3fec26765bcb3207b80f go1.23.2.darwin-arm64.tar.gz -de1f94d7dd3548ba3036de1ea97eb8243881c22a88fcc04cc08c704ded769e02 go1.23.2.linux-s390x.tar.gz -e3286bdde186077e65e961cbe18874d42a461e5b9c472c26572b8d4a98d15c40 go1.23.2.linux-armv6l.tar.gz -e4d9a1319dfdaa827407855e406c43e85c878a1f93f4f3984c85dce969c8bf70 go1.23.2.freebsd-386.tar.gz -ea8ab49c5c04c9f94a3f4894d1b030fbce8d10413905fa399f6c39c0a44d5556 go1.23.2.linux-riscv64.tar.gz -eaa3bc377badbdcae144633f8b29bf2680475b72dcd4c135343d3bdc0ba7671e go1.23.2.windows-386.zip -f11b9b4d4a0679909202fc5e88093d6ff720a8a417bfe6a34d502c3862367039 go1.23.2.freebsd-riscv64.tar.gz -f163b99b03e4bbc64cd30363f1694a08fcd44094415db1f092f13f9d1bb7c28e go1.23.2.dragonfly-amd64.tar.gz -f45af3e1434175ff85620a74c07fb41d6844655f1f2cd2389c5fca6de000f58c go1.23.2.freebsd-arm64.tar.gz -f626cdd92fc21a88b31c1251f419c17782933a42903db87a174ce74eeecc66a9 go1.23.2.linux-arm64.tar.gz -fa70d39ddeb6b55241a30b48d7af4e681c6a7d7104e8326c3bc1b12a75e091cc go1.23.2.solaris-amd64.tar.gz +8d6a77332487557c6afa2421131b50f83db4ae3c579c3bc72e670ee1f6968599 go1.23.3.src.tar.gz +bdbf2a243ed4a121c9988684e5b15989cb244c1ff9e41ca823d0187b5c859114 go1.23.3.aix-ppc64.tar.gz +b79c77bbdf61e6e486aa6bea9286f3f7969c28e2ff7686ce10c334f746bfb724 go1.23.3.darwin-amd64.pkg +c7e024d5c0bc81845070f23598caf02f05b8ae88fd4ad2cd3e236ddbea833ad2 go1.23.3.darwin-amd64.tar.gz +3e764df0db8f3c7470b9ff641954a380510a4822613c06bd5a195fd083f4731d go1.23.3.darwin-arm64.pkg +31e119fe9bde6e105407a32558d5b5fa6ca11e2bd17f8b7b2f8a06aba16a0632 go1.23.3.darwin-arm64.tar.gz +3872c9a98331050a242afe63fa6abc8fc313aca83dcaefda318e903309ac0c8d go1.23.3.dragonfly-amd64.tar.gz +69479fa016ec5b4605885643ce0c2dd5c583e02353978feb6de38c961863b9cc go1.23.3.freebsd-386.tar.gz +bf1de22a900646ef4f79480ed88337856d47089cc610f87e6fef46f6b8db0e1f go1.23.3.freebsd-amd64.tar.gz +e461f866479bc36bdd4cfec32bfecb1bb243152268a1b3223de109410dec3407 go1.23.3.freebsd-arm.tar.gz +24154b4018a45540aefeb6b5b9ffdcc8d9a8cdb78cd7fec262787b89fed19997 go1.23.3.freebsd-arm64.tar.gz +218f3f1532e61dd65c330c2a5fc85bec18cc3690489763e62ffa9bb9fc85a68e go1.23.3.freebsd-riscv64.tar.gz +24e3f34858b8687c31f5e5ab9e46d27fb613b0d50a94261c500cebb2d79c0672 go1.23.3.illumos-amd64.tar.gz +3d7b00191a43c50d28e0903a0c576104bc7e171a8670de419d41111c08dfa299 go1.23.3.linux-386.tar.gz +a0afb9744c00648bafb1b90b4aba5bdb86f424f02f9275399ce0c20b93a2c3a8 go1.23.3.linux-amd64.tar.gz +1f7cbd7f668ea32a107ecd41b6488aaee1f5d77a66efd885b175494439d4e1ce go1.23.3.linux-arm64.tar.gz +5f0332754beffc65af65a7b2da76e9dd997567d0d81b6f4f71d3588dc7b4cb00 go1.23.3.linux-armv6l.tar.gz +1d0161a8946c7d99f717bad23631738408511f9f87e78d852224a023d8882ad8 go1.23.3.linux-loong64.tar.gz +e924a7c9027f521f8a3563541ed0f89a4db3ef005b6b71263415b38e0b46e63a go1.23.3.linux-mips.tar.gz +4cdf8c38165627f032c2b17cdd95e4aafff40d75fc873824d4c94914284098ca go1.23.3.linux-mips64.tar.gz +5e49347e7325d2e268fb14040529b704e66eed77154cc73a919e9167d8527a2f go1.23.3.linux-mips64le.tar.gz +142eabc17cee99403e895383ed7a6b7b40e740e8c2f73b79352bb9d1242fbd98 go1.23.3.linux-mipsle.tar.gz +96ad61ba6b6cc0f5adfd75e65231c61e7db26d8236f01117023899528164d1b0 go1.23.3.linux-ppc64.tar.gz +e3b926c81e8099d3cee6e6e270b85b39c3bd44263f8d3df29aacb4d7e00507c8 go1.23.3.linux-ppc64le.tar.gz +324e03b6f59be841dfbaeabc466224b0f0905f5ad3a225b7c0703090e6c4b1a5 go1.23.3.linux-riscv64.tar.gz +6bd72fcef72b046b6282c2d1f2c38f31600e4fe9361fcd8341500c754fb09c38 go1.23.3.linux-s390x.tar.gz +5df382337fe2e4ea6adaafa823da5e083513a97534a38f89d691dd6f599084ca go1.23.3.netbsd-386.tar.gz +9ae7cb6095a3e91182ac03547167e230fddd4941ed02dbdb6af663b2a53d9db7 go1.23.3.netbsd-amd64.tar.gz +4a452c4134a9bea6213d8925d322f26b01c0eccda1330585bb2b241c76a0c3ea go1.23.3.netbsd-arm.tar.gz +8ff3b5184d840148dbca061c04dca35a7070dc894255d3b755066bd76a7094dc go1.23.3.netbsd-arm64.tar.gz +5b6940922e68ac1162a704a8b583fb4f039f955bfe97c35a56c40269cbcff9b1 go1.23.3.openbsd-386.tar.gz +6ae4aeb6a88f3754b10ecec90422a30fb8bf86c3187be2be9408d67a5a235ace go1.23.3.openbsd-amd64.tar.gz +e5eae226391b60c4d1ea1022663f55b225c6d7bab67f31fbafd5dd7a04684006 go1.23.3.openbsd-arm.tar.gz +e12b2c04535e0bf5561d54831122b410d708519c1ec2c56b0c2350b15243c331 go1.23.3.openbsd-arm64.tar.gz +599818e4062166d7a112f6f3fcca2dd4e2cdd3111fe48f9757bd8debf38c7f52 go1.23.3.openbsd-ppc64.tar.gz +9ca4db8cab2a07d561f5b2a9397793684ab3b22326add1fe8cda8a545a1693db go1.23.3.openbsd-riscv64.tar.gz +8fca1ec2aced936e0170605378ee7f0acb38f002490321f67fc83728ee281967 go1.23.3.plan9-386.tar.gz +22d663692224fc1933a97f61d9fe49815e3b9ef1c2be97046505683fdf2e23c7 go1.23.3.plan9-amd64.tar.gz +d0417a702d0e776d57e450fa2ce1ce7efa199a636644776862dbf946c409a462 go1.23.3.plan9-arm.tar.gz +b5d9db1c02e0ca266a142eb687bd7749890c30872b09a4a0ffcd491425039754 go1.23.3.solaris-amd64.tar.gz +14b7baf4af2046013b74dfac6e9a0a7403f15ee9940a16890bc028dfd32c49ac go1.23.3.windows-386.msi +23da9089ea6c5612d718f13c26e9bfc9aaaabe222838075346a8191d48f9dfe5 go1.23.3.windows-386.zip +614f0e3eed82245dfb4356d4e8d5b96abecca6a4c4f0168c0e389e4dd6284db8 go1.23.3.windows-amd64.msi +81968b563642096b8a7521171e2be6e77ff6f44032f7493b7bdec9d33f44f31d go1.23.3.windows-amd64.zip +c9951eecad732c59dfde6dc4803fa9253eb074663c61035c8d856f4d2eb146cb go1.23.3.windows-arm.msi +1a7db02be47deada42082d21d63eba0013f93375cfa0e7768962f1295a469022 go1.23.3.windows-arm.zip +a74e3e195219af4330b93c71cd4b736b709a5654a07cc37eebe181c4984afb82 go1.23.3.windows-arm64.msi +dbdfa868b1a3f8c62950373e4975d83f90dd8b869a3907319af8384919bcaffe go1.23.3.windows-arm64.zip # version:golangci 1.61.0 # https://github.com/golangci/golangci-lint/releases/