github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/examples/chaincode/go/utxo/util/utxo.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "crypto/sha256" 21 "encoding/hex" 22 "fmt" 23 "math" 24 25 "github.com/hyperledger/fabric/examples/chaincode/go/utxo/consensus" 26 ) 27 28 // UTXO includes the storage for the chaincode API or an in memory 29 // store for testing 30 type UTXO struct { 31 Store Store 32 } 33 34 // MakeUTXO constructs a new UTXO with the given store 35 func MakeUTXO(store Store) *UTXO { 36 utxo := &UTXO{} 37 utxo.Store = store 38 return utxo 39 } 40 41 // Key represents the key for a transaction in storage. It has both a 42 // hash and index 43 type Key struct { 44 TxHashAsHex string 45 TxIndex uint32 46 } 47 48 // GetTransactionHash returns the Bitcoin hash (double sha256) of 49 // the given transaction 50 func (u *UTXO) GetTransactionHash(txData []byte) [32]byte { 51 firstHash := sha256.Sum256(txData) 52 txHash := sha256.Sum256(firstHash[:]) 53 return txHash 54 } 55 56 // IsCoinbase returns true if this is a coinbase transaction, false otherwise 57 func (u *UTXO) IsCoinbase(index uint32) bool { 58 return index == math.MaxUint32 59 } 60 61 var mandatoryFlags = consensus.Verify_flags_p2sh 62 63 var standardFlags = mandatoryFlags | 64 consensus.Verify_flags_dersig | 65 consensus.Verify_flags_strictenc | 66 consensus.Verify_flags_minimaldata | 67 consensus.Verify_flags_nulldummy | 68 consensus.Verify_flags_discourage_upgradable_nops | 69 consensus.Verify_flags_cleanstack | 70 consensus.Verify_flags_checklocktimeverify | 71 consensus.Verify_flags_low_s 72 73 // ExecResult is the result of processing a transaction 74 type ExecResult struct { 75 SumCurrentOutputs uint64 76 SumPriorOutputs uint64 77 IsCoinbase bool 78 } 79 80 // Execute processes the given transaction and outputs a result 81 func (u *UTXO) Execute(txData []byte) (*ExecResult, error) { 82 newTX := ParseUTXOBytes(txData) 83 txHash := u.GetTransactionHash(txData) 84 execResult := &ExecResult{} 85 // Loop through outputs first 86 for index, output := range newTX.Txout { 87 currKey := &Key{TxHashAsHex: hex.EncodeToString(txHash[:]), TxIndex: uint32(index)} 88 _, ok, err := u.Store.GetState(*currKey) 89 if err != nil { 90 return nil, fmt.Errorf("Error getting state from store: %s", err) 91 } 92 if ok == true { 93 // COLLISION 94 return nil, fmt.Errorf("COLLISION detected for key = %v, with output script length = %d", currKey, len(output.Script)) 95 } 96 // Store the output in utxo 97 u.Store.PutState(*currKey, &TX_TXOUT{Script: output.Script, Value: output.Value}) 98 execResult.SumCurrentOutputs += output.Value 99 } 100 // Now loop over inputs, 101 for index, input := range newTX.Txin { 102 prevTxHash := input.SourceHash 103 prevOutputIx := input.Ix 104 if u.IsCoinbase(prevOutputIx) { 105 execResult.IsCoinbase = true 106 //fmt.Println("COINBASE!!") 107 } else { 108 //fmt.Println("NOT COINBASE!!") 109 // NOT coinbase, need to verify 110 keyToPrevOutput := &Key{TxHashAsHex: hex.EncodeToString(prevTxHash), TxIndex: prevOutputIx} 111 value, ok, err := u.Store.GetState(*keyToPrevOutput) 112 if err != nil { 113 return nil, fmt.Errorf("Error getting state from store: %s", err) 114 } 115 if !ok { 116 // Previous output not fouund, 117 return nil, fmt.Errorf("Could not find previous transaction output with key = %v", keyToPrevOutput) 118 } 119 // Call Verify_script 120 txInputIndex := uint(index) 121 result := consensus.Verify_script(&txData[0], int64(len(txData)), &value.Script[0], int64(len(value.Script)), txInputIndex, uint(standardFlags)) 122 if result != consensus.Verify_result_eval_true { 123 result = consensus.Verify_script(&txData[0], int64(len(txData)), &value.Script[0], int64(len(value.Script)), txInputIndex, uint(mandatoryFlags)) 124 if result != consensus.Verify_result_eval_true { 125 return nil, fmt.Errorf("Unexpected result from verify_script, expected %d, got %d, perhaps it is %d", consensus.Verify_result_eval_true, result, consensus.Verify_result_invalid_stack_operation) 126 } 127 } 128 // Verified, now remove prior outputs 129 u.Store.DelState(*keyToPrevOutput) 130 execResult.SumPriorOutputs += value.Value 131 } 132 133 hex := hex.EncodeToString(txHash[:]) 134 fmt.Printf("PUT TRAN %s", hex) 135 u.Store.PutTran(hex, txData) 136 } 137 return execResult, nil 138 } 139 140 // Query search the storage for a given transaction hash 141 func (u *UTXO) Query(txHashHex string) pb.Response { 142 tx, _, err := u.Store.GetTran(txHashHex) 143 return tx, err 144 }