Skip to content

Commit 9fb809b

Browse files
committed
[FAB-12891] Check owner not nil for import and transfer
- Add a boolean to optionally check if the owner is nil - Report error if an output owner is nil for import and transfer Change-Id: I2a24b7288334bf20756aa0406eebbf63d9bbed65 Signed-off-by: Wenjian Qiao <wenjianq@gmail.com>
1 parent 3297dd2 commit 9fb809b

File tree

2 files changed

+147
-38
lines changed

2 files changed

+147
-38
lines changed

token/tms/plain/verifier.go

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -108,44 +108,33 @@ func (v *Verifier) checkImportOutputs(outputs []*token.PlainOutput, txID string,
108108
return &customtx.InvalidTxError{Msg: fmt.Sprintf("no outputs in transaction: %s", txID)}
109109
}
110110
for i, output := range outputs {
111-
err := v.checkOutputDoesNotExist(i, txID, simulator)
111+
err := v.checkOutputDoesNotExist(i, output, txID, simulator)
112112
if err != nil {
113113
return err
114114
}
115115

116116
if output.Quantity == 0 {
117117
return &customtx.InvalidTxError{Msg: fmt.Sprintf("output %d quantity is 0 in transaction: %s", i, txID)}
118118
}
119+
120+
if len(output.Owner) == 0 {
121+
return &customtx.InvalidTxError{Msg: fmt.Sprintf("missing owner in output for txID '%s'", txID)}
122+
}
119123
}
120124
return nil
121125
}
122126

123127
func (v *Verifier) checkTransferAction(creator identity.PublicInfo, transferAction *token.PlainTransfer, txID string, simulator ledger.LedgerReader) error {
124-
outputType, outputSum, err := v.checkTransferOutputs(transferAction.GetOutputs(), txID, simulator)
125-
if err != nil {
126-
return err
127-
}
128-
inputType, inputSum, err := v.checkTransferInputs(creator, transferAction.GetInputs(), txID, simulator)
129-
if err != nil {
130-
return err
131-
}
132-
if outputType != inputType {
133-
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token type mismatch in inputs and outputs for transfer with ID %s (%s vs %s)", txID, outputType, inputType)}
134-
}
135-
if outputSum != inputSum {
136-
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for transfer with ID %s (%d vs %d)", txID, outputSum, inputSum)}
137-
}
138-
return nil
128+
return v.checkInputsAndOutputs(creator, transferAction.GetInputs(), transferAction.GetOutputs(), txID, simulator, true)
139129
}
140130

