Skip to content

Commit c816bf0

Browse files
committed
[FAB-12460] transactor/verifier: ApproveRequest
- add approve request method to the transactor - add methods to verify approve request txs - add tests for transactor and verifier - add logger to verifier Change-Id: Ibe33b99347fa3451699ff2f4b9f66cbebc5538c6 Signed-off-by: Kaoutar Elkhiyaoui <kao@zurich.ibm.com> Signed-off-by: Mathias Bjoerkqvist <mbj@zurich.ibm.com>
1 parent ea986cb commit c816bf0

File tree

4 files changed

+787
-42
lines changed

4 files changed

+787
-42
lines changed

token/tms/plain/transactor.go

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,18 @@ SPDX-License-Identifier: Apache-2.0
77
package plain
88

99
import (
10+
"bytes"
1011
"fmt"
1112
"strconv"
1213
"strings"
1314

1415
"github.com/golang/protobuf/proto"
15-
"github.com/hyperledger/fabric/core/ledger/customtx"
1616
"github.com/hyperledger/fabric/protos/ledger/queryresult"
1717
"github.com/hyperledger/fabric/protos/token"
1818
"github.com/hyperledger/fabric/token/ledger"
1919
"github.com/pkg/errors"
2020
)
2121

22-
var namespace = "tms"
23-
24-
const tokenInput = "tokenInput"
25-
2622
// A Transactor that can transfer tokens.
2723
type Transactor struct {
2824
PublicCredential []byte
@@ -128,41 +124,45 @@ func (t *Transactor) getInputsFromTokenIds(tokenIds [][]byte) ([]*token.InputId,
128124
// check whether the composite key conforms to the composite key of an output
129125
namespace, components, err := splitCompositeKey(inKey)
130126
if err != nil {
131-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("error splitting input composite key: '%s'", err)}
127+
return nil, "", 0, errors.New(fmt.Sprintf("error splitting input composite key: '%s'", err))
132128
}
133129
if namespace != tokenOutput {
134-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("namespace not '%s': '%s'", tokenOutput, namespace)}
130+
return nil, "", 0, errors.New(fmt.Sprintf("namespace not '%s': '%s'", tokenOutput, namespace))
135131
}
136132
if len(components) != 2 {
137-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("not enough components in output ID composite key; expected 2, received '%s'", components)}
133+
return nil, "", 0, errors.New(fmt.Sprintf("not enough components in output ID composite key; expected 2, received '%s'", components))
138134
}
139135
txID := components[0]
140136
index, err := strconv.Atoi(components[1])
141137
if err != nil {
142-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("error parsing output index '%s': '%s'", components[1], err)}
138+
return nil, "", 0, errors.New(fmt.Sprintf("error parsing output index '%s': '%s'", components[1], err))
143139
}
144140

145141
// make sure the output exists in the ledger
146-
inBytes, err := t.Ledger.GetState(tokenNamespace, inKey)
142+
inBytes, err := t.Ledger.GetState(tokenNameSpace, inKey)
147143
if err != nil {
148144
return nil, "", 0, err
149145
}
150146
if inBytes == nil {
151-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("input '%s' does not exist", inKey)}
147+
return nil, "", 0, errors.New(fmt.Sprintf("input '%s' does not exist", inKey))
152148
}
153149
input := &token.PlainOutput{}
154150
err = proto.Unmarshal(inBytes, input)
155151
if err != nil {
156-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("error unmarshaling input bytes: '%s'", err)}
152+
return nil, "", 0, errors.New(fmt.Sprintf("error unmarshaling input bytes: '%s'", err))
153+
}
154+
155+
// check the owner of the token
156+
if !bytes.Equal(t.PublicCredential, input.Owner) {
157+
return nil, "", 0, errors.New(fmt.Sprintf("the requestor does not own inputs"))
157158
}
158159

