Add NewFloor40Floor helper method

Also added tracerr.Wrap(...) to missing places.
This commit is contained in:
arnaucube
2021-03-03 11:35:50 +01:00
parent e460a7e58b
commit dfd49164f4
9 changed files with 85 additions and 22 deletions

View File

@@ -32,6 +32,8 @@ var (
// ErrFloat40NotEnoughPrecission is used when the given *big.Int can
// not be represented as Float40 due not enough precission
ErrFloat40NotEnoughPrecission = errors.New("Float40 error, not enough precission")
thres = big.NewInt(0x08_00_00_00_00)
)
// Float40 represents a float in a 64 bit format
@@ -68,7 +70,7 @@ func (f40 Float40) BigInt() (*big.Int, error) {
var f40Uint64 uint64 = uint64(f40) & 0x00_00_00_FF_FF_FF_FF_FF
f40Bytes, err := f40.Bytes()
if err != nil {
return nil, err
return nil, tracerr.Wrap(err)
}
e := f40Bytes[0] & 0xF8 >> 3 // take first 5 bits
@@ -79,25 +81,48 @@ func (f40 Float40) BigInt() (*big.Int, error) {
return r, nil
}
// NewFloat40 encodes a *big.Int integer as a Float40, returning error in case
// of loss during the encoding.
func NewFloat40(f *big.Int) (Float40, error) {
// newFloat40ME takes a *big.Int integer and returns the m (mantissa) & e
// (exponent) from the Float40 representation
func newFloat40ME(f *big.Int) (*big.Int, *big.Int) {
m := f
e := big.NewInt(0)
zero := big.NewInt(0)
ten := big.NewInt(10)
thres := big.NewInt(0x08_00_00_00_00)
for new(big.Int).Mod(m, ten).Cmp(zero) == 0 && m.Cmp(thres) >= 0 {
m = new(big.Int).Div(m, ten)
e = new(big.Int).Add(e, big.NewInt(1))
}
return m, e
}
// NewFloat40 encodes a *big.Int integer as a Float40, returning error in case
// of loss during the encoding.
func NewFloat40(f *big.Int) (Float40, error) {
m, e := newFloat40ME(f)
if e.Int64() > 31 {
return 0, ErrFloat40E31
return 0, tracerr.Wrap(ErrFloat40E31)
}
if m.Cmp(thres) >= 0 {
return 0, ErrFloat40NotEnoughPrecission
return 0, tracerr.Wrap(ErrFloat40NotEnoughPrecission)
}
r := new(big.Int).Add(m,
new(big.Int).Mul(e, thres))
return Float40(r.Uint64()), nil
}
// NewFloat40Floor encodes a *big.Int integer as a Float40, rounding down in
// case of loss during the encoding. It returns an error in case that the number
// is too big (e>31). Warning: this method should not be used inside the
// hermez-node, it's a helper for external usage to generate valid Float40
// values.
func NewFloat40Floor(f *big.Int) (Float40, error) {
m, e := newFloat40ME(f)
if e.Int64() > 31 {
return 0, tracerr.Wrap(ErrFloat40E31)
}
r := new(big.Int).Add(m,
new(big.Int).Mul(e, thres))
return Float40(r.Uint64()), nil
}

View File

@@ -4,6 +4,7 @@ import (
"math/big"
"testing"
"github.com/hermeznetwork/tracerr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -55,7 +56,44 @@ func TestExpectError(t *testing.T) {
bi, ok := new(big.Int).SetString(test, 10)
require.True(t, ok)
_, err := NewFloat40(bi)
assert.Equal(t, testVector[test], err)
assert.Equal(t, testVector[test], tracerr.Unwrap(err))
}
}
func TestNewFloat40Floor(t *testing.T) {
testVector := map[string][]uint64{
// []int contains [Float40 value, Flot40 Floor value], when
// Float40 value is expected to be 0, is because is expected to
// be an error
"9922334455000000000000000000000000000000": {1040714485495, 1040714485495},
"9922334455000000000000000000000000000001": {0, 6846188881046405121},
"9922334454999999999999999999999999999999": {0, 6846188881046405119},
"42949672950000000000000000000000000000000": {1069446856703, 1069446856703},
"99223344556573838487575": {0, 16754928163869896727},
"992233445500000000000000000000000000000000": {0, 0},
"343597383670000000000000000000000000000000": {1099511627775, 1099511627775},
"343597383680000000000000000000000000000000": {0, 1099511627776},
"343597383690000000000000000000000000000000": {0, 1099511627777},
"343597383700000000000000000000000000000000": {0, 0},
}
for test := range testVector {
bi, ok := new(big.Int).SetString(test, 10)
require.True(t, ok)
f40, err := NewFloat40(bi)
if f40 == 0 {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testVector[test][0], uint64(f40))
f40, err = NewFloat40Floor(bi)
if f40 == 0 {
assert.Equal(t, ErrFloat40E31, tracerr.Unwrap(err))
} else {
assert.NoError(t, err)
}
assert.Equal(t, testVector[test][1], uint64(f40))
}
}

View File

@@ -259,7 +259,7 @@ func L1TxFromDataAvailability(b []byte, nLevels uint32) (*L1Tx, error) {
}
l1tx.ToIdx = toIdx
l1tx.EffectiveAmount, err = Float40FromBytes(amountBytes).BigInt()
return &l1tx, err
return &l1tx, tracerr.Wrap(err)
}
// BytesGeneric returns the generic representation of a L1Tx. This method is

View File

@@ -15,12 +15,12 @@ import (
)
const (
// TXIDPrefixL1UserTx is the prefix that determines that the TxID is
// for a L1UserTx
// TxIDPrefixL1UserTx is the prefix that determines that the TxID is for
// a L1UserTx
//nolinter:gomnd
TxIDPrefixL1UserTx = byte(0)
// TXIDPrefixL1CoordTx is the prefix that determines that the TxID is
// TxIDPrefixL1CoordTx is the prefix that determines that the TxID is
// for a L1CoordinatorTx
//nolinter:gomnd
TxIDPrefixL1CoordTx = byte(1)