141131
func (v *Verifier) checkRedeemAction(creator identity.PublicInfo, redeemAction *token.PlainTransfer, txID string, simulator ledger.LedgerReader) error {
142-
// first perform the same checking as transfer
143-
err := v.checkTransferAction(creator, redeemAction, txID, simulator)
132+
err := v.checkInputsAndOutputs(creator, redeemAction.GetInputs(), redeemAction.GetOutputs(), txID, simulator, false)
144133
if err != nil {
145134
return err
146135
}
147136

148-
// then perform additional checking for redeem outputs
137+
// perform additional checking for redeem outputs
149138
// redeem transaction should not have more than 2 outputs.
150139
outputs := redeemAction.GetOutputs()
151140
if len(outputs) > 2 {
@@ -167,8 +156,40 @@ func (v *Verifier) checkRedeemAction(creator identity.PublicInfo, redeemAction *
167156
return nil
168157
}
169158

170-
func (v *Verifier) checkOutputDoesNotExist(index int, txID string, simulator ledger.LedgerReader) error {
171-
outputID, err := createOutputKey(txID, index)
159+
// checkInputsAndOutputs checks that inputs and outputs are valid and have same type and sum of quantity
160+
func (v *Verifier) checkInputsAndOutputs(
161+
creator identity.PublicInfo,
162+
inputIDs []*token.InputId,
163+
outputs []*token.PlainOutput,
164+
txID string,
165+
simulator ledger.LedgerReader,
166+
ownerRequired bool) error {
167+
168+
outputType, outputSum, err := v.checkOutputs(outputs, txID, simulator, ownerRequired)
169+
if err != nil {
170+
return err
171+
}
172+
inputType, inputSum, err := v.checkInputs(creator, inputIDs, txID, simulator)
173+
if err != nil {
174+
return err
175+
}
176+
if outputType != inputType {
177+
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token type mismatch in inputs and outputs for transaction ID %s (%s vs %s)", txID, outputType, inputType)}
178+
}
179+
if outputSum != inputSum {
180+
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for transaction ID %s (%d vs %d)", txID, outputSum, inputSum)}
181+
}
182+
return nil
183+
}
184+
185+
func (v *Verifier) checkOutputDoesNotExist(index int, output *token.PlainOutput, txID string, simulator ledger.LedgerReader) error {
186+
var outputID string
187+
var err error
188+
if output.Owner != nil {
189+
outputID, err = createOutputKey(txID, index)
190+
} else {
191+
outputID, err = createRedeemKey(txID, index)
192+
}
172193
if err != nil {
173194
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID: %s", err)}
174195
}
@@ -184,25 +205,28 @@ func (v *Verifier) checkOutputDoesNotExist(index int, txID string, simulator led
184205
return nil
185206
}
186207

187-
func (v *Verifier) checkTransferOutputs(outputs []*token.PlainOutput, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
208+
func (v *Verifier) checkOutputs(outputs []*token.PlainOutput, txID string, simulator ledger.LedgerReader, ownerRequired bool) (string, uint64, error) {
188209
tokenType := ""
189210
tokenSum := uint64(0)
190211
for i, output := range outputs {
191-
err := v.checkOutputDoesNotExist(i, txID, simulator)
212+
err := v.checkOutputDoesNotExist(i, output, txID, simulator)
192213
if err != nil {
193214
return "", 0, err
194215
}
195216
if tokenType == "" {
196217
tokenType = output.GetType()
197218
} else if tokenType != output.GetType() {
198-
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types ('%s', '%s') in transfer output for txID '%s'", tokenType, output.GetType(), txID)}
219+
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types ('%s', '%s') in output for txID '%s'", tokenType, output.GetType(), txID)}
220+
}
221+
if ownerRequired && len(output.GetOwner()) == 0 {
222+
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("missing owner in output for txID '%s'", txID)}
199223
}
200224
tokenSum += output.GetQuantity()
201225
}
202226
return tokenType, tokenSum, nil
203227
}
204228

205-
func (v *Verifier) checkTransferInputs(creator identity.PublicInfo, inputIDs []*token.InputId, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
229+
func (v *Verifier) checkInputs(creator identity.PublicInfo, inputIDs []*token.InputId, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
206230
tokenType := ""
207231
inputSum := uint64(0)
208232
processedIDs := make(map[string]bool)
@@ -222,10 +246,10 @@ func (v *Verifier) checkTransferInputs(creator identity.PublicInfo, inputIDs []*
222246
if tokenType == "" {
223247
tokenType = input.GetType()
224248
} else if tokenType != input.GetType() {
225-
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types in transfer input for txID: %s (%s, %s)", txID, tokenType, input.GetType())}
249+
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types in input for txID: %s (%s, %s)", txID, tokenType, input.GetType())}
226250
}
227251
if processedIDs[inputKey] {
228-
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("token input '%s' spent more than once in single transfer with txID '%s'", inputKey, txID)}
252+
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("token input '%s' spent more than once in transaction ID '%s'", inputKey, txID)}
229253
}
230254
processedIDs[inputKey] = true
231255
inputSum += input.GetQuantity()
@@ -355,7 +379,7 @@ func (v *Verifier) checkApproveAction(creator identity.PublicInfo, approveAction
355379
if err != nil {
356380
return err
357381
}
358-
inputType, inputSum, err := v.checkTransferInputs(creator, approveAction.GetInputs(), txID, simulator)
382+
inputType, inputSum, err := v.checkInputs(creator, approveAction.GetInputs(), txID, simulator)
359383
if err != nil {
360384
return err
361385
}
@@ -402,7 +426,7 @@ func (v *Verifier) checkApproveOutputs(creator identity.PublicInfo, output *toke
402426
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the owner of the output is not valid")}
403427
}
404428
tokenType = output.GetType()
405-
err := v.checkOutputDoesNotExist(0, txID, simulator)
429+
err := v.checkOutputDoesNotExist(0, output, txID, simulator)
406430
if err != nil {
407431
return "", 0, err
408432
}

token/tms/plain/verifier_test.go

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,18 @@ var _ = Describe("Verifier", func() {
192192
})
193193
})
194194

195+
Context("when an output has no owner", func() {
196+
BeforeEach(func() {
197+
importTransaction.GetPlainAction().GetPlainImport().Outputs[0].Owner = []byte{}
198+
importTxID = "no-owner-id"
199+
})
200+
201+
It("returns an InvalidTxError", func() {
202+
err := verifier.ProcessTx(importTxID, fakePublicInfo, importTransaction, memoryLedger)
203+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: fmt.Sprintf("missing owner in output for txID '%s'", importTxID)}))
204+
})
205+
})
206+
195207
})
196208

