@@ -2,6 +2,7 @@ package token
22
33import (
44 "errors"
5+ "fmt"
56 "math/big"
67 "strings"
78
@@ -15,6 +16,7 @@ var _ BalanceStore = &UTXOStore{}
1516var (
1617 ErrSenderRecipientEqual = errors .New (`sender recipient equal` )
1718 ErrSenderNotEqual = errors .New (`sender not equal` )
19+ ErrOpAddressNotEqual = errors .New (`operation address not equal` )
1820 ErrSymbolNotEqual = errors .New (`symbol not equal` )
1921 ErrRecipientDuplicate = errors .New (`errors recipient duplicate` )
2022)
@@ -96,6 +98,14 @@ func (u *UTXOStore) GetLocked(ctx router.Context, balanceId *BalanceId) (*Balanc
9698 return balance , nil
9799}
98100
101+ func (u * UTXOStore ) GetUTXO (ctx router.Context , utxoId * UTXOId ) (* UTXO , error ) {
102+ utxo , err := State (ctx ).Get (& UTXOId {Symbol : utxoId .Symbol , Group : utxoId .Group , Address : utxoId .Address , TxId : utxoId .TxId }, & UTXO {})
103+ if err != nil {
104+ return nil , err
105+ }
106+ return utxo .(* UTXO ), nil
107+ }
108+
99109// ListOutputs unspended outputs list
100110func (u * UTXOStore ) ListOutputs (ctx router.Context , balanceId * BalanceId ) ([]* UTXO , error ) {
101111 return listOutputs (ctx , balanceId )
@@ -154,7 +164,7 @@ func (u *UTXOStore) Transfer(ctx router.Context, transfer *TransferOperation) er
154164 Group : strings .Join (transfer .Group , `,` ),
155165 Address : transfer .Sender ,
156166 TxId : txID ,
157- Amount : decimal .BigIntSubAsDecimal (outputsAmount , transferAmount ),
167+ Amount : decimal .BigIntSubAsDecimal (outputsAmount , transferAmount , transfer . Amount . Scale ),
158168 Locked : false ,
159169 }
160170 if err := State (ctx ).Insert (senderChangeOutput ); err != nil {
@@ -174,6 +184,7 @@ func (u *UTXOStore) Transfer(ctx router.Context, transfer *TransferOperation) er
174184func (u * UTXOStore ) TransferBatch (ctx router.Context , transfers []* TransferOperation ) error {
175185 var (
176186 sender , symbol string
187+ scale int32
177188 group []string
178189 recipients = make (map [string ]interface {})
179190 totalAmount = big .NewInt (0 )
@@ -207,6 +218,8 @@ func (u *UTXOStore) TransferBatch(ctx router.Context, transfers []*TransferOpera
207218 panic (`implement me` )
208219 }
209220
221+ scale = transfer .Amount .Scale
222+
210223 if _ , ok := recipients [transfer .Recipient ]; ok {
211224 return ErrRecipientDuplicate
212225 }
@@ -262,7 +275,7 @@ func (u *UTXOStore) TransferBatch(ctx router.Context, transfers []*TransferOpera
262275 Group : strings .Join (group , `,` ),
263276 Address : sender ,
264277 TxId : txID ,
265- Amount : decimal .BigIntSubAsDecimal (outputsAmount , totalAmount ),
278+ Amount : decimal .BigIntSubAsDecimal (outputsAmount , totalAmount , scale ),
266279 Locked : false ,
267280 }
268281 if err := State (ctx ).Insert (senderChangeOutput ); err != nil {
@@ -288,7 +301,9 @@ func (u *UTXOStore) Mint(ctx router.Context, op *BalanceOperation) error {
288301
289302// Lock tokens
290303func (u * UTXOStore ) Lock (ctx router.Context , op * BalanceOperation ) (* LockId , error ) {
291- // return setLock(ctx, op, true)
304+ if err := router .ValidateRequest (op ); err != nil {
305+ return nil , err
306+ }
292307
293308 outputs , err := u .ListOutputs (ctx , & BalanceId {
294309 Symbol : op .Symbol ,
@@ -334,16 +349,121 @@ func (u *UTXOStore) Lock(ctx router.Context, op *BalanceOperation) (*LockId, err
334349 Group : strings .Join (op .Group , `,` ),
335350 Address : op .Address ,
336351 TxId : ctx .Stub ().GetTxID () + ".1" ,
337- Amount : decimal .BigIntSubAsDecimal (outputsAmount , opAmount ),
352+ Amount : decimal .BigIntSubAsDecimal (outputsAmount , opAmount , op . Amount . Scale ),
338353 Locked : false ,
339354 }
340355 if err := State (ctx ).Insert (senderChangeOutput ); err != nil {
341356 return nil , err
342357 }
343358 }
359+
344360 return & LockId {lockedOutput .Symbol , lockedOutput .Group , lockedOutput .Address , lockedOutput .TxId }, nil
345361}
346362
363+ func (u * UTXOStore ) LockBatch (ctx router.Context , ops []* BalanceOperation ) ([]* LockId , error ) {
364+ var (
365+ opAddress , symbol string
366+ scale int32
367+ group []string
368+ totalAmount = big .NewInt (0 )
369+ )
370+
371+ for _ , op := range ops {
372+ if err := router .ValidateRequest (op ); err != nil {
373+ return nil , err
374+ }
375+
376+ if opAddress == `` {
377+ opAddress = op .Address
378+ }
379+ if op .Address != opAddress {
380+ return nil , ErrOpAddressNotEqual
381+ }
382+
383+ if symbol == `` {
384+ symbol = op .Symbol
385+ }
386+ if op .Symbol != symbol {
387+ return nil , ErrSymbolNotEqual
388+ }
389+
390+ if len (op .Group ) > 0 {
391+ panic (`implement me` )
392+ }
393+
394+ scale = op .Amount .Scale
395+
396+ opAmount , err := op .Amount .BigInt ()
397+ if err != nil {
398+ return nil , err
399+ }
400+ totalAmount .Add (totalAmount , opAmount )
401+ }
402+
403+ outputs , err := u .ListOutputs (ctx , & BalanceId {
404+ Symbol : symbol ,
405+ Group : group ,
406+ Address : opAddress ,
407+ })
408+ if err != nil {
409+ return nil , err
410+ }
411+
412+ useOutputs , outputsAmount , err := selectOutputsForAmount (outputs , totalAmount , false )
413+ if err != nil {
414+ return nil , err
415+ }
416+
417+ for _ , output := range useOutputs {
418+ if err := State (ctx ).Delete (output .ID ()); err != nil {
419+ return nil , err
420+ }
421+ }
422+
423+ lockIds := make ([]* LockId , len (ops ))
424+
425+ for id , op := range ops {
426+ txID := ctx .Stub ().GetTxID () + fmt .Sprintf (".%v" , id )
427+
428+ lockedOutput := & UTXO {
429+ Symbol : op .Symbol ,
430+ Group : strings .Join (op .Group , `,` ),
431+ Address : op .Address ,
432+ TxId : txID ,
433+ Amount : op .Amount ,
434+ Locked : true ,
435+ //Meta: transfer.Meta,
436+ }
437+
438+ lockIds [id ] = & LockId {
439+ Symbol : op .Symbol ,
440+ Group : strings .Join (op .Group , `,` ),
441+ Address : op .Address ,
442+ TxId : txID ,
443+ }
444+
445+ if err := State (ctx ).Insert (lockedOutput ); err != nil {
446+ return nil , err
447+ }
448+ }
449+
450+ if outputsAmount .Cmp (totalAmount ) == 1 {
451+ senderChangeOutput := & UTXO {
452+ Symbol : symbol ,
453+ Group : strings .Join (group , `,` ),
454+ Address : opAddress ,
455+ TxId : ctx .Stub ().GetTxID () + fmt .Sprintf (".%v" , len (ops )),
456+ Amount : decimal .BigIntSubAsDecimal (outputsAmount , totalAmount , scale ),
457+ Locked : false ,
458+ }
459+ if err := State (ctx ).Insert (senderChangeOutput ); err != nil {
460+ return nil , err
461+ }
462+ }
463+
464+ return lockIds , nil
465+ }
466+
347467func (u * UTXOStore ) LockAll (ctx router.Context , op * BalanceOperation ) error {
348468 utxos , err := State (ctx ).ListWith (& UTXO {}, state.Key {op .Symbol , strings .Join (op .Group , `,` )}) // todo: ???
349469 if err != nil {
@@ -379,7 +499,7 @@ func (u *UTXOStore) Burn(ctx router.Context, op *BalanceOperation) error {
379499 return burn (ctx , op , false )
380500}
381501
382- // Burn locked tokens
502+ // BurnLock locked tokens
383503func (u * UTXOStore ) BurnLock (ctx router.Context , id * LockId ) error {
384504 utxo , err := State (ctx ).Get (& UTXOId {Symbol : id .Symbol , Group : id .Group , Address : id .Address , TxId : id .TxId }, & UTXO {})
385505 if err != nil {
@@ -393,7 +513,7 @@ func (u *UTXOStore) BurnLock(ctx router.Context, id *LockId) error {
393513 return nil
394514}
395515
396- // Transfer locked tokens between accounts
516+ // TransferLock locked tokens between accounts
397517func (u * UTXOStore ) TransferLock (ctx router.Context , id * LockId , transfer * TransferOperation ) error {
398518 utxo , err := State (ctx ).Get (& UTXOId {Symbol : id .Symbol , Group : id .Group , Address : id .Address , TxId : id .TxId }, & UTXO {})
399519 if err != nil {
@@ -500,7 +620,7 @@ func burn(ctx router.Context, burn *BalanceOperation, locked bool) error {
500620 Group : strings .Join (burn .Group , `,` ),
501621 Address : burn .Address ,
502622 TxId : ctx .Stub ().GetTxID (),
503- Amount : decimal .BigIntSubAsDecimal (outputsAmount , burnAmount ),
623+ Amount : decimal .BigIntSubAsDecimal (outputsAmount , burnAmount , burn . Amount . Scale ),
504624 Locked : locked ,
505625 }
506626 if err := State (ctx ).Insert (senderChangeOutput ); err != nil {
0 commit comments