github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/core/common/ccprovider/cdspackage.go (about) 1 /* 2 Copyright IBM Corp. 2017 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 ccprovider 18 19 import ( 20 "bytes" 21 "fmt" 22 "hash" 23 "io/ioutil" 24 "os" 25 26 "github.com/golang/protobuf/proto" 27 pb "github.com/hyperledger/fabric-protos-go/peer" 28 "github.com/hyperledger/fabric/bccsp" 29 ) 30 31 //----- CDSData ------ 32 33 //CDSData is data stored in the LSCC on instantiation of a CC 34 //for CDSPackage. This needs to be serialized for ChaincodeData 35 //hence the protobuf format 36 type CDSData struct { 37 //CodeHash hash of CodePackage from ChaincodeDeploymentSpec 38 CodeHash []byte `protobuf:"bytes,1,opt,name=codehash,proto3"` 39 40 //MetaDataHash hash of Name and Version from ChaincodeDeploymentSpec 41 MetaDataHash []byte `protobuf:"bytes,2,opt,name=metadatahash,proto3"` 42 } 43 44 //----implement functions needed from proto.Message for proto's mar/unmarshal functions 45 46 //Reset resets 47 func (data *CDSData) Reset() { *data = CDSData{} } 48 49 //String converts to string 50 func (data *CDSData) String() string { return proto.CompactTextString(data) } 51 52 //ProtoMessage just exists to make proto happy 53 func (*CDSData) ProtoMessage() {} 54 55 //Equals data equals other 56 func (data *CDSData) Equals(other *CDSData) bool { 57 return other != nil && bytes.Equal(data.CodeHash, other.CodeHash) && bytes.Equal(data.MetaDataHash, other.MetaDataHash) 58 } 59 60 // GetHasher interface defines a subset of bccsp which contains GetHash function. 61 type GetHasher interface { 62 GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) 63 } 64 65 //--------- CDSPackage ------------ 66 67 //CDSPackage encapsulates ChaincodeDeploymentSpec. 68 type CDSPackage struct { 69 buf []byte 70 depSpec *pb.ChaincodeDeploymentSpec 71 data *CDSData 72 datab []byte 73 id []byte 74 GetHasher GetHasher 75 } 76 77 // GetId gets the fingerprint of the chaincode based on package computation 78 func (ccpack *CDSPackage) GetId() []byte { 79 //this has to be after creating a package and initializing it 80 //If those steps fail, GetId() should never be called 81 if ccpack.id == nil { 82 panic("GetId called on uninitialized package") 83 } 84 return ccpack.id 85 } 86 87 // GetDepSpec gets the ChaincodeDeploymentSpec from the package 88 func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec { 89 //this has to be after creating a package and initializing it 90 //If those steps fail, GetDepSpec() should never be called 91 if ccpack.depSpec == nil { 92 panic("GetDepSpec called on uninitialized package") 93 } 94 return ccpack.depSpec 95 } 96 97 // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package 98 func (ccpack *CDSPackage) GetDepSpecBytes() []byte { 99 //this has to be after creating a package and initializing it 100 //If those steps fail, GetDepSpecBytes() should never be called 101 if ccpack.buf == nil { 102 panic("GetDepSpecBytes called on uninitialized package") 103 } 104 return ccpack.buf 105 } 106 107 // GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message 108 func (ccpack *CDSPackage) GetPackageObject() proto.Message { 109 return ccpack.depSpec 110 } 111 112 // GetChaincodeData gets the ChaincodeData 113 func (ccpack *CDSPackage) GetChaincodeData() *ChaincodeData { 114 //this has to be after creating a package and initializing it 115 //If those steps fail, GetChaincodeData() should never be called 116 if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil { 117 panic("GetChaincodeData called on uninitialized package") 118 } 119 return &ChaincodeData{Name: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name, Version: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version, Data: ccpack.datab, Id: ccpack.id} 120 } 121 122 func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, []byte, *CDSData, error) { 123 // check for nil argument. It is an assertion that getCDSData 124 // is never called on a package that did not go through/succeed 125 // package initialization. 126 if cds == nil { 127 panic("nil cds") 128 } 129 130 b, err := proto.Marshal(cds) 131 if err != nil { 132 return nil, nil, nil, err 133 } 134 135 //compute hashes now 136 // hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{}) 137 hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{}) 138 if err != nil { 139 return nil, nil, nil, err 140 } 141 142 cdsdata := &CDSData{} 143 144 //code hash 145 hash.Write(cds.CodePackage) 146 cdsdata.CodeHash = hash.Sum(nil) 147 148 hash.Reset() 149 150 //metadata hash 151 hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name)) 152 hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version)) 153 154 cdsdata.MetaDataHash = hash.Sum(nil) 155 156 b, err = proto.Marshal(cdsdata) 157 if err != nil { 158 return nil, nil, nil, err 159 } 160 161 hash.Reset() 162 163 //compute the id 164 hash.Write(cdsdata.CodeHash) 165 hash.Write(cdsdata.MetaDataHash) 166 167 id := hash.Sum(nil) 168 169 return b, id, cdsdata, nil 170 } 171 172 // ValidateCC returns error if the chaincode is not found or if its not a 173 // ChaincodeDeploymentSpec 174 func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error { 175 if ccpack.depSpec == nil { 176 return fmt.Errorf("uninitialized package") 177 } 178 179 if ccpack.data == nil { 180 return fmt.Errorf("nil data") 181 } 182 183 // This is a hack. LSCC expects a specific LSCC error when names are invalid so it 184 // has its own validation code. We can't use that error because of import cycles. 185 // Unfortunately, we also need to check if what have makes some sort of sense as 186 // protobuf will gladly deserialize garbage and there are paths where we assume that 187 // a successful unmarshal means everything works but, if it fails, we try to unmarshal 188 // into something different. 189 if !isPrintable(ccdata.Name) { 190 return fmt.Errorf("invalid chaincode name: %q", ccdata.Name) 191 } 192 193 if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version { 194 return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId) 195 } 196 197 otherdata := &CDSData{} 198 err := proto.Unmarshal(ccdata.Data, otherdata) 199 if err != nil { 200 return err 201 } 202 203 if !ccpack.data.Equals(otherdata) { 204 return fmt.Errorf("data mismatch") 205 } 206 207 return nil 208 } 209 210 //InitFromBuffer sets the buffer if valid and returns ChaincodeData 211 func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) { 212 213 depSpec := &pb.ChaincodeDeploymentSpec{} 214 err := proto.Unmarshal(buf, depSpec) 215 if err != nil { 216 return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes") 217 } 218 219 databytes, id, data, err := ccpack.getCDSData(depSpec) 220 if err != nil { 221 return nil, err 222 } 223 224 ccpack.buf = buf 225 ccpack.depSpec = depSpec 226 ccpack.data = data 227 ccpack.datab = databytes 228 ccpack.id = id 229 230 return ccpack.GetChaincodeData(), nil 231 } 232 233 //InitFromFS returns the chaincode and its package from the file system 234 func (ccpack *CDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) { 235 236 buf, err := GetChaincodePackageFromPath(ccNameVersion, path) 237 if err != nil { 238 return nil, nil, err 239 } 240 241 ccdata, err := ccpack.InitFromBuffer(buf) 242 if err != nil { 243 return nil, nil, err 244 } 245 246 if err := ccpack.ValidateCC(ccdata); err != nil { 247 return nil, nil, err 248 } 249 250 return ccpack.buf, ccpack.depSpec, nil 251 } 252 253 //InitFromFS returns the chaincode and its package from the file system 254 func (ccpack *CDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) { 255 return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath) 256 } 257 258 //PutChaincodeToFS - serializes chaincode to a package on the file system 259 func (ccpack *CDSPackage) PutChaincodeToFS() error { 260 if ccpack.buf == nil { 261 return fmt.Errorf("uninitialized package") 262 } 263 264 if ccpack.id == nil { 265 return fmt.Errorf("id cannot be nil if buf is not nil") 266 } 267 268 if ccpack.depSpec == nil { 269 return fmt.Errorf("depspec cannot be nil if buf is not nil") 270 } 271 272 if ccpack.data == nil { 273 return fmt.Errorf("nil data") 274 } 275 276 if ccpack.datab == nil { 277 return fmt.Errorf("nil data bytes") 278 } 279 280 ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name 281 ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version 282 283 //return error if chaincode exists 284 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 285 if _, err := os.Stat(path); err == nil { 286 return fmt.Errorf("chaincode %s exists", path) 287 } 288 289 if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil { 290 return err 291 } 292 293 return nil 294 }