159160
// check the token type - only one type allowed per transfer
160161
if tokenType == "" {
161162
tokenType = input.Type
162163
} else if tokenType != input.Type {
163-
return nil, "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("two or more token types specified in input: '%s', '%s'", tokenType, input.Type)}
164+
return nil, "", 0, errors.New(fmt.Sprintf("two or more token types specified in input: '%s', '%s'", tokenType, input.Type))
164165
}
165-
166166
// add input to list of inputs
167167
inputs = append(inputs, &token.InputId{TxId: txID, Index: uint32(index)})
168168

@@ -175,7 +175,7 @@ func (t *Transactor) getInputsFromTokenIds(tokenIds [][]byte) ([]*token.InputId,
175175

176176
// ListTokens creates a TokenTransaction that lists the unspent tokens owned by owner.
177177
func (t *Transactor) ListTokens() (*token.UnspentTokens, error) {
178-
iterator, err := t.Ledger.GetStateRangeScanIterator(namespace, "", "")
178+
iterator, err := t.Ledger.GetStateRangeScanIterator(tokenNameSpace, "", "")
179179
if err != nil {
180180
return nil, err
181181
}
@@ -228,7 +228,67 @@ func (t *Transactor) ListTokens() (*token.UnspentTokens, error) {
228228
}
229229

230230
func (t *Transactor) RequestApprove(request *token.ApproveRequest) (*token.TokenTransaction, error) {
231-
panic("implement me!")
231+
if len(request.GetTokenIds()) == 0 {
232+
return nil, errors.New("no token ids in ApproveAllowanceRequest")
233+
}
234+
235+
if len(request.AllowanceShares) == 0 {
236+
return nil, errors.New("no recipient shares in ApproveAllowanceRequest")
237+
}
238+
239+
var delegatedOutputs []*token.PlainDelegatedOutput
240+
241+
inputs, tokenType, sumQuantity, err := t.getInputsFromTokenIds(request.GetTokenIds())
242+
if err != nil {
243+
return nil, err
244+
}
245+
246+
// prepare approve tx
247+
248+
delegatedQuantity := uint64(0)
249+
for _, share := range request.GetAllowanceShares() {
250+
if len(share.Recipient) == 0 {
251+
return nil, errors.Errorf("the recipient in approve must be specified")
252+
}
253+
if share.Quantity <= 0 {
254+
return nil, errors.Errorf("the quantity to approve [%d] must be greater than 0", share.GetQuantity())
255+
}
256+
delegatedOutputs = append(delegatedOutputs, &token.PlainDelegatedOutput{
257+
Owner: []byte(request.Credential),
258+
Delegatees: [][]byte{share.Recipient},
259+
Type: tokenType,
260+
Quantity: share.Quantity,
261+
})
262+
delegatedQuantity = delegatedQuantity + share.Quantity
263+
}
264+
if sumQuantity < delegatedQuantity {
265+
return nil, errors.Errorf("insufficient funds: %v < %v", sumQuantity, delegatedQuantity)
266+
267+
}
268+
var output *token.PlainOutput
269+
if sumQuantity != delegatedQuantity {
270+
output = &token.PlainOutput{
271+
Owner: request.Credential,
272+
Type: tokenType,
273+
Quantity: sumQuantity - delegatedQuantity,
274+
}
275+
}
276+
277+
transaction := &token.TokenTransaction{
278+
Action: &token.TokenTransaction_PlainAction{
279+
PlainAction: &token.PlainTokenAction{
280+
Data: &token.PlainTokenAction_PlainApprove{
281+
PlainApprove: &token.PlainApprove{
282+
Inputs: inputs,
283+
DelegatedOutputs: delegatedOutputs,
284+
Output: output,
285+
},
286+
},
287+
},
288+
},
289+
}
290+
291+
return transaction, nil
232292
}
233293

234294
// isSpent checks whether an output token with identifier outputID has been spent.
@@ -237,7 +297,7 @@ func (t *Transactor) isSpent(outputID string) (bool, error) {
237297
if err != nil {
238298
return false, err
239299
}
240-
result, err := t.Ledger.GetState(namespace, key)
300+
result, err := t.Ledger.GetState(tokenNameSpace, key)
241301
if err != nil {
242302
return false, err
243303
}

0 commit comments

Comments
 (0)