-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
deploy: Fix NEO transfer deadlock caused by rounding integer division
Previously, auto-deploy/update procedure could stuck when committee multi-sig account had less than N amount of NEO, where N is a number of the NeoFS Alphabet accounts in the deployed/update NeoFS network. This was caused by rounding integer division of fund amounts: zero funds were transferred due to which the balance did not change and each iteration did not change the network state. Fix zero transfers and also distribute remainder as evenly as possible to decrease total number of transactions. Fixes #2681. Signed-off-by: Leonard Lyubich <leonard@morphbits.io>
- Loading branch information
1 parent
005584e
commit 7651077
Showing
2 changed files
with
142 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package deploy | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestDivideFundsEvenly(t *testing.T) { | ||
t.Run("zero", func(t *testing.T) { | ||
var vals []*big.Int | ||
|
||
divideFundsEvenly(big.NewInt(0), 5, func(ind int, amount *big.Int) { | ||
vals = append(vals, amount) | ||
}) | ||
require.Empty(t, vals) | ||
}) | ||
|
||
t.Run("less than N", func(t *testing.T) { | ||
var vals []*big.Int | ||
|
||
divideFundsEvenly(big.NewInt(4), 5, func(ind int, amount *big.Int) { | ||
vals = append(vals, amount) | ||
}) | ||
require.Len(t, vals, 4) | ||
for i := range vals { | ||
require.Equal(t, big.NewInt(1), vals[i]) | ||
} | ||
}) | ||
|
||
t.Run("multiple", func(t *testing.T) { | ||
var vals []*big.Int | ||
|
||
divideFundsEvenly(big.NewInt(15), 3, func(ind int, amount *big.Int) { | ||
vals = append(vals, amount) | ||
}) | ||
require.Len(t, vals, 3) | ||
for i := range vals { | ||
require.Equal(t, big.NewInt(5), vals[i]) | ||
} | ||
}) | ||
|
||
t.Run("with remainder", func(t *testing.T) { | ||
var vals []*big.Int | ||
|
||
divideFundsEvenly(big.NewInt(16), 3, func(ind int, amount *big.Int) { | ||
vals = append(vals, amount) | ||
}) | ||
require.Len(t, vals, 3) | ||
require.Equal(t, big.NewInt(6), vals[0]) | ||
require.Equal(t, big.NewInt(5), vals[1]) | ||
require.Equal(t, big.NewInt(5), vals[2]) | ||
}) | ||
|
||
t.Run("bigger than uint64", func(t *testing.T) { | ||
bigU64 := new(big.Int).SetUint64(math.MaxUint64) | ||
fullAmount := new(big.Int).Mul(bigU64, big.NewInt(3)) | ||
|
||
var vals []*big.Int | ||
|
||
divideFundsEvenly(fullAmount, 3, func(ind int, amount *big.Int) { | ||
vals = append(vals, amount) | ||
}) | ||
require.Len(t, vals, 3) | ||
for i := range vals { | ||
require.Equal(t, bigU64, vals[i]) | ||
} | ||
}) | ||
} | ||
|
||
func BenchmarkDivideFundsEvenly(b *testing.B) { | ||
for _, tc := range []struct { | ||
n int | ||
a *big.Int | ||
}{ | ||
{ | ||
n: 7, | ||
a: big.NewInt(705), | ||
}, | ||
{ | ||
n: 100, | ||
a: new(big.Int).Mul(new(big.Int).SetUint64(math.MaxUint64), big.NewInt(255)), | ||
}, | ||
} { | ||
b.Run(fmt.Sprintf("N=%d,amount=%s", tc.n, tc.a), func(b *testing.B) { | ||
b.ReportAllocs() | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
divideFundsEvenly(tc.a, tc.n, func(ind int, amount *big.Int) {}) | ||
} | ||
}) | ||
} | ||
} |