github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/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 if _, err := proto.Marshal(cds); err != nil { 131 return nil, nil, nil, err 132 } 133 134 //compute hashes now 135 // hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{}) 136 hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{}) 137 if err != nil { 138 return nil, nil, nil, err 139 } 140 141 cdsdata := &CDSData{} 142 143 //code hash 144 hash.Write(cds.CodePackage) 145 cdsdata.CodeHash = hash.Sum(nil) 146 147 hash.Reset() 148 149 //metadata hash 150 hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name)) 151 hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version)) 152 153 cdsdata.MetaDataHash = hash.Sum(nil) 154 155 b, err := proto.Marshal(cdsdata) 156 if err != nil { 157 return nil, nil, nil, err 158 } 159 160 hash.Reset() 161 162 //compute the id 163 hash.Write(cdsdata.CodeHash) 164 hash.Write(cdsdata.MetaDataHash) 165 166 id := hash.Sum(nil) 167 168 return b, id, cdsdata, nil 169 } 170 171 // ValidateCC returns error if the chaincode is not found or if its not a 172 // ChaincodeDeploymentSpec 173 func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error { 174 if ccpack.depSpec == nil { 175 return fmt.Errorf("uninitialized package") 176 } 177 178 if ccpack.data == nil { 179 return fmt.Errorf("nil data") 180 } 181 182 // This is a hack. LSCC expects a specific LSCC error when names are invalid so it 183 // has its own validation code. We can't use that error because of import cycles. 184 // Unfortunately, we also need to check if what have makes some sort of sense as 185 // protobuf will gladly deserialize garbage and there are paths where we assume that 186 // a successful unmarshal means everything works but, if it fails, we try to unmarshal 187 // into something different. 188 if !isPrintable(ccdata.Name) { 189 return fmt.Errorf("invalid chaincode name: %q", ccdata.Name) 190 } 191 192 if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version { 193 return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId) 194 } 195 196 otherdata := &CDSData{} 197 err := proto.Unmarshal(ccdata.Data, otherdata) 198 if err != nil { 199 return err 200 } 201 202 if !ccpack.data.Equals(otherdata) { 203 return fmt.Errorf("data mismatch") 204 } 205 206 return nil 207 } 208 209 //InitFromBuffer sets the buffer if valid and returns ChaincodeData 210 func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) { 211 212 depSpec := &pb.ChaincodeDeploymentSpec{} 213 err := proto.Unmarshal(buf, depSpec) 214 if err != nil { 215 return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes") 216 } 217 218 databytes, id, data, err := ccpack.getCDSData(depSpec) 219 if err != nil { 220 return nil, err 221 } 222 223 ccpack.buf = buf 224 ccpack.depSpec = depSpec 225 ccpack.data = data 226 ccpack.datab = databytes 227 ccpack.id = id 228 229 return ccpack.GetChaincodeData(), nil 230 } 231 232 //InitFromFS returns the chaincode and its package from the file system 233 func (ccpack *CDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) { 234 235 buf, err := GetChaincodePackageFromPath(ccNameVersion, path) 236 if err != nil { 237 return nil, nil, err 238 } 239 240 ccdata, err := ccpack.InitFromBuffer(buf) 241 if err != nil { 242 return nil, nil, err 243 } 244 245 if err := ccpack.ValidateCC(ccdata); err != nil { 246 return nil, nil, err 247 } 248 249 return ccpack.buf, ccpack.depSpec, nil 250 } 251 252 //InitFromFS returns the chaincode and its package from the file system 253 func (ccpack *CDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) { 254 return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath) 255 } 256 257 //PutChaincodeToFS - serializes chaincode to a package on the file system 258 func (ccpack *CDSPackage) PutChaincodeToFS() error { 259 if ccpack.buf == nil { 260 return fmt.Errorf("uninitialized package") 261 } 262 263 if ccpack.id == nil { 264 return fmt.Errorf("id cannot be nil if buf is not nil") 265 } 266 267 if ccpack.depSpec == nil { 268 return fmt.Errorf("depspec cannot be nil if buf is not nil") 269 } 270 271 if ccpack.data == nil { 272 return fmt.Errorf("nil data") 273 } 274 275 if ccpack.datab == nil { 276 return fmt.Errorf("nil data bytes") 277 } 278 279 ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name 280 ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version 281 282 //return error if chaincode exists 283 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 284 if _, err := os.Stat(path); err == nil { 285 return fmt.Errorf("chaincode %s exists", path) 286 } 287 288 if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil { 289 return err 290 } 291 292 return nil 293 }