Skip to content

Commit

Permalink
double check the use of NewQuantityFromUInt64 #218 (#381)
Browse files Browse the repository at this point in the history
Signed-off-by: Angelo De Caro <[email protected]>
  • Loading branch information
adecaro authored Sep 19, 2022
1 parent 9211221 commit 2a4e2d7
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 30 deletions.
16 changes: 8 additions & 8 deletions integration/token/fungible/views/accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ func (a *AcceptCashView) Call(context view.Context) (interface{}, error) {
id, err := ttx.RespondRequestRecipientIdentityUsingWallet(context, "")
assert.NoError(err, "failed to respond to identity request")

// At some point, the recipient receives the token transaction that in the mean time has been assembled
// At some point, the recipient receives the token transaction that in the meantime has been assembled
tx, err := ttx.ReceiveTransaction(context)
assert.NoError(err, "failed to receive tokens")

// The recipient can perform any check on the transaction as required by the business process
// In particular, here, the recipient checks that the transaction contains at least one output, and
// that there is at least one output that names the recipient. (The recipient is receiving something.
// that there is at least one output that names the recipient.(The recipient is receiving something).
outputs, err := tx.Outputs()
assert.NoError(err, "failed getting outputs")
assert.True(outputs.Count() > 0)
Expand All @@ -39,13 +39,13 @@ func (a *AcceptCashView) Call(context view.Context) (interface{}, error) {
// The recipient here is checking that, for each type of token she is receiving,
// she does not hold already more than 3000 units of that type.
// Just a fancy query to show the capabilities of the services we are using.
precision := tx.TokenService().PublicParametersManager().Precision()
for _, output := range outputs.ByRecipient(id).Outputs() {
unspentTokens, err := ttx.MyWallet(context).ListUnspentTokens(ttx.WithType(output.Type))
assert.NoError(err, "failed retrieving the unspent tokens for type [%s]", output.Type)
assert.True(
unspentTokens.Sum(tx.TokenService().PublicParametersManager().Precision()).Cmp(token2.NewQuantityFromUInt64(3000)) <= 0,
"cannot have more than 3000 unspent quantity for type [%s]", output.Type,
)
upperBound, err := token2.UInt64ToQuantity(3000, precision)
assert.NoError(err, "failed to convert to quantity")
assert.True(unspentTokens.Sum(precision).Cmp(upperBound) <= 0, "cannot have more than 3000 unspent quantity for type [%s]", output.Type)
}

// If everything is fine, the recipient accepts and sends back her signature.
Expand Down Expand Up @@ -89,13 +89,13 @@ func (a *AcceptPreparedCashView) Call(context view.Context) (interface{}, error)
id, err := ttx.RespondRequestRecipientIdentityUsingWallet(context, "")
assert.NoError(err, "failed to respond to identity request")

// At some point, the recipient receives the token transaction that in the mean time has been assembled
// At some point, the recipient receives the token transaction that in the meantime has been assembled
tx, err := ttx.ReceiveTransaction(context)
assert.NoError(err, "failed to receive tokens")

// The recipient can perform any check on the transaction as required by the business process
// In particular, here, the recipient checks that the transaction contains at least one output, and
// that there is at least one output that names the recipient. (The recipient is receiving something.
// that there is at least one output that names the recipient (The recipient is receiving something).
outputs, err := tx.Outputs()
assert.NoError(err, "failed getting outputs")
assert.True(outputs.Count() > 0)
Expand Down
6 changes: 5 additions & 1 deletion integration/token/fungible/views/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ func (p *IssueCashView) Call(context view.Context) (interface{}, error) {
fmt.Printf("History [%s,%s]<[241]?\n", history.Sum(precision).ToBigInt().Text(10), p.TokenType)

// Fail if the sum of the issued tokens and the current quest is larger than 241
assert.True(history.Sum(precision).Add(token2.NewQuantityFromUInt64(p.Quantity)).Cmp(token2.NewQuantityFromUInt64(241)) <= 0)
q, err := token2.UInt64ToQuantity(p.Quantity, precision)
assert.NoError(err, "failed to covert to quantity")
upperBound, err := token2.UInt64ToQuantity(241, precision)
assert.NoError(err, "failed to covert to quantity")
assert.True(history.Sum(precision).Add(q).Cmp(upperBound) <= 0)
}

// At this point, the issuer is ready to prepare the token transaction.
Expand Down
9 changes: 6 additions & 3 deletions integration/token/fungible/views/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ func (t *TransferWithSelectorView) Call(context view.Context) (interface{}, erro
assert.NotNil(senderWallet, "sender wallet [%s] not found", t.Wallet)

// If no specific tokens are requested, then a custom token selection process start
precision := token2.GetManagementService(context).PublicParametersManager().Precision()
amount, err := token.UInt64ToQuantity(t.Amount, precision)
assert.NoError(err, "failed to convert to quantity")

if len(t.TokenIDs) == 0 {
// The sender uses the default token selector each transaction comes equipped with
selector, err := tx.Selector()
Expand All @@ -171,7 +175,7 @@ func (t *TransferWithSelectorView) Call(context view.Context) (interface{}, erro
// Select the request amount of tokens of the given type
ids, sum, err = selector.Select(
ttx.GetWallet(context, t.Wallet),
token.NewQuantityFromUInt64(t.Amount).Decimal(),
amount.Decimal(),
t.Type,
)
// If an error occurs and retry has been asked, then wait first a bit
Expand Down Expand Up @@ -207,7 +211,6 @@ func (t *TransferWithSelectorView) Call(context view.Context) (interface{}, erro
assert.NoError(err, "failed getting tokens from ids")

// Then, the sender double check that what returned by the selector is correct
precision := tx.TokenService().PublicParametersManager().Precision()
recomputedSum := token.NewZeroQuantity(precision)
for _, tok := range tokens {
// Is the token of the right type?
Expand All @@ -220,7 +223,7 @@ func (t *TransferWithSelectorView) Call(context view.Context) (interface{}, erro
// Is the recomputed sum correct?
assert.True(sum.Cmp(recomputedSum) == 0, "sums do not match")
// Is the amount selected equal or larger than what requested?
assert.False(sum.Cmp(token.NewQuantityFromUInt64(t.Amount)) < 0, "if this point is reached, funds are sufficients")
assert.False(sum.Cmp(amount) < 0, "if this point is reached, funds are sufficient")

t.TokenIDs = ids
}
Expand Down
8 changes: 4 additions & 4 deletions samples/fungible/views/accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ func (a *AcceptCashView) Call(context view.Context) (interface{}, error) {
// The recipient here is checking that, for each type of token she is receiving,
// she does not hold already more than 3000 units of that type.
// Just a fancy query to show the capabilities of the services we are using.
precision := tx.TokenService().PublicParametersManager().Precision()
for _, output := range outputs.ByRecipient(id).Outputs() {
unspentTokens, err := ttx.MyWallet(context).ListUnspentTokens(ttx.WithType(output.Type))
assert.NoError(err, "failed retrieving the unspent tokens for type [%s]", output.Type)
assert.True(
unspentTokens.Sum(tx.TokenService().PublicParametersManager().Precision()).Cmp(token2.NewQuantityFromUInt64(3000)) <= 0,
"cannot have more than 3000 unspent quantity for type [%s]", output.Type,
)
upperLimit, err := token2.UInt64ToQuantity(3000, precision)
assert.NoError(err, "failed to convert to quantity")
assert.True(unspentTokens.Sum(precision).Cmp(upperLimit) <= 0, "cannot have more than 3000 unspent quantity for type [%s]", output.Type)
}

// If everything is fine, the recipient accepts and sends back her signature.
Expand Down
6 changes: 5 additions & 1 deletion samples/fungible/views/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ func (p *IssueCashView) Call(context view.Context) (interface{}, error) {
fmt.Printf("History [%s,%s]<[230]?\n", history.Sum(precision).ToBigInt().Text(10), p.TokenType)

// Fail if the sum of the issued tokens and the current quest is larger than 230
assert.True(history.Sum(precision).Add(token2.NewQuantityFromUInt64(p.Quantity)).Cmp(token2.NewQuantityFromUInt64(230)) <= 0)
q, err := token2.UInt64ToQuantity(p.Quantity, precision)
assert.NoError(err, "failed to convert quantity")
upperBound, err := token2.UInt64ToQuantity(230, precision)
assert.NoError(err, "failed to convert upper bound")
assert.True(history.Sum(precision).Add(q).Cmp(upperBound) <= 0)
}

// At this point, the issuer is ready to prepare the token transaction.
Expand Down
7 changes: 6 additions & 1 deletion token/core/fabtoken/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ func (s *Service) Issue(issuerIdentity view.Identity, typ string, values []uint6

var outs []*Output
var metas [][]byte
precision := s.PublicParamsManager().PublicParameters().Precision()
for i, v := range values {
q, err := token2.UInt64ToQuantity(v, precision)
if err != nil {
return nil, nil, nil, errors.Wrapf(err, "failed to convert [%d] to quantity of precision [%d]", v, precision)
}
outs = append(outs, &Output{
Output: &token2.Token{
Owner: &token2.Owner{
Raw: owners[i],
},
Type: typ,
Quantity: token2.NewQuantityFromUInt64(v).Hex(),
Quantity: q.Hex(),
},
})

Expand Down
18 changes: 11 additions & 7 deletions token/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,17 +1010,21 @@ func (r *Request) prepareTransfer(redeem bool, wallet *OwnerWallet, typ string,
}

// Compute output tokens
outputSum := uint64(0)
precision := r.TokenService.PublicParametersManager().Precision()
outputSum := token.NewZeroQuantity(precision)
var outputTokens []*token.Token
for i, value := range values {
outputSum += value
q, err := token.UInt64ToQuantity(value, precision)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to convert [%d] to quantity of precision [%d]", value, precision)
}
outputSum = outputSum.Add(q)
outputTokens = append(outputTokens, &token.Token{
Owner: &token.Owner{Raw: owners[i]},
Type: typ,
Quantity: token.NewQuantityFromUInt64(value).Hex(),
Quantity: q.Hex(),
})
}
qOutputSum := token.NewQuantityFromUInt64(outputSum)

// Select input tokens, if not passed as opt
if len(transferOpts.TokenIDs) == 0 {
Expand All @@ -1032,15 +1036,15 @@ func (r *Request) prepareTransfer(redeem bool, wallet *OwnerWallet, typ string,
return nil, nil, errors.Wrapf(err, "failed getting default selector")
}
}
tokenIDs, inputSum, err = selector.Select(wallet, token.NewQuantityFromUInt64(outputSum).Decimal(), typ)
tokenIDs, inputSum, err = selector.Select(wallet, outputSum.Decimal(), typ)
if err != nil {
return nil, nil, errors.Wrap(err, "failed selecting tokens")
}
}

// Is there a rest?
if inputSum.Cmp(qOutputSum) == 1 {
diff := inputSum.Sub(qOutputSum)
if inputSum.Cmp(outputSum) == 1 {
diff := inputSum.Sub(outputSum)
logger.Debugf("reassign rest [%s] to sender", diff.Decimal())

pseudonym, err := wallet.GetRecipientIdentity()
Expand Down
2 changes: 1 addition & 1 deletion token/services/nfttx/qe.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (s *QueryExecutor) QueryByKey(state interface{}, key string, value string)
if err != nil {
return errors.Wrap(err, "failed to convert quantity")
}
if q.Cmp(token2.NewQuantityFromUInt64(1)) == 0 {
if q.Cmp(token2.NewOneQuantity(s.precision)) == 0 {
// this is the token
decoded, err := base64.StdEncoding.DecodeString(t.Type)
if err != nil {
Expand Down
29 changes: 26 additions & 3 deletions token/token/quantity.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ type Quantity interface {
ToBigInt() *big.Int
}

// ToQuantity converts a string q to a BigQuantity of a given precision.
// ToQuantity converts a string q to a Quantity of a given precision.
// Argument q is supposed to be formatted following big.Int#scan specification.
// The precision is expressed in bits.
func ToQuantity(q string, precision uint64) (Quantity, error) {
if precision == 0 {
return nil, errors.New("precision be larger than 0")
return nil, errors.New("precision must be larger than 0")
}
v, success := big.NewInt(0).SetString(q, 0)
if !success {
Expand All @@ -64,6 +64,29 @@ func ToQuantity(q string, precision uint64) (Quantity, error) {
}
}

// UInt64ToQuantity converts a uint64 q to a Quantity of a given precision.
// Argument q is supposed to be formatted following big.Int#scan specification.
// The precision is expressed in bits.
func UInt64ToQuantity(u uint64, precision uint64) (Quantity, error) {
if precision == 0 {
return nil, errors.New("precision must be larger than 0")
}
v := big.NewInt(0).SetUint64(u)
if v.Cmp(big.NewInt(0)) < 0 {
return nil, errors.New("quantity must be larger than 0")
}
if v.BitLen() > int(precision) {
return nil, errors.Errorf("%d has precision %d > %d", u, v.BitLen(), precision)
}

switch precision {
case 64:
return &UInt64Quantity{Value: v.Uint64()}, nil
default:
return &BigQuantity{Int: v, Precision: precision}, nil
}
}

// NewZeroQuantity returns to zero quantity at the passed precision/
// The precision is expressed in bits.
func NewZeroQuantity(precision uint64) Quantity {
Expand Down Expand Up @@ -91,7 +114,7 @@ type BigQuantity struct {

func NewUBigQuantity(q string, precision uint64) (*BigQuantity, error) {
if precision == 0 {
return nil, errors.New("precision be larger than 0")
return nil, errors.New("precision must be larger than 0")
}
v, success := big.NewInt(0).SetString(q, 0)
if !success {
Expand Down
2 changes: 1 addition & 1 deletion token/token/quantity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

func TestToQuantity(t *testing.T) {
_, err := token2.ToQuantity(ToHex(100), 0)
assert.Equal(t, "precision be larger than 0", err.Error())
assert.Equal(t, "precision must be larger than 0", err.Error())

_, err = token2.ToQuantity(IntToHex(-100), 64)
assert.Equal(t, "invalid input [0x-64,64]", err.Error())
Expand Down

0 comments on commit 2a4e2d7

Please sign in to comment.