197209
Describe("Output GetState error scenarios", func() {
@@ -485,7 +497,7 @@ var _ = Describe("Verifier", func() {
485497

486498
It("returns an InvalidTxError", func() {
487499
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
488-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token input '\x00tokenOutput\x000\x000\x00' spent more than once in single transfer with txID '1'"}))
500+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token input '\x00tokenOutput\x000\x000\x00' spent more than once in transaction ID '1'"}))
489501
})
490502
})
491503

@@ -512,7 +524,7 @@ var _ = Describe("Verifier", func() {
512524

513525
It("returns an InvalidTxError", func() {
514526
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
515-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token type mismatch in inputs and outputs for transfer with ID 1 (wild_pineapple vs TOK1)"}))
527+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token type mismatch in inputs and outputs for transaction ID 1 (wild_pineapple vs TOK1)"}))
516528
})
517529
})
518530

@@ -539,7 +551,7 @@ var _ = Describe("Verifier", func() {
539551

540552
It("returns an InvalidTxError", func() {
541553
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
542-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token sum mismatch in inputs and outputs for transfer with ID 1 (124 vs 111)"}))
554+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token sum mismatch in inputs and outputs for transaction ID 1 (124 vs 111)"}))
543555
})
544556
})
545557

@@ -586,7 +598,7 @@ var _ = Describe("Verifier", func() {
586598

587599
It("returns an InvalidTxError", func() {
588600
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
589-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types in transfer input for txID: 1 (TOK1, TOK2)"}))
601+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types in input for txID: 1 (TOK1, TOK2)"}))
590602
})
591603
})
592604

@@ -613,7 +625,7 @@ var _ = Describe("Verifier", func() {
613625

614626
It("returns an InvalidTxError", func() {
615627
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
616-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types ('TOK1', 'TOK2') in transfer output for txID '1'"}))
628+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types ('TOK1', 'TOK2') in output for txID '1'"}))
617629
})
618630
})
619631

@@ -656,6 +668,17 @@ var _ = Describe("Verifier", func() {
656668
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: fmt.Sprintf("output already exists: %s", existingOutputId)}))
657669
})
658670
})
671+
672+
Context("when an output has no owner", func() {
673+
BeforeEach(func() {
674+
transferTransaction.GetPlainAction().GetPlainTransfer().Outputs[0].Owner = nil
675+
})
676+
677+
It("returns an InvalidTxError", func() {
678+
err := verifier.ProcessTx(transferTxID, fakePublicInfo, transferTransaction, memoryLedger)
679+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: fmt.Sprintf("missing owner in output for txID '%s'", transferTxID)}))
680+
})
681+
})
659682
})
660683

661684
Describe("Test ProcessTx PlainRedeem with memory ledger", func() {
@@ -793,7 +816,7 @@ var _ = Describe("Verifier", func() {
793816
It("returns an error", func() {
794817
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, memoryLedger)
795818
Expect(err).To(Equal(&customtx.InvalidTxError{
796-
Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for transfer with ID %s (%d vs %d)", redeemTxID, 100, 111)}))
819+
Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for transaction ID %s (%d vs %d)", redeemTxID, 100, 111)}))
797820
})
798821
})
799822

@@ -842,7 +865,19 @@ var _ = Describe("Verifier", func() {
842865
It("returns an error", func() {
843866
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, memoryLedger)
844867
Expect(err).To(Equal(&customtx.InvalidTxError{
845-
Msg: fmt.Sprintf("multiple token types in transfer input for txID: %s (TOK1, TOK2)", redeemTxID)}))
868+
Msg: fmt.Sprintf("multiple token types in input for txID: %s (TOK1, TOK2)", redeemTxID)}))
869+
})
870+
})
871+
872+
Context("when redeem output has wrong type", func() {
873+
BeforeEach(func() {
874+
redeemTransaction.GetPlainAction().GetPlainRedeem().Outputs[0].Type = "newtype"
875+
})
876+
877+
It("returns an error", func() {
878+
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, memoryLedger)
879+
Expect(err).To(MatchError(fmt.Sprintf(
880+
fmt.Sprintf("token type mismatch in inputs and outputs for transaction ID %s (%s vs %s)", redeemTxID, "newtype", "TOK1"))))
846881
})
847882
})
848883

