Merge pull request #610 from hermeznetwork/feature/float40-floor Add NewFloor40Floor helper method
@@ -32,6 +32,8 @@ ErrFloat40E31 = errors.New("Float40 error, e > 31")
// 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 @@ // take the 5 used bytes (FF * 5)
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 @@ r := new(big.Int).Mul(big.NewInt(int64(m)), exp)
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 }
@@ -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 @@ for test := range testVector {
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)) } }
@@ -259,7 +259,7 @@ return nil, tracerr.Wrap(err)
} 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
@@ -15,12 +15,12 @@ "github.com/iden3/go-iden3-crypto/babyjub"
) 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)
@@ -210,7 +210,7 @@ if ctx.Err() != nil {
return nil, ctx.Err() } else if err != nil { log.Errorw("proversPool.Get", "err", err) - return nil, err + return nil, tracerr.Wrap(err) } defer func() { // If we encounter any error (notice that this function returns@@ -240,7 +240,7 @@ // no log
} else { log.Errorw("forgeBatch", "err", err) } - return nil, err + return nil, tracerr.Wrap(err) } // 3. Send the ZKInputs to the proof server@@ -249,7 +249,7 @@ if err := p.sendServerProof(ctx, batchInfo); ctx.Err() != nil {
return nil, ctx.Err() } else if err != nil { log.Errorw("sendServerProof", "err", err) - return nil, err + return nil, tracerr.Wrap(err) } return batchInfo, nil }@@ -427,7 +427,7 @@ }
// If we haven't reached the ForgeDelay, skip forging the batch if slotCommitted && now.Sub(p.lastForgeTime) < p.cfg.ForgeDelay { - return nil, errForgeBeforeDelay + return nil, tracerr.Wrap(errForgeBeforeDelay) } // 1. Decide if we forge L2Tx or L1+L2Tx@@ -485,7 +485,7 @@ if noTxs {
if err := p.txSelector.Reset(batchInfo.BatchNum-1, false); err != nil { return nil, tracerr.Wrap(err) } - return nil, errForgeNoTxsBeforeDelay + return nil, tracerr.Wrap(errForgeNoTxsBeforeDelay) } }
@@ -67,7 +67,7 @@ return nil, tracerr.Wrap(err)
} accNonce, err := ethClient.EthNonceAt(ctx, *address, nil) if err != nil { - return nil, err + return nil, tracerr.Wrap(err) } log.Infow("TxManager started", "nonce", accNonce) return &TxManager{
@@ -179,7 +179,7 @@ batch.num_accounts, batch.last_idx, batch.exit_root, batch.forge_l1_txs_num,
batch.slot_num, batch.total_fees_usd FROM batch WHERE batch_num = $1;`, batchNum, ) - return &batch, err + return &batch, tracerr.Wrap(err) } // GetAllBatches retrieve all batches from the DB@@ -235,7 +235,7 @@ batch.fees_collected, batch.fee_idxs_coordinator, batch.state_root,
batch.num_accounts, batch.last_idx, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num, batch.total_fees_usd FROM batch ORDER BY batch_num DESC LIMIT 1;`, ) - return &batch, err + return &batch, tracerr.Wrap(err) } // GetLastL1BatchBlockNum returns the blockNum of the latest forged l1Batch
@@ -458,7 +458,7 @@ source := path.Join(k.cfg.Path, fmt.Sprintf("%s%d", PathBatchNum, batchNum))
if _, err := os.Stat(source); os.IsNotExist(err) { return false, nil } else if err != nil { - return false, err + return false, tracerr.Wrap(err) } return true, nil }
@@ -268,7 +268,7 @@ }
if err := auth.Sign(func(msg []byte) ([]byte, error) { return keyStore.SignHash(feeAccount, msg) }, chainIDU16, cfg.SmartContracts.Rollup); err != nil { - return nil, err + return nil, tracerr.Wrap(err) } coordAccount := &txselector.CoordAccount{ Addr: cfg.Coordinator.FeeAccount.Address,