Skip to content
This repository was archived by the owner on Feb 4, 2026. It is now read-only.

Commit 65a82cf

Browse files
authored
Improvement token and patch envelope (#35)
* Change settings for json serializer * Add LockBatch and GetUTXO * Do not check the sig for Init method * Resolve the unclear json serialization
1 parent e038aad commit 65a82cf

7 files changed

Lines changed: 197 additions & 27 deletions

File tree

extensions/envelope/middleware.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,34 @@ const (
1818

1919
nonceObjectType = "nonce"
2020
invokeType = "invoke"
21+
initType = "init"
2122

2223
TimeLayout = "2006-01-02T15:04:05.000Z"
2324

2425
PubKey string = "envelopePubkey" // router context key
2526
)
2627

27-
// middleware for checking signature that is got in envelop
28+
// Verify is a middleware for checking signature in envelop
2829
func Verify() router.MiddlewareFunc {
2930
return func(next router.HandlerFunc, pos ...int) router.HandlerFunc {
3031
return func(c router.Context) (interface{}, error) {
3132
if c.Handler().Type == invokeType {
3233
iArgs := c.GetArgs()
33-
if len(iArgs) == 2 {
34-
c.Logger().Sugar().Error(ErrSignatureNotFound)
35-
return nil, ErrSignatureNotFound
36-
} else {
37-
var (
38-
e *Envelope
39-
err error
40-
)
41-
if e, err = verifyEnvelope(c, iArgs[methodNamePos], iArgs[payloadPos], iArgs[envelopePos]); err != nil {
42-
return nil, err
34+
if string(iArgs[methodNamePos]) != initType {
35+
if len(iArgs) == 2 {
36+
c.Logger().Sugar().Error(ErrSignatureNotFound)
37+
return nil, ErrSignatureNotFound
38+
} else {
39+
var (
40+
e *Envelope
41+
err error
42+
)
43+
if e, err = verifyEnvelope(c, iArgs[methodNamePos], iArgs[payloadPos], iArgs[envelopePos]); err != nil {
44+
return nil, err
45+
}
46+
// store correct pubkey in context
47+
c.SetParam(PubKey, e.PublicKey)
4348
}
44-
// store corect pubkey in context
45-
c.SetParam(PubKey, e.PublicKey)
4649
}
4750
}
4851
return next(c)
@@ -58,6 +61,7 @@ func verifyEnvelope(c router.Context, method, payload, envlp []byte) (*Envelope,
5861
return nil, err
5962
}
6063
envelope := data.(*Envelope)
64+
6165
if envelope.Deadline.AsTime().Unix() != 0 {
6266
if envelope.Deadline.AsTime().Unix() < time.Now().Unix() {
6367
c.Logger().Sugar().Error(ErrDeadlineExpired)
@@ -70,7 +74,7 @@ func verifyEnvelope(c router.Context, method, payload, envlp []byte) (*Envelope,
7074
c.Logger().Sugar().Error(ErrInvalidMethod)
7175
return nil, ErrInvalidMethod
7276
}
73-
if string(c.Stub().GetChannelID()) != envelope.Channel {
77+
if c.Stub().GetChannelID() != envelope.Channel {
7478
c.Logger().Sugar().Error(ErrInvalidChannel)
7579
return nil, ErrInvalidChannel
7680
}
@@ -95,7 +99,8 @@ func verifyEnvelope(c router.Context, method, payload, envlp []byte) (*Envelope,
9599
deadline = envelope.Deadline.AsTime().Format(TimeLayout)
96100
}
97101
if err := CheckSig(payload, envelope.Nonce, envelope.Channel, envelope.Chaincode, envelope.Method, deadline, pubkey, sig); err != nil {
98-
c.Logger().Sugar().Error(ErrCheckSignatureFailed)
102+
c.Logger().Error(ErrCheckSignatureFailed.Error(), zap.String("payload", string(payload)), zap.Any("envelope", envelope))
103+
//c.Logger().Sugar().Error(ErrCheckSignatureFailed)
99104
return nil, ErrCheckSignatureFailed
100105
}
101106
} else {

extensions/envelope/signature.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,20 @@ func Hash(payload []byte, nonce, channel, chaincode, method, deadline string, pu
3737
func CreateSig(payload []byte, nonce, channel, chaincode, method, deadline string, privateKey []byte) ([]byte, []byte) {
3838
pubKey := ed25519.PrivateKey(privateKey).Public()
3939
hashed := Hash(payload, nonce, channel, chaincode, method, deadline, []byte(pubKey.(ed25519.PublicKey)))
40-
sig := ed25519.Sign(ed25519.PrivateKey(privateKey), hashed[:])
41-
return []byte(pubKey.(ed25519.PublicKey)), sig
40+
sig := ed25519.Sign(privateKey, hashed[:])
41+
return pubKey.(ed25519.PublicKey), sig
4242
}
4343

4444
func CheckSig(payload []byte, nonce, channel, chaincode, method, deadline string, pubKey []byte, sig []byte) error {
4545
hashed := Hash(payload, nonce, channel, chaincode, method, deadline, pubKey)
46-
if !ed25519.Verify(ed25519.PublicKey(pubKey), hashed[:], sig) {
46+
if !ed25519.Verify(pubKey, hashed[:], sig) {
4747
return ErrCheckSignatureFailed
4848
}
4949
return nil
5050
}
5151

5252
func removeSpacesBetweenCommaAndQuotes(s []byte) []byte {
53-
return []byte(strings.ReplaceAll(string(s), `", "`, `","`))
53+
removed := strings.ReplaceAll(string(s), `", "`, `","`)
54+
removed = strings.ReplaceAll(removed, `"}, {"`, `"},{"`)
55+
return []byte(strings.ReplaceAll(removed, `], "`, `],"`))
5456
}

extensions/token/balance_account.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ func (s *AccountStore) Lock(ctx router.Context, burn *BalanceOperation) (*LockId
111111
return nil, nil
112112
}
113113

114+
func (u *AccountStore) LockBatch(ctx router.Context, ops []*BalanceOperation) ([]*LockId, error) {
115+
return nil, nil
116+
}
117+
114118
func (s *AccountStore) Unlock(ctx router.Context, id *LockId) error {
115119
return nil
116120
}
@@ -119,6 +123,10 @@ func (s *AccountStore) BurnLock(ctx router.Context, id *LockId) error {
119123
return nil
120124
}
121125

126+
func (s *AccountStore) GetUTXO(ctx router.Context, utxoId *UTXOId) (*UTXO, error) {
127+
return nil, nil
128+
}
129+
122130
// todo: add TransferLock implementation
123131
func (u *AccountStore) TransferLock(ctx router.Context, id *LockId, transfer *TransferOperation) error {
124132
return nil

extensions/token/balance_store.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ type (
1515
BalanceStore interface {
1616
Get(router.Context, *BalanceId) (*Balance, error)
1717
GetLocked(router.Context, *BalanceId) (*Balance, error)
18+
GetUTXO(router.Context, *UTXOId) (*UTXO, error)
1819
List(router.Context, *TokenId) ([]*Balance, error)
1920
Transfer(router.Context, *TransferOperation) error
2021
TransferBatch(router.Context, []*TransferOperation) error
2122
Mint(router.Context, *BalanceOperation) error
2223
Burn(router.Context, *BalanceOperation) error
2324
Lock(router.Context, *BalanceOperation) (*LockId, error)
25+
LockBatch(router.Context, []*BalanceOperation) ([]*LockId, error)
2426
Unlock(router.Context, *LockId) error
2527
BurnLock(router.Context, *LockId) error
2628
TransferLock(router.Context, *LockId, *TransferOperation) error

extensions/token/balance_utxo.go

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package token
22

33
import (
44
"errors"
5+
"fmt"
56
"math/big"
67
"strings"
78

@@ -15,6 +16,7 @@ var _ BalanceStore = &UTXOStore{}
1516
var (
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
100110
func (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
174184
func (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
290303
func (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+
347467
func (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
383503
func (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
397517
func (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

Comments
 (0)