@@ -871,6 +906,56 @@ var _ = Describe("Verifier", func() {
871906
Expect(err).To(MatchError(fmt.Sprintf(fmt.Sprintf("wrong owner for remaining tokens, should be original owner owner-1, but got owner-2"))))
872907
})
873908
})
909+
910+
Context("when output for remaining tokens has no owner", func() {
911+
BeforeEach(func() {
912+
// do not set owner in the output for unredeemed tokens
913+
redeemTransaction = &token.TokenTransaction{
914+
Action: &token.TokenTransaction_PlainAction{
915+
PlainAction: &token.PlainTokenAction{
916+
Data: &token.PlainTokenAction_PlainRedeem{
917+
PlainRedeem: &token.PlainTransfer{
918+
Inputs: inputIds,
919+
Outputs: []*token.PlainOutput{
920+
{Type: "TOK1", Quantity: 99},
921+
{Type: "TOK1", Quantity: 12},
922+
},
923+
},
924+
},
925+
},
926+
},
927+
}
928+
})
929+
930+
It("returns an error", func() {
931+
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, memoryLedger)
932+
Expect(err).To(MatchError(fmt.Sprintf(fmt.Sprintf("wrong owner for remaining tokens, should be original owner owner-1, but got "))))
933+
})
934+
})
935+
936+
Context("when output for redeemed tokens has owner", func() {
937+
BeforeEach(func() {
938+
// set owner for the redeem output
939+
redeemTransaction.GetPlainAction().GetPlainRedeem().Outputs[0].Owner = []byte("Owner-1")
940+
})
941+
942+
It("returns an error", func() {
943+
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, memoryLedger)
944+
Expect(err).To(MatchError(fmt.Sprintf(fmt.Sprintf("owner should be nil in a redeem output"))))
945+
})
946+
})
947+
948+
Context("when redeem output key already exists", func() {
949+
BeforeEach(func() {
950+
fakeLedger.GetStateReturns([]byte("state-bytes"), nil)
951+
})
952+
953+
It("returns an error", func() {
954+
err := verifier.ProcessTx(redeemTxID, fakePublicInfo, redeemTransaction, fakeLedger)
955+
existingOutputID := string("\x00") + "tokenRedeem" + string("\x00") + redeemTxID + string("\x00") + "0" + string("\x00")
956+
Expect(err).To(MatchError(fmt.Sprintf("output already exists: %s", existingOutputID)))
957+
})
958+
})
874959
})
875960

876961
Describe("Test ProcessTx PlainApprove", func() {
@@ -991,7 +1076,7 @@ var _ = Describe("Verifier", func() {
9911076
}
9921077

9931078
err := verifier.ProcessTx(approveTxID, fakePublicInfo, approveTransaction, fakeLedger)
994-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token input '\x00tokenOutput\x000\x000\x00' spent more than once in single transfer with txID '1'"}))
1079+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "token input '\x00tokenOutput\x000\x000\x00' spent more than once in transaction ID '1'"}))
9951080
})
9961081
})
9971082

@@ -1078,7 +1163,7 @@ var _ = Describe("Verifier", func() {
10781163
},
10791164
}
10801165
err = verifier.ProcessTx(approveTxID, fakePublicInfo, approveTransaction, fakeLedger)
1081-
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types in transfer input for txID: 1 (XYZ, ABC)"}))
1166+
Expect(err).To(Equal(&customtx.InvalidTxError{Msg: "multiple token types in input for txID: 1 (XYZ, ABC)"}))
10821167
})
10831168
})
10841169

0 commit comments

Comments
 (0)