From 1cad0657cb6296c372d73fdc46a1d645de9851f6 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Mon, 23 Oct 2023 21:06:51 +0000 Subject: [PATCH 1/4] add tests for transformAmount() --- contract/vm_callback_test.go | 139 +++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 contract/vm_callback_test.go diff --git a/contract/vm_callback_test.go b/contract/vm_callback_test.go new file mode 100644 index 000000000..23428e015 --- /dev/null +++ b/contract/vm_callback_test.go @@ -0,0 +1,139 @@ +package contract + +import ( + "errors" + "math/big" + "testing" + + "github.com/aergoio/aergo/v2/types" + "github.com/stretchr/testify/assert" +) + +func bigIntFromString(str string) *big.Int { + bigInt, success := new(big.Int).SetString(str, 10) + if !success { + panic("bigIntFromString: invalid number: " + str) + } + return bigInt +} + +func TestTransformAmount(t *testing.T) { + // Define the test cases + tests := []struct { + amountStr string + expectedAmount *big.Int + expectedError error + }{ + // Empty Input String + {"", big.NewInt(0), nil}, + // Valid Amount without Unit + {"1", big.NewInt(1), nil}, + {"10", big.NewInt(10), nil}, + {"123", big.NewInt(123), nil}, + {"123000000", big.NewInt(123000000), nil}, + // Valid Amount with Unit + {"100aergo", types.NewAmount(100, types.Aergo), nil}, + {"100 aergo", types.NewAmount(100, types.Aergo), nil}, + {"123gaer", types.NewAmount(123, types.Gaer), nil}, + {"123 gaer", types.NewAmount(123, types.Gaer), nil}, + {"123aer", types.NewAmount(123, types.Aer), nil}, + {"123 aer", types.NewAmount(123, types.Aer), nil}, + // Multipart Amount + {"100aergo 200gaer", bigIntFromString("100000000200000000000"), nil}, + {"100 aergo 123 gaer", bigIntFromString("100000000123000000000"), nil}, + {"123aergo 456aer", bigIntFromString("123000000000000000456"), nil}, + {"123 aergo 456 aer", bigIntFromString("123000000000000000456"), nil}, + {"123aergo 456gaer 789aer", bigIntFromString("123000000456000000789"), nil}, + {"123 aergo 456 gaer 789 aer", bigIntFromString("123000000456000000789"), nil}, + // Invalid Order + {"789aer 456gaer 123aergo", bigIntFromString("123000000456000000789"), nil}, + {"789 aer 456 gaer 123 aergo", bigIntFromString("123000000456000000789"), nil}, + {"789aer 123aergo 456gaer", bigIntFromString("123000000456000000789"), nil}, + {"789 aer 123 aergo 456 gaer", bigIntFromString("123000000456000000789"), nil}, + {"456gaer 789aer 123aergo", bigIntFromString("123000000456000000789"), nil}, + {"123aergo 789aer 456gaer", bigIntFromString("123000000456000000789"), nil}, + // Repeated Units + {"123aergo 456aergo", bigIntFromString("579000000000000000000"), nil}, + {"123gaer 456gaer", bigIntFromString("579000000000"), nil}, + {"123aer 456aer", bigIntFromString("579"), nil}, + {"123 aergo 456 aergo", bigIntFromString("579000000000000000000"), nil}, + {"123 gaer 456 gaer", bigIntFromString("579000000000"), nil}, + {"123 aer 456 aer", bigIntFromString("579"), nil}, + {"123aergo 456aergo 789aer", bigIntFromString("579000000000000000789"), nil}, + {"123aergo 456aergo 789gaer", bigIntFromString("579000000789000000000"), nil}, + {"123aergo 456gaer 789gaer", bigIntFromString("123000001245000000000"), nil}, + {"123aergo 456aer 789aer", bigIntFromString("123000000000000001245"), nil}, + {"123 aergo 456 aergo 789 aer", bigIntFromString("579000000000000000789"), nil}, + {"123 aergo 456 aergo 789 gaer", bigIntFromString("579000000789000000000"), nil}, + {"123 aergo 456 gaer 789 gaer", bigIntFromString("123000001245000000000"), nil}, + {"123 aergo 456 aer 789 aer", bigIntFromString("123000000000000001245"), nil}, + // Invalid Amount String + {"notanumber", nil, errors.New("converting error for Integer: notanumber")}, + {"e123", nil, errors.New("converting error for Integer: e123")}, + {"123e", nil, errors.New("converting error for Integer: 123e")}, + {"123 456", nil, errors.New("converting error for Integer: 123 456")}, + // Negative Amount + {"-100", nil, errors.New("negative amount not allowed")}, + {"-100aergo", nil, errors.New("negative amount not allowed")}, + {"-100 aergo", nil, errors.New("negative amount not allowed")}, + {"-100 aergo", nil, errors.New("negative amount not allowed")}, + {"-100aer", nil, errors.New("negative amount not allowed")}, + {"-100 aer", nil, errors.New("negative amount not allowed")}, + {"-100 aer", nil, errors.New("negative amount not allowed")}, + // Large Number + {"99999999999999999999999999", bigIntFromString("99999999999999999999999999"), nil}, + // Zero Value + {"0", big.NewInt(0), nil}, + {"0aergo", big.NewInt(0), nil}, + {"0 aergo", big.NewInt(0), nil}, + {"0gaer", big.NewInt(0), nil}, + {"0 gaer", big.NewInt(0), nil}, + {"0aer", big.NewInt(0), nil}, + {"0 aer", big.NewInt(0), nil}, + // Only Unit + {"aergo", nil, errors.New("converting error for BigNum: aergo")}, + {"gaer", nil, errors.New("converting error for BigNum: gaer")}, + {"aer", nil, errors.New("converting error for BigNum: aer")}, + // Invalid Content + {"100 invalid 200", nil, errors.New("converting error for Integer: 100 invalid 200")}, + {"invalid 200", nil, errors.New("converting error for Integer: invalid 200")}, + {"100 invalid", nil, errors.New("converting error for Integer: 100 invalid")}, + // Non-Integer Values + {"123.456", nil, errors.New("converting error for Integer: 123.456")}, + {"123.456 aergo", nil, errors.New("converting error for BigNum: 123.456 aergo")}, + {".1", nil, errors.New("converting error for Integer: .1")}, + {".1aergo", nil, errors.New("converting error for BigNum: .1aergo")}, + {".1 aergo", nil, errors.New("converting error for BigNum: .1 aergo")}, + {".10", nil, errors.New("converting error for Integer: .10")}, + // Exponents + {"1e+18", nil, errors.New("converting error for Integer: 1e+18")}, + {"2e18", nil, errors.New("converting error for Integer: 2e18")}, + {"3e08", nil, errors.New("converting error for Integer: 3e08")}, + {"1e+18 aer", nil, errors.New("converting error for BigNum: 1e+18 aer")}, + {"2e+18 aer", nil, errors.New("converting error for BigNum: 2e+18 aer")}, + {"3e18 aer", nil, errors.New("converting error for BigNum: 3e18 aer")}, + {"1e+18aer", nil, errors.New("converting error for BigNum: 1e+18aer")}, + {"2e+18aer", nil, errors.New("converting error for BigNum: 2e+18aer")}, + {"3e18aer", nil, errors.New("converting error for BigNum: 3e18aer")}, + {"3e+5 aergo", nil, errors.New("converting error for BigNum: 3e+5 aergo")}, + {"3e5 aergo", nil, errors.New("converting error for BigNum: 3e5 aergo")}, + {"3e05 aergo", nil, errors.New("converting error for BigNum: 3e05 aergo")}, + {"5e+3aergo", nil, errors.New("converting error for BigNum: 5e+3aergo")}, + } + + for _, tt := range tests { + result, err := transformAmount(tt.amountStr) + + if tt.expectedError != nil { + if assert.Error(t, err, "Expected error: %s", tt.expectedError.Error()) { + assert.Equal(t, tt.expectedError.Error(), err.Error()) + } + } else { + assert.NoError(t, err, "Expected no error, but got: %v", err) + } + + if tt.expectedAmount != nil { + assert.Equal(t, tt.expectedAmount, result) + } + } +} From 2d68abfad35a12d10b8f43513ec7b704b5fe5628 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Mon, 23 Oct 2023 23:10:51 +0000 Subject: [PATCH 2/4] fix test error logs --- contract/vm_callback_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contract/vm_callback_test.go b/contract/vm_callback_test.go index 23428e015..036872f7d 100644 --- a/contract/vm_callback_test.go +++ b/contract/vm_callback_test.go @@ -129,11 +129,10 @@ func TestTransformAmount(t *testing.T) { assert.Equal(t, tt.expectedError.Error(), err.Error()) } } else { - assert.NoError(t, err, "Expected no error, but got: %v", err) - } - - if tt.expectedAmount != nil { - assert.Equal(t, tt.expectedAmount, result) + if assert.NoError(t, err) && tt.expectedAmount != nil { + assert.Equal(t, tt.expectedAmount, result) + } } } + } From dd63546a7a60f1af0b34c7ba0da7f84bc2b7acd9 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Mon, 23 Oct 2023 23:57:57 +0000 Subject: [PATCH 3/4] replace the transformAmount function --- contract/vm_callback.go | 112 +++++++++++++++++++---------------- contract/vm_callback_test.go | 40 ++++++------- 2 files changed, 80 insertions(+), 72 deletions(-) diff --git a/contract/vm_callback.go b/contract/vm_callback.go index 5997b0840..6ba8640bb 100644 --- a/contract/vm_callback.go +++ b/contract/vm_callback.go @@ -30,9 +30,7 @@ import ( "errors" "fmt" "github.com/aergoio/aergo-lib/log" - "index/suffixarray" "math/big" - "regexp" "strconv" "strings" "unsafe" @@ -1032,73 +1030,83 @@ func luaCryptoKeccak256(data unsafe.Pointer, dataLen C.int) (unsafe.Pointer, int } } +// transformAmount processes the input string to calculate the total amount, +// taking into account the different units ("aergo", "gaer", "aer") func transformAmount(amountStr string) (*big.Int, error) { - var ret *big.Int - var prev int if len(amountStr) == 0 { return zeroBig, nil } - index := suffixarray.New([]byte(amountStr)) - r := regexp.MustCompile("(?i)aergo|gaer|aer") - res := index.FindAllIndex(r, -1) - for _, pair := range res { - amountBig, _ := new(big.Int).SetString(strings.TrimSpace(amountStr[prev:pair[0]]), 10) - if amountBig == nil { - return nil, errors.New("converting error for BigNum: " + amountStr[prev:]) - } - cmp := amountBig.Cmp(zeroBig) - if cmp < 0 { - return nil, errors.New("negative amount not allowed") - } else if cmp == 0 { - prev = pair[1] - continue - } - switch pair[1] - pair[0] { - case 3: - case 4: - amountBig = new(big.Int).Mul(amountBig, mulGaer) - case 5: - amountBig = new(big.Int).Mul(amountBig, mulAergo) - } - if ret != nil { - ret = new(big.Int).Add(ret, amountBig) - } else { - ret = amountBig - } - prev = pair[1] - } + totalAmount := new(big.Int) + remainingStr := amountStr + + // Define the units and corresponding multipliers + for _, data := range []struct { + unit string + multiplier *big.Int + }{ + {"aergo", mulAergo}, + {"gaer", mulGaer}, + {"aer", zeroBig}, + } { + idx := strings.Index(strings.ToLower(remainingStr), data.unit) + if idx != -1 { + // Extract the part before the unit + subStr := remainingStr[:idx] + + // Parse and convert the amount + partialAmount, err := parseAndConvert(subStr, data.unit, data.multiplier, amountStr) + if err != nil { + return nil, err + } - if prev >= len(amountStr) { - if ret != nil { - return ret, nil - } else { - return zeroBig, nil + // Add to the total amount + totalAmount.Add(totalAmount, partialAmount) + + // Adjust the remaining string to process + remainingStr = remainingStr[idx+len(data.unit):] } } - num := strings.TrimSpace(amountStr[prev:]) - if len(num) == 0 { - if ret != nil { - return ret, nil - } else { - return zeroBig, nil + + // Process the rest of the string, if there is some + if len(remainingStr) > 0 { + partialAmount, err := parseAndConvert(remainingStr, "", zeroBig, amountStr) + if err != nil { + return nil, err } + + // Add to the total amount + totalAmount.Add(totalAmount, partialAmount) } - amountBig, _ := new(big.Int).SetString(num, 10) + return totalAmount, nil +} + +// parseAndConvert is a helper function to parse the substring as a big integer +// and apply the necessary multiplier based on the unit. +func parseAndConvert(subStr, unit string, mulUnit *big.Int, amountStr string) (*big.Int, error) { + trimmedStr := strings.TrimSpace(subStr) - if amountBig == nil { - return nil, errors.New("converting error for Integer: " + amountStr[prev:]) + // Convert the trimmed string to a big integer + amountBig, valid := new(big.Int).SetString(trimmedStr, 10) + if !valid { + // Emits a backwards compatible error message + // the same as: dataType := len(unit) > 0 ? "BigNum" : "Integer" + dataType := map[bool]string{true: "BigNum", false: "Integer"}[len(unit) > 0] + return nil, errors.New("converting error for " + dataType + ": " + strings.TrimSpace(amountStr)) } + + // Check for negative amounts if amountBig.Cmp(zeroBig) < 0 { return nil, errors.New("negative amount not allowed") } - if ret != nil { - ret = new(big.Int).Add(ret, amountBig) - } else { - ret = amountBig + + // Apply multiplier based on unit + if mulUnit != zeroBig { + amountBig.Mul(amountBig, mulUnit) } - return ret, nil + + return amountBig, nil } //export luaDeployContract diff --git a/contract/vm_callback_test.go b/contract/vm_callback_test.go index 036872f7d..74ce946ff 100644 --- a/contract/vm_callback_test.go +++ b/contract/vm_callback_test.go @@ -46,27 +46,27 @@ func TestTransformAmount(t *testing.T) { {"123aergo 456gaer 789aer", bigIntFromString("123000000456000000789"), nil}, {"123 aergo 456 gaer 789 aer", bigIntFromString("123000000456000000789"), nil}, // Invalid Order - {"789aer 456gaer 123aergo", bigIntFromString("123000000456000000789"), nil}, - {"789 aer 456 gaer 123 aergo", bigIntFromString("123000000456000000789"), nil}, - {"789aer 123aergo 456gaer", bigIntFromString("123000000456000000789"), nil}, - {"789 aer 123 aergo 456 gaer", bigIntFromString("123000000456000000789"), nil}, - {"456gaer 789aer 123aergo", bigIntFromString("123000000456000000789"), nil}, - {"123aergo 789aer 456gaer", bigIntFromString("123000000456000000789"), nil}, + {"789aer 456gaer 123aergo", nil, errors.New("converting error for BigNum: 789aer 456gaer 123aergo")}, + {"789 aer 456 gaer 123 aergo", nil, errors.New("converting error for BigNum: 789 aer 456 gaer 123 aergo")}, + {"789aer 123aergo 456gaer", nil, errors.New("converting error for BigNum: 789aer 123aergo 456gaer")}, + {"789 aer 123 aergo 456 gaer", nil, errors.New("converting error for BigNum: 789 aer 123 aergo 456 gaer")}, + {"456gaer 789aer 123aergo", nil, errors.New("converting error for BigNum: 456gaer 789aer 123aergo")}, + {"123aergo 789aer 456gaer", nil, errors.New("converting error for BigNum: 123aergo 789aer 456gaer")}, // Repeated Units - {"123aergo 456aergo", bigIntFromString("579000000000000000000"), nil}, - {"123gaer 456gaer", bigIntFromString("579000000000"), nil}, - {"123aer 456aer", bigIntFromString("579"), nil}, - {"123 aergo 456 aergo", bigIntFromString("579000000000000000000"), nil}, - {"123 gaer 456 gaer", bigIntFromString("579000000000"), nil}, - {"123 aer 456 aer", bigIntFromString("579"), nil}, - {"123aergo 456aergo 789aer", bigIntFromString("579000000000000000789"), nil}, - {"123aergo 456aergo 789gaer", bigIntFromString("579000000789000000000"), nil}, - {"123aergo 456gaer 789gaer", bigIntFromString("123000001245000000000"), nil}, - {"123aergo 456aer 789aer", bigIntFromString("123000000000000001245"), nil}, - {"123 aergo 456 aergo 789 aer", bigIntFromString("579000000000000000789"), nil}, - {"123 aergo 456 aergo 789 gaer", bigIntFromString("579000000789000000000"), nil}, - {"123 aergo 456 gaer 789 gaer", bigIntFromString("123000001245000000000"), nil}, - {"123 aergo 456 aer 789 aer", bigIntFromString("123000000000000001245"), nil}, + {"123aergo 456aergo", nil, errors.New("converting error for Integer: 123aergo 456aergo")}, + {"123gaer 456gaer", nil, errors.New("converting error for BigNum: 123gaer 456gaer")}, + {"123aer 456aer", nil, errors.New("converting error for Integer: 123aer 456aer")}, + {"123 aergo 456 aergo", nil, errors.New("converting error for Integer: 123 aergo 456 aergo")}, + {"123 gaer 456 gaer", nil, errors.New("converting error for BigNum: 123 gaer 456 gaer")}, + {"123 aer 456 aer", nil, errors.New("converting error for Integer: 123 aer 456 aer")}, + {"123aergo 456aergo 789aer", nil, errors.New("converting error for Integer: 123aergo 456aergo 789aer")}, + {"123aergo 456aergo 789gaer", nil, errors.New("converting error for BigNum: 123aergo 456aergo 789gaer")}, + {"123aergo 456gaer 789gaer", nil, errors.New("converting error for BigNum: 123aergo 456gaer 789gaer")}, + {"123aergo 456aer 789aer", nil, errors.New("converting error for Integer: 123aergo 456aer 789aer")}, + {"123 aergo 456 aergo 789 aer", nil, errors.New("converting error for Integer: 123 aergo 456 aergo 789 aer")}, + {"123 aergo 456 aergo 789 gaer", nil, errors.New("converting error for BigNum: 123 aergo 456 aergo 789 gaer")}, + {"123 aergo 456 gaer 789 gaer", nil, errors.New("converting error for BigNum: 123 aergo 456 gaer 789 gaer")}, + {"123 aergo 456 aer 789 aer", nil, errors.New("converting error for Integer: 123 aergo 456 aer 789 aer")}, // Invalid Amount String {"notanumber", nil, errors.New("converting error for Integer: notanumber")}, {"e123", nil, errors.New("converting error for Integer: e123")}, From 9ceb03fd690906339de8112cfe9cf2726e2f807c Mon Sep 17 00:00:00 2001 From: kch Date: Wed, 25 Oct 2023 07:20:30 +0000 Subject: [PATCH 4/4] apply go fmt --- contract/system/vote.go | 2 +- contract/vm_callback.go | 2 +- contract/vm_callback_test.go | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contract/system/vote.go b/contract/system/vote.go index 1ea2b29e1..cf5f9e083 100644 --- a/contract/system/vote.go +++ b/contract/system/vote.go @@ -26,7 +26,7 @@ const ( ) var ( - votingCatalog []types.VotingIssue + votingCatalog []types.VotingIssue lastBpCount int defaultVoteKey = []byte(types.OpvoteBP.ID()) ) diff --git a/contract/vm_callback.go b/contract/vm_callback.go index a9f848cdf..5cf046388 100644 --- a/contract/vm_callback.go +++ b/contract/vm_callback.go @@ -1042,7 +1042,7 @@ func transformAmount(amountStr string) (*big.Int, error) { // Define the units and corresponding multipliers for _, data := range []struct { - unit string + unit string multiplier *big.Int }{ {"aergo", mulAergo}, diff --git a/contract/vm_callback_test.go b/contract/vm_callback_test.go index 74ce946ff..5f5b9d84f 100644 --- a/contract/vm_callback_test.go +++ b/contract/vm_callback_test.go @@ -10,19 +10,19 @@ import ( ) func bigIntFromString(str string) *big.Int { - bigInt, success := new(big.Int).SetString(str, 10) - if !success { - panic("bigIntFromString: invalid number: " + str) - } - return bigInt + bigInt, success := new(big.Int).SetString(str, 10) + if !success { + panic("bigIntFromString: invalid number: " + str) + } + return bigInt } func TestTransformAmount(t *testing.T) { // Define the test cases tests := []struct { - amountStr string - expectedAmount *big.Int - expectedError error + amountStr string + expectedAmount *big.Int + expectedError error }{ // Empty Input String {"", big.NewInt(0), nil},