1 Star 1 Fork 0

Hyperledger Fabric 国密 / fabric

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
verifier.go 23.12 KB
一键复制 编辑 原始数据 按行查看 历史
Jtyoui 提交于 2021-07-22 15:59 . 国密
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package plain
import (
"bytes"
"encoding/hex"
"fmt"
"strconv"
"unicode/utf8"
"github.com/golang/protobuf/proto"
"gitee.com/hyperledger-fabric-gm/fabric/common/flogging"
"gitee.com/hyperledger-fabric-gm/fabric/core/ledger/customtx"
"gitee.com/hyperledger-fabric-gm/fabric/protos/token"
"gitee.com/hyperledger-fabric-gm/fabric/protos/utils"
"gitee.com/hyperledger-fabric-gm/fabric/token/identity"
"gitee.com/hyperledger-fabric-gm/fabric/token/ledger"
"github.com/pkg/errors"
)
const (
minUnicodeRuneValue = 0 //U+0000
maxUnicodeRuneValue = utf8.MaxRune //U+10FFFF - maximum (and unallocated) code point
compositeKeyNamespace = "\x00"
tokenOutput = "tokenOutput"
tokenRedeem = "tokenRedeem"
tokenTx = "tokenTx"
tokenDelegatedOutput = "tokenDelegatedOutput"
tokenInput = "tokenInput"
tokenDelegatedInput = "tokenDelegateInput"
tokenNameSpace = "tms"
)
var verifierLogger = flogging.MustGetLogger("token.tms.plain.verifier")
// A Verifier validates and commits token transactions.
type Verifier struct {
IssuingValidator identity.IssuingValidator
}
// ProcessTx checks that transactions are correct wrt. the most recent ledger state.
// ProcessTx checks are ones that shall be done sequentially, since transactions within a block may introduce dependencies.
func (v *Verifier) ProcessTx(txID string, creator identity.PublicInfo, ttx *token.TokenTransaction, simulator ledger.LedgerWriter) error {
verifierLogger.Debugf("checking transaction with txID '%s'", txID)
err := v.checkProcess(txID, creator, ttx, simulator)
if err != nil {
return err
}
verifierLogger.Debugf("committing transaction with txID '%s'", txID)
err = v.commitProcess(txID, creator, ttx, simulator)
if err != nil {
verifierLogger.Errorf("error committing transaction with txID '%s': %s", txID, err)
return err
}
verifierLogger.Debugf("successfully processed transaction with txID '%s'", txID)
return nil
}
func (v *Verifier) checkProcess(txID string, creator identity.PublicInfo, ttx *token.TokenTransaction, simulator ledger.LedgerReader) error {
action := ttx.GetPlainAction()
if action == nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("check process failed for transaction '%s': missing token action", txID)}
}
err := v.checkAction(creator, action, txID, simulator)
if err != nil {
return err
}
err = v.checkTxDoesNotExist(txID, simulator)
if err != nil {
return err
}
return nil
}
func (v *Verifier) checkAction(creator identity.PublicInfo, plainAction *token.PlainTokenAction, txID string, simulator ledger.LedgerReader) error {
switch action := plainAction.Data.(type) {
case *token.PlainTokenAction_PlainImport:
return v.checkImportAction(creator, action.PlainImport, txID, simulator)
case *token.PlainTokenAction_PlainTransfer:
return v.checkTransferAction(creator, action.PlainTransfer, txID, simulator)
case *token.PlainTokenAction_PlainRedeem:
return v.checkRedeemAction(creator, action.PlainRedeem, txID, simulator)
case *token.PlainTokenAction_PlainApprove:
return v.checkApproveAction(creator, action.PlainApprove, txID, simulator)
default:
return &customtx.InvalidTxError{Msg: fmt.Sprintf("unknown plain token action: %T", action)}
}
}
func (v *Verifier) checkImportAction(creator identity.PublicInfo, importAction *token.PlainImport, txID string, simulator ledger.LedgerReader) error {
err := v.checkImportOutputs(importAction.GetOutputs(), txID, simulator)
if err != nil {
return err
}
return v.checkImportPolicy(creator, txID, importAction)
}
func (v *Verifier) checkImportOutputs(outputs []*token.PlainOutput, txID string, simulator ledger.LedgerReader) error {
if len(outputs) == 0 {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("no outputs in transaction: %s", txID)}
}
for i, output := range outputs {
err := v.checkOutputDoesNotExist(i, txID, simulator)
if err != nil {
return err
}
if output.Quantity == 0 {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("output %d quantity is 0 in transaction: %s", i, txID)}
}
}
return nil
}
func (v *Verifier) checkTransferAction(creator identity.PublicInfo, transferAction *token.PlainTransfer, txID string, simulator ledger.LedgerReader) error {
outputType, outputSum, err := v.checkTransferOutputs(transferAction.GetOutputs(), txID, simulator)
if err != nil {
return err
}
inputType, inputSum, err := v.checkTransferInputs(creator, transferAction.GetInputs(), txID, simulator)
if err != nil {
return err
}
if outputType != inputType {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token type mismatch in inputs and outputs for transfer with ID %s (%s vs %s)", txID, outputType, inputType)}
}
if outputSum != inputSum {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for transfer with ID %s (%d vs %d)", txID, outputSum, inputSum)}
}
return nil
}
func (v *Verifier) checkRedeemAction(creator identity.PublicInfo, redeemAction *token.PlainTransfer, txID string, simulator ledger.LedgerReader) error {
// first perform the same checking as transfer
err := v.checkTransferAction(creator, redeemAction, txID, simulator)
if err != nil {
return err
}
// then perform additional checking for redeem outputs
// redeem transaction should not have more than 2 outputs.
outputs := redeemAction.GetOutputs()
if len(outputs) > 2 {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("too many outputs (%d) in a redeem transaction", len(outputs))}
}
// output[0] should always be a redeem output - i.e., owner should be nil
if outputs[0].Owner != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("owner should be nil in a redeem output")}
}
// if output[1] presents, its owner must be same as the creator
if len(outputs) == 2 && !bytes.Equal(creator.Public(), outputs[1].Owner) {
println(hex.EncodeToString(creator.Public()))
println(hex.EncodeToString(outputs[1].Owner))
return &customtx.InvalidTxError{Msg: fmt.Sprintf("wrong owner for remaining tokens, should be original owner %s, but got %s", creator.Public(), outputs[1].Owner)}
}
return nil
}
func (v *Verifier) checkOutputDoesNotExist(index int, txID string, simulator ledger.LedgerReader) error {
outputID, err := createOutputKey(txID, index)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID: %s", err)}
}
existingOutputBytes, err := simulator.GetState(tokenNameSpace, outputID)
if err != nil {
return err
}
if existingOutputBytes != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("output already exists: %s", outputID)}
}
return nil
}
func (v *Verifier) checkTransferOutputs(outputs []*token.PlainOutput, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
tokenType := ""
tokenSum := uint64(0)
for i, output := range outputs {
err := v.checkOutputDoesNotExist(i, txID, simulator)
if err != nil {
return "", 0, err
}
if tokenType == "" {
tokenType = output.GetType()
} else if tokenType != output.GetType() {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types ('%s', '%s') in transfer output for txID '%s'", tokenType, output.GetType(), txID)}
}
tokenSum += output.GetQuantity()
}
return tokenType, tokenSum, nil
}
func (v *Verifier) checkTransferInputs(creator identity.PublicInfo, inputIDs []*token.InputId, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
tokenType := ""
inputSum := uint64(0)
processedIDs := make(map[string]bool)
for _, id := range inputIDs {
inputKey, err := createOutputKey(id.TxId, int(id.Index))
if err != nil {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID for transfer input: %s", err)}
}
input, err := v.getOutput(inputKey, simulator)
if err != nil {
return "", 0, err
}
err = v.checkInputOwner(creator, input, inputKey)
if err != nil {
return "", 0, err
}
if tokenType == "" {
tokenType = input.GetType()
} else if tokenType != input.GetType() {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types in transfer input for txID: %s (%s, %s)", txID, tokenType, input.GetType())}
}
if processedIDs[inputKey] {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("token input '%s' spent more than once in single transfer with txID '%s'", inputKey, txID)}
}
processedIDs[inputKey] = true
inputSum += input.GetQuantity()
spentKey, err := createSpentKey(id.TxId, int(id.Index))
if err != nil {
return "", 0, err
}
spent, err := v.isSpent(spentKey, simulator)
if err != nil {
return "", 0, err
}
if spent {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("input with ID %s for transfer has already been spent", inputKey)}
}
}
return tokenType, inputSum, nil
}
func (v *Verifier) checkInputOwner(creator identity.PublicInfo, input *token.PlainOutput, inputID string) error {
if !bytes.Equal(creator.Public(), input.Owner) {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("transfer input with ID %s not owned by creator", inputID)}
}
return nil
}
func (v *Verifier) checkTxDoesNotExist(txID string, simulator ledger.LedgerReader) error {
txKey, err := createTxKey(txID)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating txID: %s", err)}
}
existingTx, err := simulator.GetState(tokenNameSpace, txKey)
if err != nil {
return err
}
if existingTx != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("transaction already exists: %s", txID)}
}
return nil
}
func (v *Verifier) checkImportPolicy(creator identity.PublicInfo, txID string, importData *token.PlainImport) error {
for _, output := range importData.Outputs {
err := v.IssuingValidator.Validate(creator, output.Type)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("import policy check failed: %s", err)}
}
}
return nil
}
func (v *Verifier) commitProcess(txID string, creator identity.PublicInfo, ttx *token.TokenTransaction, simulator ledger.LedgerWriter) error {
verifierLogger.Debugf("committing action with txID '%s'", txID)
err := v.commitAction(ttx.GetPlainAction(), txID, simulator)
if err != nil {
verifierLogger.Errorf("error committing action with txID '%s': %s", txID, err)
return err
}
verifierLogger.Debugf("adding transaction with txID '%s'", txID)
err = v.addTransaction(txID, ttx, simulator)
if err != nil {
verifierLogger.Debugf("error adding transaction with txID '%s': %s", txID, err)
return err
}
verifierLogger.Debugf("action with txID '%s' committed successfully", txID)
return nil
}
func (v *Verifier) commitAction(plainAction *token.PlainTokenAction, txID string, simulator ledger.LedgerWriter) (err error) {
switch action := plainAction.Data.(type) {
case *token.PlainTokenAction_PlainImport:
err = v.commitImportAction(action.PlainImport, txID, simulator)
case *token.PlainTokenAction_PlainTransfer:
err = v.commitTransferAction(action.PlainTransfer, txID, simulator)
case *token.PlainTokenAction_PlainRedeem:
// call the same commit method as transfer because PlainRedeem points to the same type of outputs as transfer
err = v.commitTransferAction(action.PlainRedeem, txID, simulator)
case *token.PlainTokenAction_PlainApprove:
err = v.commitApproveAction(action.PlainApprove, txID, simulator)
}
return
}
func (v *Verifier) commitImportAction(importAction *token.PlainImport, txID string, simulator ledger.LedgerWriter) error {
for i, output := range importAction.GetOutputs() {
outputID, err := createOutputKey(txID, i)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID: %s", err)}
}
err = v.addOutput(outputID, output, simulator)
if err != nil {
return err
}
}
return nil
}
// commitTransferAction is called for both transfer and redeem transactions
// Check the owner of each output to determine how to generate the key
func (v *Verifier) commitTransferAction(transferAction *token.PlainTransfer, txID string, simulator ledger.LedgerWriter) error {
var outputID string
var err error
for i, output := range transferAction.GetOutputs() {
if output.Owner != nil {
outputID, err = createOutputKey(txID, i)
} else {
outputID, err = createRedeemKey(txID, i)
}
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID: %s", err)}
}
err = v.addOutput(outputID, output, simulator)
if err != nil {
return err
}
}
return v.markInputsSpent(txID, transferAction.GetInputs(), simulator)
}
func (v *Verifier) checkApproveAction(creator identity.PublicInfo, approveAction *token.PlainApprove, txID string, simulator ledger.LedgerReader) error {
outputType, outputSum, err := v.checkApproveOutputs(creator, approveAction.GetOutput(), approveAction.GetDelegatedOutputs(), txID, simulator)
if err != nil {
return err
}
inputType, inputSum, err := v.checkTransferInputs(creator, approveAction.GetInputs(), txID, simulator)
if err != nil {
return err
}
if outputType != inputType {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token type mismatch in inputs and outputs for approve with ID %s (%s vs %s)", txID, outputType, inputType)}
}
if outputSum != inputSum {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("token sum mismatch in inputs and outputs for approve with ID %s (%d vs %d)", txID, outputSum, inputSum)}
}
return nil
}
func (v *Verifier) commitApproveAction(approveAction *token.PlainApprove, txID string, simulator ledger.LedgerWriter) error {
if approveAction.GetOutput() != nil {
outputID, err := createOutputKey(txID, 0)
if err != nil {
return err
}
err = v.addOutput(outputID, approveAction.GetOutput(), simulator)
if err != nil {
return err
}
}
for i, delegatedOutput := range approveAction.GetDelegatedOutputs() {
// createDelegatedOutputKey() error already checked in checkDelegatedOutputsDoesNotExist
outputID, _ := createDelegatedOutputKey(txID, i)
err := v.addDelegatedOutput(outputID, delegatedOutput, simulator)
if err != nil {
return err
}
}
return v.markInputsSpent(txID, approveAction.GetInputs(), simulator)
}
func (v *Verifier) checkApproveOutputs(creator identity.PublicInfo, output *token.PlainOutput, delegatedOutputs []*token.PlainDelegatedOutput, txID string, simulator ledger.LedgerReader) (string, uint64, error) {
tokenType := ""
tokenSum := uint64(0)
// output is optional in approve tx
// check whether this tx contains an output
if output != nil {
// check that the owner is not an empty slice
if !bytes.Equal(output.Owner, creator.Public()) {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the owner of the output is not valid")}
}
tokenType = output.GetType()
err := v.checkOutputDoesNotExist(0, txID, simulator)
if err != nil {
return "", 0, err
}
// check that a spent key can be created for the output in the future
spentKey, err := createSpentKey(txID, 0)
if err != nil {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the output for approve txID '%s' is invalid: cannot create a spent key", txID)}
}
spent, err := v.isSpent(spentKey, simulator)
if err != nil {
return "", 0, errors.New(fmt.Sprintf("the output for approve txID '%s' is invalid: cannot check spent status", txID))
}
if spent {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the output for approve txID '%s' is invalid: cannot create a spent key", txID)}
}
tokenSum = output.GetQuantity()
}
// check consistency of delegated outputs
for i, delegatedOutput := range delegatedOutputs {
// check that delegated outputs have the creator as one of the owners
if !bytes.Equal(delegatedOutput.Owner, creator.Public()) {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the owner of the delegated output is invalid")}
}
// check consistency of type
if tokenType == "" {
tokenType = delegatedOutput.GetType()
} else if tokenType != delegatedOutput.GetType() {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("multiple token types ('%s', '%s') in approve outputs for txID '%s'", tokenType, delegatedOutput.GetType(), txID)}
}
// each delegated output should have one delegatees
if len(delegatedOutput.Delegatees) != 1 {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the number of delegates in approve txID '%s' is not 1, it is [%d]", txID, len(delegatedOutput.Delegatees))}
}
// check that the delegatee is not an empty slice
if len(delegatedOutput.Delegatees[0]) == 0 {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the delegated output for approve txID '%s' does not have a delegatee", txID)}
}
err := v.checkDelegatedOutputDoesNotExist(i, txID, simulator)
if err != nil {
return "", 0, errors.New(fmt.Sprintf("the delegated output for approve txID '%s' is invalid: the ID exists", txID))
}
// check that a spent key can be created for the delegate output in the future
spentKey, err := createSpentDelegatedOutputKey(txID, int(i))
if err != nil {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the delegated output for approve txID '%s' is invalid: cannot create a spent key", txID)}
}
spent, err := v.isSpent(spentKey, simulator)
if err != nil {
return "", 0, errors.New(fmt.Sprintf("the delegated output for approve txID '%s' is invalid: cannot check spent status", txID))
}
if spent {
return "", 0, &customtx.InvalidTxError{Msg: fmt.Sprintf("the delegated output for approve txID '%s' is invalid: cannot create a spent key", txID)}
}
tokenSum += delegatedOutput.GetQuantity()
}
return tokenType, tokenSum, nil
}
func (v *Verifier) addOutput(outputID string, output *token.PlainOutput, simulator ledger.LedgerWriter) error {
outputBytes := utils.MarshalOrPanic(output)
return simulator.SetState(tokenNameSpace, outputID, outputBytes)
}
func (v *Verifier) addDelegatedOutput(outputID string, delegatedOutput *token.PlainDelegatedOutput, simulator ledger.LedgerWriter) error {
outputBytes := utils.MarshalOrPanic(delegatedOutput)
return simulator.SetState(tokenNameSpace, outputID, outputBytes)
}
func (v *Verifier) addTransaction(txID string, ttx *token.TokenTransaction, simulator ledger.LedgerWriter) error {
ttxBytes := utils.MarshalOrPanic(ttx)
ttxID, err := createTxKey(txID)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating txID: %s", err)}
}
return simulator.SetState(tokenNameSpace, ttxID, ttxBytes)
}
var TokenInputSpentMarker = []byte{1}
func (v *Verifier) markInputsSpent(txID string, inputs []*token.InputId, simulator ledger.LedgerWriter) error {
for _, id := range inputs {
inputID, err := createSpentKey(id.TxId, int(id.Index))
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating spent key: %s", err)}
}
verifierLogger.Debugf("marking input '%s' as spent", inputID)
err = simulator.SetState(tokenNameSpace, inputID, TokenInputSpentMarker)
if err != nil {
return err
}
}
return nil
}
func (v *Verifier) getOutput(outputID string, simulator ledger.LedgerReader) (*token.PlainOutput, error) {
outputBytes, err := simulator.GetState(tokenNameSpace, outputID)
if err != nil {
return nil, err
}
if outputBytes == nil {
return nil, &customtx.InvalidTxError{Msg: fmt.Sprintf("input with ID %s for transfer does not exist", outputID)}
}
if len(outputBytes) == 0 {
return nil, &customtx.InvalidTxError{Msg: fmt.Sprintf("input with ID %s for transfer does not exist", outputID)}
}
output := &token.PlainOutput{}
err = proto.Unmarshal(outputBytes, output)
if err != nil {
return nil, &customtx.InvalidTxError{Msg: fmt.Sprintf("unmarshaling error: %s", err)}
}
return output, nil
}
// isSpent checks whether an output token with identifier outputID has been spent.
func (v *Verifier) isSpent(spentKey string, simulator ledger.LedgerReader) (bool, error) {
verifierLogger.Debugf("checking if input with ID '%s' has been spent", spentKey)
result, err := simulator.GetState(tokenNameSpace, spentKey)
return result != nil, err
}
// Create a ledger key for an individual output in a token transaction, as a function of
// the transaction ID, and the index of the output
func createOutputKey(txID string, index int) (string, error) {
return createCompositeKey(tokenOutput, []string{txID, strconv.Itoa(index)})
}
// Create a ledger key for a redeem output in a token transaction, as a function of
// the transaction ID, and the index of the output
func createRedeemKey(txID string, index int) (string, error) {
return createCompositeKey(tokenRedeem, []string{txID, strconv.Itoa(index)})
}
// Create a ledger key for a token transaction, as a function of the transaction ID
func createTxKey(txID string) (string, error) {
return createCompositeKey(tokenTx, []string{txID})
}
// Create a ledger key for a spent individual output in a token transaction, as a function of
// the transaction ID, and the index of the output
func createSpentKey(txID string, index int) (string, error) {
return createCompositeKey("tokenInput", []string{txID, strconv.Itoa(index)})
}
// createCompositeKey and its related functions and consts copied from core/chaincode/shim/chaincode.go
func createCompositeKey(objectType string, attributes []string) (string, error) {
if err := validateCompositeKeyAttribute(objectType); err != nil {
return "", err
}
ck := compositeKeyNamespace + objectType + string(minUnicodeRuneValue)
for _, att := range attributes {
if err := validateCompositeKeyAttribute(att); err != nil {
return "", err
}
ck += att + string(minUnicodeRuneValue)
}
return ck, nil
}
func validateCompositeKeyAttribute(str string) error {
if !utf8.ValidString(str) {
return errors.Errorf("not a valid utf8 string: [%x]", str)
}
for index, runeValue := range str {
if runeValue == minUnicodeRuneValue || runeValue == maxUnicodeRuneValue {
return errors.Errorf(`input contain unicode %#U starting at position [%d]. %#U and %#U are not allowed in the input attribute of a composite key`,
runeValue, index, minUnicodeRuneValue, maxUnicodeRuneValue)
}
}
return nil
}
func parseCompositeKeyBytes(keyBytes []byte) string {
return string(keyBytes)
}
func getCompositeKeyBytes(compositeKey string) []byte {
return []byte(compositeKey)
}
// Create a ledger key for an individual delegated output in a token transaction, as a function of
// the transaction ID, and the index of the output
func createDelegatedOutputKey(txID string, index int) (string, error) {
return createCompositeKey(tokenDelegatedOutput, []string{txID, strconv.Itoa(index)})
}
// Create a ledger key for a spent individual delegated output in a token transaction, as a function of
// the transaction ID, and the index of the delegated output
func createSpentDelegatedOutputKey(txID string, index int) (string, error) {
return createCompositeKey(tokenDelegatedInput, []string{txID, strconv.Itoa(index)})
}
func (v *Verifier) checkDelegatedOutputDoesNotExist(index int, txID string, simulator ledger.LedgerReader) error {
outputID, err := createDelegatedOutputKey(txID, index)
if err != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("error creating output ID: %s", err)}
}
existingOutputBytes, err := simulator.GetState(tokenNameSpace, outputID)
if err != nil {
return err
}
if existingOutputBytes != nil {
return &customtx.InvalidTxError{Msg: fmt.Sprintf("output already exists: %s", outputID)}
}
return nil
}
Go
1
https://gitee.com/hyperledger-fabric-gm/fabric.git
git@gitee.com:hyperledger-fabric-gm/fabric.git
hyperledger-fabric-gm
fabric
fabric
v1.4.9

搜索帮助