├── .gitignore ├── .gitmodules ├── .travis.yml ├── README.md ├── fabric-ccenv-netcore ├── Dockerfile └── build.sh └── src ├── Chaincode.NET ├── Chaincode.NET.Protos │ ├── Chaincode.NET.Protos.csproj │ ├── Chaincode.cs │ ├── ChaincodeEvent.cs │ ├── ChaincodeShim.cs │ ├── ChaincodeShimGrpc.cs │ ├── Common.cs │ ├── Identities.cs │ ├── KvQueryResult.cs │ ├── Proposal.cs │ └── ProposalResponse.cs ├── Chaincode.NET.Sample │ ├── AssetHolding.cs │ ├── Chaincode.NET.Sample.csproj │ ├── FabCar.cs │ └── Program.cs ├── Chaincode.NET.Test │ ├── Chaincode.NET.Test.csproj │ ├── Chaincode │ │ ├── ChaincodeStubTest.cs │ │ └── ClientIdentityTest.cs │ ├── Handler │ │ ├── HandlerTest.cs │ │ ├── IteratorTest.cs │ │ └── ShimTest.cs │ ├── Messaging │ │ ├── MessageQueueTest.cs │ │ └── QueueMessageTest.cs │ └── Settings │ │ └── ChaincodeSettingsTest.cs ├── Chaincode.NET.sln ├── Chaincode.NET.sln.DotSettings └── Chaincode.NET │ ├── Chaincode.NET.csproj │ ├── Chaincode │ ├── ChaincodeFunctionParameterInformation.cs │ ├── ChaincodeInvocationMap.cs │ ├── ChaincodeStub.cs │ ├── ClientIdentity.cs │ ├── IChaincode.cs │ ├── IChaincodeStub.cs │ ├── IChaincodeStubFactory.cs │ ├── IClientIdentity.cs │ └── ResponseCodes.cs │ ├── Extensions │ ├── ByteArrayExtensions.cs │ ├── ByteStringExtensions.cs │ ├── ChaincodeFunctionParameterInformationExtensions.cs │ ├── ChaincodeStubExtensions.cs │ ├── IntExtensions.cs │ ├── StringExtensions.cs │ └── TaskExtensions.cs │ ├── Handler │ ├── ChaincodeSupportClientFactory.cs │ ├── Handler.cs │ ├── HandlerFactory.cs │ ├── IChaincodeSupportClientFactory.cs │ ├── IHandler.cs │ ├── IHandlerFactory.cs │ ├── Iterators │ │ ├── CommonIterator.cs │ │ ├── HistoryQueryIterator.cs │ │ ├── IteratorTypes.cs │ │ ├── QueryResult.cs │ │ └── StateQueryIterator.cs │ ├── Shim.cs │ └── States.cs │ ├── Messaging │ ├── IMessageQueue.cs │ ├── IMessageQueueFactory.cs │ ├── MessageMethod.cs │ ├── MessageQueue.cs │ ├── MessageQueueFactory.cs │ └── QueueMessage.cs │ ├── ProviderConfiguration.cs │ ├── README.md │ └── Settings │ └── ChaincodeSettings.cs └── generate_proto.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | src/protos 3 | /package 4 | 5 | 6 | # Created by https://www.gitignore.io/api/aspnetcore 7 | 8 | ### ASPNETCore ### 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.userosscache 16 | *.sln.docstates 17 | 18 | # User-specific files (MonoDevelop/Xamarin Studio) 19 | *.userprefs 20 | 21 | # Build results 22 | [Dd]ebug/ 23 | [Dd]ebugPublic/ 24 | [Rr]elease/ 25 | [Rr]eleases/ 26 | x64/ 27 | x86/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # DNX 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | *_i.c 57 | *_p.c 58 | *_i.h 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.svclog 79 | *.scc 80 | 81 | # Chutzpah Test files 82 | _Chutzpah* 83 | 84 | # Visual C++ cache files 85 | ipch/ 86 | *.aps 87 | *.ncb 88 | *.opendb 89 | *.opensdf 90 | *.sdf 91 | *.cachefile 92 | *.VC.db 93 | *.VC.VC.opendb 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | *.vspx 99 | *.sap 100 | 101 | # TFS 2012 Local Workspace 102 | $tf/ 103 | 104 | # Guidance Automation Toolkit 105 | *.gpState 106 | 107 | # ReSharper is a .NET coding add-in 108 | _ReSharper*/ 109 | *.[Rr]e[Ss]harper 110 | *.DotSettings.user 111 | 112 | # JustCode is a .NET coding add-in 113 | .JustCode 114 | 115 | # TeamCity is a build add-in 116 | _TeamCity* 117 | 118 | # DotCover is a Code Coverage Tool 119 | *.dotCover 120 | 121 | # Visual Studio code coverage results 122 | *.coverage 123 | *.coveragexml 124 | 125 | # NCrunch 126 | _NCrunch_* 127 | .*crunch*.local.xml 128 | nCrunchTemp_* 129 | 130 | # MightyMoose 131 | *.mm.* 132 | AutoTest.Net/ 133 | 134 | # Web workbench (sass) 135 | .sass-cache/ 136 | 137 | # Installshield output folder 138 | [Ee]xpress/ 139 | 140 | # DocProject is a documentation generator add-in 141 | DocProject/buildhelp/ 142 | DocProject/Help/*.HxT 143 | DocProject/Help/*.HxC 144 | DocProject/Help/*.hhc 145 | DocProject/Help/*.hhk 146 | DocProject/Help/*.hhp 147 | DocProject/Help/Html2 148 | DocProject/Help/html 149 | 150 | # Click-Once directory 151 | publish/ 152 | 153 | # Publish Web Output 154 | *.[Pp]ublish.xml 155 | *.azurePubxml 156 | # TODO: Comment the next line if you want to checkin your web deploy settings 157 | # but database connection strings (with potential passwords) will be unencrypted 158 | *.pubxml 159 | *.publishproj 160 | 161 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 162 | # checkin your Azure Web App publish settings, but sensitive information contained 163 | # in these scripts will be unencrypted 164 | PublishScripts/ 165 | 166 | # NuGet Packages 167 | *.nupkg 168 | # The packages folder can be ignored because of Package Restore 169 | **/packages/* 170 | # except build/, which is used as an MSBuild target. 171 | !**/packages/build/ 172 | # Uncomment if necessary however generally it will be regenerated when needed 173 | #!**/packages/repositories.config 174 | # NuGet v3's project.json files produces more ignoreable files 175 | *.nuget.props 176 | *.nuget.targets 177 | 178 | # Microsoft Azure Build Output 179 | csx/ 180 | *.build.csdef 181 | 182 | # Microsoft Azure Emulator 183 | ecf/ 184 | rcf/ 185 | 186 | # Windows Store app package directories and files 187 | AppPackages/ 188 | BundleArtifacts/ 189 | Package.StoreAssociation.xml 190 | _pkginfo.txt 191 | 192 | # Visual Studio cache files 193 | # files ending in .cache can be ignored 194 | *.[Cc]ache 195 | # but keep track of directories ending in .cache 196 | !*.[Cc]ache/ 197 | 198 | # Others 199 | ClientBin/ 200 | ~$* 201 | *~ 202 | *.dbmdl 203 | *.dbproj.schemaview 204 | *.jfm 205 | *.pfx 206 | *.publishsettings 207 | node_modules/ 208 | orleans.codegen.cs 209 | 210 | # Since there are multiple workflows, uncomment next line to ignore bower_components 211 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 212 | #bower_components/ 213 | 214 | # RIA/Silverlight projects 215 | Generated_Code/ 216 | 217 | # Backup & report files from converting an old project file 218 | # to a newer Visual Studio version. Backup files are not needed, 219 | # because we have git ;-) 220 | _UpgradeReport_Files/ 221 | Backup*/ 222 | UpgradeLog*.XML 223 | UpgradeLog*.htm 224 | 225 | # SQL Server files 226 | *.mdf 227 | *.ldf 228 | 229 | # Business Intelligence projects 230 | *.rdl.data 231 | *.bim.layout 232 | *.bim_*.settings 233 | 234 | # Microsoft Fakes 235 | FakesAssemblies/ 236 | 237 | # GhostDoc plugin setting file 238 | *.GhostDoc.xml 239 | 240 | # Node.js Tools for Visual Studio 241 | .ntvs_analysis.dat 242 | 243 | # Visual Studio 6 build log 244 | *.plg 245 | 246 | # Visual Studio 6 workspace options file 247 | *.opt 248 | 249 | # Visual Studio LightSwitch build output 250 | **/*.HTMLClient/GeneratedArtifacts 251 | **/*.DesktopClient/GeneratedArtifacts 252 | **/*.DesktopClient/ModelManifest.xml 253 | **/*.Server/GeneratedArtifacts 254 | **/*.Server/ModelManifest.xml 255 | _Pvt_Extensions 256 | 257 | # Paket dependency manager 258 | .paket/paket.exe 259 | paket-files/ 260 | 261 | # FAKE - F# Make 262 | .fake/ 263 | 264 | # JetBrains Rider 265 | .idea/ 266 | *.sln.iml 267 | 268 | # CodeRush 269 | .cr/ 270 | 271 | # Python Tools for Visual Studio (PTVS) 272 | __pycache__/ 273 | *.pyc 274 | 275 | # Cake - Uncomment if you are using it 276 | # tools/ 277 | 278 | 279 | # End of https://www.gitignore.io/api/aspnetcore -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thinktecture/fabric-chaincode-netcore/1376f0e23b2c2666867baff701456053166a0963/.gitmodules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: none 3 | dotnet: 2.1.302 4 | go: master 5 | # Travis only supports Ubuntu 14 which has a very old openversion from 2014. 6 | # Updating the package was not possible due to a dependency error at postgres-client. 7 | os: osx 8 | script: 9 | # Ignore the error on go get, since the protos don't include a Go file 10 | - go get -d github.com/hyperledger/fabric/protos || true 11 | - dotnet build src/Chaincode.NET/Chaincode.NET/Chaincode.NET.csproj --configuration Release 12 | - dotnet test src/Chaincode.NET/Chaincode.NET.Test/Chaincode.NET.Test.csproj --configuration Release 13 | - dotnet pack src/Chaincode.NET/Chaincode.NET/Chaincode.NET.csproj --configuration Release --output ../../../package -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Thinktecture Hyperledger Fabric Chaincode .NET Adapter 2 | 3 | With this package you are able to build chaincode (aka "Smart Contracts") for [Hyperledger Fabric](https://hyperledger.org/projects/fabric) using .NET Core. 4 | 5 | ## Usage 6 | 7 | ## Development 8 | 9 | The following instructions are meant for developers of the Chaincode.NET package. 10 | 11 | ### Folder structure 12 | 13 | #### fabric-ccenv-netcore 14 | 15 | The `fabric-ccenv-netcore` folder contains an adoption of the original [fabric-ccenv](https://hub.docker.com/r/hyperledger/fabric-ccenv/) which additionally installs .NET Core to run the chaincode within a Docker environment. 16 | 17 | #### src 18 | 19 | The `src` folder contains the code for the NuGet package. 20 | 21 | ### Building 22 | 23 | #### Requirements 24 | 25 | * [.NET Core 2.1 SDK](https://www.microsoft.com/net/download) 26 | * [Golang](https://golang.org/dl/) 27 | 28 | #### Build Steps 29 | 30 | In order to build the source folder, please follow the steps: 31 | 32 | 1. Open a terminal within the `Chaincode.NET` folder and run `dotnet restore` to restore all the packages 33 | 2. Make sure, you have Golang and the proto files for Hyperledger Fabric installed: `go get github.com/hyperledger/fabric/protos` 34 | 3. Run `generate_protos.sh` within the `src` folder. It will generate the C# classes for Hyperledger Fabric's Protofiles. 35 | 4. Open the Project with JetBrains Rider (preferred) or Visual Studio 36 | 5. Build Chaincode.NET 37 | 38 | ### Testing 39 | 40 | TODO 41 | -------------------------------------------------------------------------------- /fabric-ccenv-netcore/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hyperledger/fabric-ccenv:latest 2 | 3 | # https://github.com/docker-library/buildpack-deps/blob/master/xenial/curl/Dockerfile 4 | RUN apt-get update && apt-get install -y --no-install-recommends \ 5 | ca-certificates \ 6 | curl \ 7 | netbase \ 8 | wget \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | RUN set -ex; \ 12 | if ! command -v gpg > /dev/null; then \ 13 | apt-get update; \ 14 | apt-get install -y --no-install-recommends \ 15 | gnupg \ 16 | dirmngr \ 17 | ; \ 18 | rm -rf /var/lib/apt/lists/*; \ 19 | fi 20 | 21 | # https://github.com/docker-library/buildpack-deps/blob/master/xenial/scm/Dockerfile 22 | RUN apt-get update && apt-get install -y --no-install-recommends \ 23 | bzr \ 24 | git \ 25 | mercurial \ 26 | openssh-client \ 27 | subversion \ 28 | \ 29 | procps \ 30 | && rm -rf /var/lib/apt/lists/* 31 | 32 | # MS xenial dockerfile: https://github.com/docker-library/buildpack-deps/blob/2da658b9a1b91fa61d63ffad2ea52685cac6c702/xenial/Dockerfile 33 | RUN set -ex; \ 34 | apt-get update; \ 35 | apt-get install -y --no-install-recommends \ 36 | autoconf \ 37 | automake \ 38 | bzip2 \ 39 | dpkg-dev \ 40 | file \ 41 | g++ \ 42 | gcc \ 43 | imagemagick \ 44 | libbz2-dev \ 45 | libc6-dev \ 46 | libcurl4-openssl-dev \ 47 | libdb-dev \ 48 | libevent-dev \ 49 | libffi-dev \ 50 | libgdbm-dev \ 51 | libgeoip-dev \ 52 | libglib2.0-dev \ 53 | libjpeg-dev \ 54 | libkrb5-dev \ 55 | liblzma-dev \ 56 | libmagickcore-dev \ 57 | libmagickwand-dev \ 58 | libncurses5-dev \ 59 | libncursesw5-dev \ 60 | libpng-dev \ 61 | libpq-dev \ 62 | libreadline-dev \ 63 | libsqlite3-dev \ 64 | libssl-dev \ 65 | libtool \ 66 | libwebp-dev \ 67 | libxml2-dev \ 68 | libxslt-dev \ 69 | libyaml-dev \ 70 | make \ 71 | patch \ 72 | xz-utils \ 73 | zlib1g-dev \ 74 | \ 75 | # https://lists.debian.org/debian-devel-announce/2016/09/msg00000.html 76 | $( \ 77 | # if we use just "apt-cache show" here, it returns zero because "Can't select versions from package 'libmysqlclient-dev' as it is purely virtual", hence the pipe to grep 78 | if apt-cache show 'default-libmysqlclient-dev' 2>/dev/null | grep -q '^Version:'; then \ 79 | echo 'default-libmysqlclient-dev'; \ 80 | else \ 81 | echo 'libmysqlclient-dev'; \ 82 | fi \ 83 | ) \ 84 | ; \ 85 | rm -rf /var/lib/apt/lists/* 86 | 87 | # Install .NET Core SDK 88 | ENV DOTNET_SDK_VERSION 2.1.301 89 | 90 | RUN curl -SL --output dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \ 91 | && dotnet_sha512='2101df5b1ca8a4a67f239c65080112a69fb2b48c1a121f293bfb18be9928f7cfbf2d38ed720cbf39c9c04734f505c360bb2835fa5f6200e4d763bd77b47027da' \ 92 | && sha512sum dotnet.tar.gz \ 93 | && echo "$dotnet_sha512 dotnet.tar.gz" | sha512sum -c - \ 94 | && mkdir -p /usr/share/dotnet \ 95 | && tar -zxf dotnet.tar.gz -C /usr/share/dotnet \ 96 | && rm dotnet.tar.gz \ 97 | && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet 98 | 99 | # Enable detection of running in a container 100 | ENV DOTNET_RUNNING_IN_CONTAINER=true \ 101 | # Enable correct mode for dotnet watch (only mode supported in a container) 102 | DOTNET_USE_POLLING_FILE_WATCHER=true \ 103 | # Skip extraction of XML docs - generally not useful within an image/container - helps perfomance 104 | NUGET_XMLDOC_MODE=skip 105 | -------------------------------------------------------------------------------- /fabric-ccenv-netcore/build.sh: -------------------------------------------------------------------------------- 1 | docker build . -t mr/fabric-ccenv-netcore 2 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/Chaincode.NET.Protos.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 7.3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/ChaincodeEvent.cs: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: peer/chaincode_event.proto 3 | #pragma warning disable 1591, 0612, 3021 4 | #region Designer generated code 5 | 6 | using pb = global::Google.Protobuf; 7 | using pbr = global::Google.Protobuf.Reflection; 8 | 9 | namespace Chaincode.NET.Protos { 10 | 11 | /// Holder for reflection information generated from peer/chaincode_event.proto 12 | public static partial class ChaincodeEventReflection { 13 | 14 | #region Descriptor 15 | /// File descriptor for peer/chaincode_event.proto 16 | public static pbr::FileDescriptor Descriptor { 17 | get { return descriptor; } 18 | } 19 | private static pbr::FileDescriptor descriptor; 20 | 21 | static ChaincodeEventReflection() { 22 | byte[] descriptorData = global::System.Convert.FromBase64String( 23 | string.Concat( 24 | "ChpwZWVyL2NoYWluY29kZV9ldmVudC5wcm90bxIGcHJvdG9zIloKDkNoYWlu", 25 | "Y29kZUV2ZW50EhQKDGNoYWluY29kZV9pZBgBIAEoCRINCgV0eF9pZBgCIAEo", 26 | "CRISCgpldmVudF9uYW1lGAMgASgJEg8KB3BheWxvYWQYBCABKAxCZgoib3Jn", 27 | "Lmh5cGVybGVkZ2VyLmZhYnJpYy5wcm90b3MucGVlckIVQ2hhaW5jb2RlRXZl", 28 | "bnRQYWNrYWdlWilnaXRodWIuY29tL2h5cGVybGVkZ2VyL2ZhYnJpYy9wcm90", 29 | "b3MvcGVlcmIGcHJvdG8z")); 30 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 31 | new pbr::FileDescriptor[] { }, 32 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 33 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.ChaincodeEvent), global::Chaincode.NET.Protos.ChaincodeEvent.Parser, new[]{ "ChaincodeId", "TxId", "EventName", "Payload" }, null, null, null) 34 | })); 35 | } 36 | #endregion 37 | 38 | } 39 | #region Messages 40 | /// 41 | ///ChaincodeEvent is used for events and registrations that are specific to chaincode 42 | ///string type - "chaincode" 43 | /// 44 | public sealed partial class ChaincodeEvent : pb::IMessage { 45 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ChaincodeEvent()); 46 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 47 | public static pb::MessageParser Parser { get { return _parser; } } 48 | 49 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 50 | public static pbr::MessageDescriptor Descriptor { 51 | get { return global::Chaincode.NET.Protos.ChaincodeEventReflection.Descriptor.MessageTypes[0]; } 52 | } 53 | 54 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 55 | pbr::MessageDescriptor pb::IMessage.Descriptor { 56 | get { return Descriptor; } 57 | } 58 | 59 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 60 | public ChaincodeEvent() { 61 | OnConstruction(); 62 | } 63 | 64 | partial void OnConstruction(); 65 | 66 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 67 | public ChaincodeEvent(ChaincodeEvent other) : this() { 68 | chaincodeId_ = other.chaincodeId_; 69 | txId_ = other.txId_; 70 | eventName_ = other.eventName_; 71 | payload_ = other.payload_; 72 | } 73 | 74 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 75 | public ChaincodeEvent Clone() { 76 | return new ChaincodeEvent(this); 77 | } 78 | 79 | /// Field number for the "chaincode_id" field. 80 | public const int ChaincodeIdFieldNumber = 1; 81 | private string chaincodeId_ = ""; 82 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 83 | public string ChaincodeId { 84 | get { return chaincodeId_; } 85 | set { 86 | chaincodeId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 87 | } 88 | } 89 | 90 | /// Field number for the "tx_id" field. 91 | public const int TxIdFieldNumber = 2; 92 | private string txId_ = ""; 93 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 94 | public string TxId { 95 | get { return txId_; } 96 | set { 97 | txId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 98 | } 99 | } 100 | 101 | /// Field number for the "event_name" field. 102 | public const int EventNameFieldNumber = 3; 103 | private string eventName_ = ""; 104 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 105 | public string EventName { 106 | get { return eventName_; } 107 | set { 108 | eventName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 109 | } 110 | } 111 | 112 | /// Field number for the "payload" field. 113 | public const int PayloadFieldNumber = 4; 114 | private pb::ByteString payload_ = pb::ByteString.Empty; 115 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 116 | public pb::ByteString Payload { 117 | get { return payload_; } 118 | set { 119 | payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 120 | } 121 | } 122 | 123 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 124 | public override bool Equals(object other) { 125 | return Equals(other as ChaincodeEvent); 126 | } 127 | 128 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 129 | public bool Equals(ChaincodeEvent other) { 130 | if (ReferenceEquals(other, null)) { 131 | return false; 132 | } 133 | if (ReferenceEquals(other, this)) { 134 | return true; 135 | } 136 | if (ChaincodeId != other.ChaincodeId) return false; 137 | if (TxId != other.TxId) return false; 138 | if (EventName != other.EventName) return false; 139 | if (Payload != other.Payload) return false; 140 | return true; 141 | } 142 | 143 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 144 | public override int GetHashCode() { 145 | int hash = 1; 146 | if (ChaincodeId.Length != 0) hash ^= ChaincodeId.GetHashCode(); 147 | if (TxId.Length != 0) hash ^= TxId.GetHashCode(); 148 | if (EventName.Length != 0) hash ^= EventName.GetHashCode(); 149 | if (Payload.Length != 0) hash ^= Payload.GetHashCode(); 150 | return hash; 151 | } 152 | 153 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 154 | public override string ToString() { 155 | return pb::JsonFormatter.ToDiagnosticString(this); 156 | } 157 | 158 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 159 | public void WriteTo(pb::CodedOutputStream output) { 160 | if (ChaincodeId.Length != 0) { 161 | output.WriteRawTag(10); 162 | output.WriteString(ChaincodeId); 163 | } 164 | if (TxId.Length != 0) { 165 | output.WriteRawTag(18); 166 | output.WriteString(TxId); 167 | } 168 | if (EventName.Length != 0) { 169 | output.WriteRawTag(26); 170 | output.WriteString(EventName); 171 | } 172 | if (Payload.Length != 0) { 173 | output.WriteRawTag(34); 174 | output.WriteBytes(Payload); 175 | } 176 | } 177 | 178 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 179 | public int CalculateSize() { 180 | int size = 0; 181 | if (ChaincodeId.Length != 0) { 182 | size += 1 + pb::CodedOutputStream.ComputeStringSize(ChaincodeId); 183 | } 184 | if (TxId.Length != 0) { 185 | size += 1 + pb::CodedOutputStream.ComputeStringSize(TxId); 186 | } 187 | if (EventName.Length != 0) { 188 | size += 1 + pb::CodedOutputStream.ComputeStringSize(EventName); 189 | } 190 | if (Payload.Length != 0) { 191 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Payload); 192 | } 193 | return size; 194 | } 195 | 196 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 197 | public void MergeFrom(ChaincodeEvent other) { 198 | if (other == null) { 199 | return; 200 | } 201 | if (other.ChaincodeId.Length != 0) { 202 | ChaincodeId = other.ChaincodeId; 203 | } 204 | if (other.TxId.Length != 0) { 205 | TxId = other.TxId; 206 | } 207 | if (other.EventName.Length != 0) { 208 | EventName = other.EventName; 209 | } 210 | if (other.Payload.Length != 0) { 211 | Payload = other.Payload; 212 | } 213 | } 214 | 215 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 216 | public void MergeFrom(pb::CodedInputStream input) { 217 | uint tag; 218 | while ((tag = input.ReadTag()) != 0) { 219 | switch(tag) { 220 | default: 221 | input.SkipLastField(); 222 | break; 223 | case 10: { 224 | ChaincodeId = input.ReadString(); 225 | break; 226 | } 227 | case 18: { 228 | TxId = input.ReadString(); 229 | break; 230 | } 231 | case 26: { 232 | EventName = input.ReadString(); 233 | break; 234 | } 235 | case 34: { 236 | Payload = input.ReadBytes(); 237 | break; 238 | } 239 | } 240 | } 241 | } 242 | 243 | } 244 | 245 | #endregion 246 | 247 | } 248 | 249 | #endregion Designer generated code 250 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/ChaincodeShimGrpc.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: peer/chaincode_shim.proto 4 | // 5 | // Original file comments: 6 | // 7 | // Copyright IBM Corp. All Rights Reserved. 8 | // 9 | // SPDX-License-Identifier: Apache-2.0 10 | // 11 | #pragma warning disable 1591 12 | #region Designer generated code 13 | 14 | using grpc = global::Grpc.Core; 15 | 16 | namespace Chaincode.NET.Protos { 17 | /// 18 | /// Interface that provides support to chaincode execution. ChaincodeContext 19 | /// provides the context necessary for the server to respond appropriately. 20 | /// 21 | public static partial class ChaincodeSupport 22 | { 23 | static readonly string __ServiceName = "protos.ChaincodeSupport"; 24 | 25 | static readonly grpc::Marshaller __Marshaller_ChaincodeMessage = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Chaincode.NET.Protos.ChaincodeMessage.Parser.ParseFrom); 26 | 27 | static readonly grpc::Method __Method_Register = new grpc::Method( 28 | grpc::MethodType.DuplexStreaming, 29 | __ServiceName, 30 | "Register", 31 | __Marshaller_ChaincodeMessage, 32 | __Marshaller_ChaincodeMessage); 33 | 34 | /// Service descriptor 35 | public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor 36 | { 37 | get { return global::Chaincode.NET.Protos.ChaincodeShimReflection.Descriptor.Services[0]; } 38 | } 39 | 40 | /// Base class for server-side implementations of ChaincodeSupport 41 | public abstract partial class ChaincodeSupportBase 42 | { 43 | public virtual global::System.Threading.Tasks.Task Register(grpc::IAsyncStreamReader requestStream, grpc::IServerStreamWriter responseStream, grpc::ServerCallContext context) 44 | { 45 | throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); 46 | } 47 | 48 | } 49 | 50 | /// Client for ChaincodeSupport 51 | public partial class ChaincodeSupportClient : grpc::ClientBase 52 | { 53 | /// Creates a new client for ChaincodeSupport 54 | /// The channel to use to make remote calls. 55 | public ChaincodeSupportClient(grpc::Channel channel) : base(channel) 56 | { 57 | } 58 | /// Creates a new client for ChaincodeSupport that uses a custom CallInvoker. 59 | /// The callInvoker to use to make remote calls. 60 | public ChaincodeSupportClient(grpc::CallInvoker callInvoker) : base(callInvoker) 61 | { 62 | } 63 | /// Protected parameterless constructor to allow creation of test doubles. 64 | protected ChaincodeSupportClient() : base() 65 | { 66 | } 67 | /// Protected constructor to allow creation of configured clients. 68 | /// The client configuration. 69 | protected ChaincodeSupportClient(ClientBaseConfiguration configuration) : base(configuration) 70 | { 71 | } 72 | 73 | public virtual grpc::AsyncDuplexStreamingCall Register(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) 74 | { 75 | return Register(new grpc::CallOptions(headers, deadline, cancellationToken)); 76 | } 77 | public virtual grpc::AsyncDuplexStreamingCall Register(grpc::CallOptions options) 78 | { 79 | return CallInvoker.AsyncDuplexStreamingCall(__Method_Register, null, options); 80 | } 81 | /// Creates a new instance of client from given ClientBaseConfiguration. 82 | protected override ChaincodeSupportClient NewInstance(ClientBaseConfiguration configuration) 83 | { 84 | return new ChaincodeSupportClient(configuration); 85 | } 86 | } 87 | 88 | /// Creates service definition that can be registered with a server 89 | /// An object implementing the server-side handling logic. 90 | public static grpc::ServerServiceDefinition BindService(ChaincodeSupportBase serviceImpl) 91 | { 92 | return grpc::ServerServiceDefinition.CreateBuilder() 93 | .AddMethod(__Method_Register, serviceImpl.Register).Build(); 94 | } 95 | 96 | } 97 | } 98 | #endregion 99 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/Identities.cs: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: msp/identities.proto 3 | #pragma warning disable 1591, 0612, 3021 4 | #region Designer generated code 5 | 6 | using pb = global::Google.Protobuf; 7 | using pbr = global::Google.Protobuf.Reflection; 8 | 9 | namespace Chaincode.NET.Protos { 10 | 11 | /// Holder for reflection information generated from msp/identities.proto 12 | public static partial class IdentitiesReflection { 13 | 14 | #region Descriptor 15 | /// File descriptor for msp/identities.proto 16 | public static pbr::FileDescriptor Descriptor { 17 | get { return descriptor; } 18 | } 19 | private static pbr::FileDescriptor descriptor; 20 | 21 | static IdentitiesReflection() { 22 | byte[] descriptorData = global::System.Convert.FromBase64String( 23 | string.Concat( 24 | "ChRtc3AvaWRlbnRpdGllcy5wcm90bxIDbXNwIjUKElNlcmlhbGl6ZWRJZGVu", 25 | "dGl0eRINCgVtc3BpZBgBIAEoCRIQCghpZF9ieXRlcxgCIAEoDCJfChhTZXJp", 26 | "YWxpemVkSWRlbWl4SWRlbnRpdHkSDAoETnltWBgBIAEoDBIMCgROeW1ZGAIg", 27 | "ASgMEgoKAk9VGAMgASgMEgwKBFJvbGUYBCABKAwSDQoFUHJvb2YYBSABKAxC", 28 | "TQohb3JnLmh5cGVybGVkZ2VyLmZhYnJpYy5wcm90b3MubXNwWihnaXRodWIu", 29 | "Y29tL2h5cGVybGVkZ2VyL2ZhYnJpYy9wcm90b3MvbXNwYgZwcm90bzM=")); 30 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 31 | new pbr::FileDescriptor[] { }, 32 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 33 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.SerializedIdentity), global::Chaincode.NET.Protos.SerializedIdentity.Parser, new[]{ "Mspid", "IdBytes" }, null, null, null), 34 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.SerializedIdemixIdentity), global::Chaincode.NET.Protos.SerializedIdemixIdentity.Parser, new[]{ "NymX", "NymY", "OU", "Role", "Proof" }, null, null, null) 35 | })); 36 | } 37 | #endregion 38 | 39 | } 40 | #region Messages 41 | /// 42 | /// This struct represents an Identity 43 | /// (with its MSP identifier) to be used 44 | /// to serialize it and deserialize it 45 | /// 46 | public sealed partial class SerializedIdentity : pb::IMessage { 47 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SerializedIdentity()); 48 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 49 | public static pb::MessageParser Parser { get { return _parser; } } 50 | 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 52 | public static pbr::MessageDescriptor Descriptor { 53 | get { return global::Chaincode.NET.Protos.IdentitiesReflection.Descriptor.MessageTypes[0]; } 54 | } 55 | 56 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 57 | pbr::MessageDescriptor pb::IMessage.Descriptor { 58 | get { return Descriptor; } 59 | } 60 | 61 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 62 | public SerializedIdentity() { 63 | OnConstruction(); 64 | } 65 | 66 | partial void OnConstruction(); 67 | 68 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 69 | public SerializedIdentity(SerializedIdentity other) : this() { 70 | mspid_ = other.mspid_; 71 | idBytes_ = other.idBytes_; 72 | } 73 | 74 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 75 | public SerializedIdentity Clone() { 76 | return new SerializedIdentity(this); 77 | } 78 | 79 | /// Field number for the "mspid" field. 80 | public const int MspidFieldNumber = 1; 81 | private string mspid_ = ""; 82 | /// 83 | /// The identifier of the associated membership service provider 84 | /// 85 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 86 | public string Mspid { 87 | get { return mspid_; } 88 | set { 89 | mspid_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 90 | } 91 | } 92 | 93 | /// Field number for the "id_bytes" field. 94 | public const int IdBytesFieldNumber = 2; 95 | private pb::ByteString idBytes_ = pb::ByteString.Empty; 96 | /// 97 | /// the Identity, serialized according to the rules of its MPS 98 | /// 99 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 100 | public pb::ByteString IdBytes { 101 | get { return idBytes_; } 102 | set { 103 | idBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 104 | } 105 | } 106 | 107 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 108 | public override bool Equals(object other) { 109 | return Equals(other as SerializedIdentity); 110 | } 111 | 112 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 113 | public bool Equals(SerializedIdentity other) { 114 | if (ReferenceEquals(other, null)) { 115 | return false; 116 | } 117 | if (ReferenceEquals(other, this)) { 118 | return true; 119 | } 120 | if (Mspid != other.Mspid) return false; 121 | if (IdBytes != other.IdBytes) return false; 122 | return true; 123 | } 124 | 125 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 126 | public override int GetHashCode() { 127 | int hash = 1; 128 | if (Mspid.Length != 0) hash ^= Mspid.GetHashCode(); 129 | if (IdBytes.Length != 0) hash ^= IdBytes.GetHashCode(); 130 | return hash; 131 | } 132 | 133 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 134 | public override string ToString() { 135 | return pb::JsonFormatter.ToDiagnosticString(this); 136 | } 137 | 138 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 139 | public void WriteTo(pb::CodedOutputStream output) { 140 | if (Mspid.Length != 0) { 141 | output.WriteRawTag(10); 142 | output.WriteString(Mspid); 143 | } 144 | if (IdBytes.Length != 0) { 145 | output.WriteRawTag(18); 146 | output.WriteBytes(IdBytes); 147 | } 148 | } 149 | 150 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 151 | public int CalculateSize() { 152 | int size = 0; 153 | if (Mspid.Length != 0) { 154 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Mspid); 155 | } 156 | if (IdBytes.Length != 0) { 157 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(IdBytes); 158 | } 159 | return size; 160 | } 161 | 162 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 163 | public void MergeFrom(SerializedIdentity other) { 164 | if (other == null) { 165 | return; 166 | } 167 | if (other.Mspid.Length != 0) { 168 | Mspid = other.Mspid; 169 | } 170 | if (other.IdBytes.Length != 0) { 171 | IdBytes = other.IdBytes; 172 | } 173 | } 174 | 175 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 176 | public void MergeFrom(pb::CodedInputStream input) { 177 | uint tag; 178 | while ((tag = input.ReadTag()) != 0) { 179 | switch(tag) { 180 | default: 181 | input.SkipLastField(); 182 | break; 183 | case 10: { 184 | Mspid = input.ReadString(); 185 | break; 186 | } 187 | case 18: { 188 | IdBytes = input.ReadBytes(); 189 | break; 190 | } 191 | } 192 | } 193 | } 194 | 195 | } 196 | 197 | /// 198 | /// This struct represents an Idemix Identity 199 | /// to be used to serialize it and deserialize it. 200 | /// The IdemixMSP will first serialize an idemix identity to bytes using 201 | /// this proto, and then uses these bytes as id_bytes in SerializedIdentity 202 | /// 203 | public sealed partial class SerializedIdemixIdentity : pb::IMessage { 204 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SerializedIdemixIdentity()); 205 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 206 | public static pb::MessageParser Parser { get { return _parser; } } 207 | 208 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 209 | public static pbr::MessageDescriptor Descriptor { 210 | get { return global::Chaincode.NET.Protos.IdentitiesReflection.Descriptor.MessageTypes[1]; } 211 | } 212 | 213 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 214 | pbr::MessageDescriptor pb::IMessage.Descriptor { 215 | get { return Descriptor; } 216 | } 217 | 218 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 219 | public SerializedIdemixIdentity() { 220 | OnConstruction(); 221 | } 222 | 223 | partial void OnConstruction(); 224 | 225 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 226 | public SerializedIdemixIdentity(SerializedIdemixIdentity other) : this() { 227 | nymX_ = other.nymX_; 228 | nymY_ = other.nymY_; 229 | oU_ = other.oU_; 230 | role_ = other.role_; 231 | proof_ = other.proof_; 232 | } 233 | 234 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 235 | public SerializedIdemixIdentity Clone() { 236 | return new SerializedIdemixIdentity(this); 237 | } 238 | 239 | /// Field number for the "NymX" field. 240 | public const int NymXFieldNumber = 1; 241 | private pb::ByteString nymX_ = pb::ByteString.Empty; 242 | /// 243 | /// NymX is the X-component of the pseudonym elliptic curve point. 244 | /// It is a []byte representation of an amcl.BIG 245 | /// The pseudonym can be seen as a public key of the identity, it is used to verify signatures. 246 | /// 247 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 248 | public pb::ByteString NymX { 249 | get { return nymX_; } 250 | set { 251 | nymX_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 252 | } 253 | } 254 | 255 | /// Field number for the "NymY" field. 256 | public const int NymYFieldNumber = 2; 257 | private pb::ByteString nymY_ = pb::ByteString.Empty; 258 | /// 259 | /// NymY is the Y-component of the pseudonym elliptic curve point. 260 | /// It is a []byte representation of an amcl.BIG 261 | /// The pseudonym can be seen as a public key of the identity, it is used to verify signatures. 262 | /// 263 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 264 | public pb::ByteString NymY { 265 | get { return nymY_; } 266 | set { 267 | nymY_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 268 | } 269 | } 270 | 271 | /// Field number for the "OU" field. 272 | public const int OUFieldNumber = 3; 273 | private pb::ByteString oU_ = pb::ByteString.Empty; 274 | /// 275 | /// OU contains the organizational unit of the idemix identity 276 | /// 277 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 278 | public pb::ByteString OU { 279 | get { return oU_; } 280 | set { 281 | oU_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 282 | } 283 | } 284 | 285 | /// Field number for the "Role" field. 286 | public const int RoleFieldNumber = 4; 287 | private pb::ByteString role_ = pb::ByteString.Empty; 288 | /// 289 | /// Role contains the role of this identity (e.g., ADMIN or MEMBER) 290 | /// 291 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 292 | public pb::ByteString Role { 293 | get { return role_; } 294 | set { 295 | role_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 296 | } 297 | } 298 | 299 | /// Field number for the "Proof" field. 300 | public const int ProofFieldNumber = 5; 301 | private pb::ByteString proof_ = pb::ByteString.Empty; 302 | /// 303 | /// Proof contains the cryptographic evidence that this identity is valid 304 | /// 305 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 306 | public pb::ByteString Proof { 307 | get { return proof_; } 308 | set { 309 | proof_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 310 | } 311 | } 312 | 313 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 314 | public override bool Equals(object other) { 315 | return Equals(other as SerializedIdemixIdentity); 316 | } 317 | 318 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 319 | public bool Equals(SerializedIdemixIdentity other) { 320 | if (ReferenceEquals(other, null)) { 321 | return false; 322 | } 323 | if (ReferenceEquals(other, this)) { 324 | return true; 325 | } 326 | if (NymX != other.NymX) return false; 327 | if (NymY != other.NymY) return false; 328 | if (OU != other.OU) return false; 329 | if (Role != other.Role) return false; 330 | if (Proof != other.Proof) return false; 331 | return true; 332 | } 333 | 334 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 335 | public override int GetHashCode() { 336 | int hash = 1; 337 | if (NymX.Length != 0) hash ^= NymX.GetHashCode(); 338 | if (NymY.Length != 0) hash ^= NymY.GetHashCode(); 339 | if (OU.Length != 0) hash ^= OU.GetHashCode(); 340 | if (Role.Length != 0) hash ^= Role.GetHashCode(); 341 | if (Proof.Length != 0) hash ^= Proof.GetHashCode(); 342 | return hash; 343 | } 344 | 345 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 346 | public override string ToString() { 347 | return pb::JsonFormatter.ToDiagnosticString(this); 348 | } 349 | 350 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 351 | public void WriteTo(pb::CodedOutputStream output) { 352 | if (NymX.Length != 0) { 353 | output.WriteRawTag(10); 354 | output.WriteBytes(NymX); 355 | } 356 | if (NymY.Length != 0) { 357 | output.WriteRawTag(18); 358 | output.WriteBytes(NymY); 359 | } 360 | if (OU.Length != 0) { 361 | output.WriteRawTag(26); 362 | output.WriteBytes(OU); 363 | } 364 | if (Role.Length != 0) { 365 | output.WriteRawTag(34); 366 | output.WriteBytes(Role); 367 | } 368 | if (Proof.Length != 0) { 369 | output.WriteRawTag(42); 370 | output.WriteBytes(Proof); 371 | } 372 | } 373 | 374 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 375 | public int CalculateSize() { 376 | int size = 0; 377 | if (NymX.Length != 0) { 378 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(NymX); 379 | } 380 | if (NymY.Length != 0) { 381 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(NymY); 382 | } 383 | if (OU.Length != 0) { 384 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(OU); 385 | } 386 | if (Role.Length != 0) { 387 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Role); 388 | } 389 | if (Proof.Length != 0) { 390 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Proof); 391 | } 392 | return size; 393 | } 394 | 395 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 396 | public void MergeFrom(SerializedIdemixIdentity other) { 397 | if (other == null) { 398 | return; 399 | } 400 | if (other.NymX.Length != 0) { 401 | NymX = other.NymX; 402 | } 403 | if (other.NymY.Length != 0) { 404 | NymY = other.NymY; 405 | } 406 | if (other.OU.Length != 0) { 407 | OU = other.OU; 408 | } 409 | if (other.Role.Length != 0) { 410 | Role = other.Role; 411 | } 412 | if (other.Proof.Length != 0) { 413 | Proof = other.Proof; 414 | } 415 | } 416 | 417 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 418 | public void MergeFrom(pb::CodedInputStream input) { 419 | uint tag; 420 | while ((tag = input.ReadTag()) != 0) { 421 | switch(tag) { 422 | default: 423 | input.SkipLastField(); 424 | break; 425 | case 10: { 426 | NymX = input.ReadBytes(); 427 | break; 428 | } 429 | case 18: { 430 | NymY = input.ReadBytes(); 431 | break; 432 | } 433 | case 26: { 434 | OU = input.ReadBytes(); 435 | break; 436 | } 437 | case 34: { 438 | Role = input.ReadBytes(); 439 | break; 440 | } 441 | case 42: { 442 | Proof = input.ReadBytes(); 443 | break; 444 | } 445 | } 446 | } 447 | } 448 | 449 | } 450 | 451 | #endregion 452 | 453 | } 454 | 455 | #endregion Designer generated code 456 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/KvQueryResult.cs: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: ledger/queryresult/kv_query_result.proto 3 | #pragma warning disable 1591, 0612, 3021 4 | #region Designer generated code 5 | 6 | using pb = global::Google.Protobuf; 7 | using pbr = global::Google.Protobuf.Reflection; 8 | 9 | namespace Chaincode.NET.Protos { 10 | 11 | /// Holder for reflection information generated from ledger/queryresult/kv_query_result.proto 12 | public static partial class KvQueryResultReflection { 13 | 14 | #region Descriptor 15 | /// File descriptor for ledger/queryresult/kv_query_result.proto 16 | public static pbr::FileDescriptor Descriptor { 17 | get { return descriptor; } 18 | } 19 | private static pbr::FileDescriptor descriptor; 20 | 21 | static KvQueryResultReflection() { 22 | byte[] descriptorData = global::System.Convert.FromBase64String( 23 | string.Concat( 24 | "CihsZWRnZXIvcXVlcnlyZXN1bHQva3ZfcXVlcnlfcmVzdWx0LnByb3RvEgtx", 25 | "dWVyeXJlc3VsdBofZ29vZ2xlL3Byb3RvYnVmL3RpbWVzdGFtcC5wcm90byIz", 26 | "CgJLVhIRCgluYW1lc3BhY2UYASABKAkSCwoDa2V5GAIgASgJEg0KBXZhbHVl", 27 | "GAMgASgMInEKD0tleU1vZGlmaWNhdGlvbhINCgV0eF9pZBgBIAEoCRINCgV2", 28 | "YWx1ZRgCIAEoDBItCgl0aW1lc3RhbXAYAyABKAsyGi5nb29nbGUucHJvdG9i", 29 | "dWYuVGltZXN0YW1wEhEKCWlzX2RlbGV0ZRgEIAEoCEJrCjBvcmcuaHlwZXJs", 30 | "ZWRnZXIuZmFicmljLnByb3Rvcy5sZWRnZXIucXVlcnlyZXN1bHRaN2dpdGh1", 31 | "Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljL3Byb3Rvcy9sZWRnZXIvcXVlcnly", 32 | "ZXN1bHRiBnByb3RvMw==")); 33 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 34 | new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, }, 35 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 36 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.KV), global::Chaincode.NET.Protos.KV.Parser, new[]{ "Namespace", "Key", "Value" }, null, null, null), 37 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.KeyModification), global::Chaincode.NET.Protos.KeyModification.Parser, new[]{ "TxId", "Value", "Timestamp", "IsDelete" }, null, null, null) 38 | })); 39 | } 40 | #endregion 41 | 42 | } 43 | #region Messages 44 | /// 45 | /// KV -- QueryResult for range/execute query. Holds a key and corresponding value. 46 | /// 47 | public sealed partial class KV : pb::IMessage { 48 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new KV()); 49 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 50 | public static pb::MessageParser Parser { get { return _parser; } } 51 | 52 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 53 | public static pbr::MessageDescriptor Descriptor { 54 | get { return global::Chaincode.NET.Protos.KvQueryResultReflection.Descriptor.MessageTypes[0]; } 55 | } 56 | 57 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 58 | pbr::MessageDescriptor pb::IMessage.Descriptor { 59 | get { return Descriptor; } 60 | } 61 | 62 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 63 | public KV() { 64 | OnConstruction(); 65 | } 66 | 67 | partial void OnConstruction(); 68 | 69 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 70 | public KV(KV other) : this() { 71 | namespace_ = other.namespace_; 72 | key_ = other.key_; 73 | value_ = other.value_; 74 | } 75 | 76 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 77 | public KV Clone() { 78 | return new KV(this); 79 | } 80 | 81 | /// Field number for the "namespace" field. 82 | public const int NamespaceFieldNumber = 1; 83 | private string namespace_ = ""; 84 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 85 | public string Namespace { 86 | get { return namespace_; } 87 | set { 88 | namespace_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 89 | } 90 | } 91 | 92 | /// Field number for the "key" field. 93 | public const int KeyFieldNumber = 2; 94 | private string key_ = ""; 95 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 96 | public string Key { 97 | get { return key_; } 98 | set { 99 | key_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 100 | } 101 | } 102 | 103 | /// Field number for the "value" field. 104 | public const int ValueFieldNumber = 3; 105 | private pb::ByteString value_ = pb::ByteString.Empty; 106 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 107 | public pb::ByteString Value { 108 | get { return value_; } 109 | set { 110 | value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 111 | } 112 | } 113 | 114 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 115 | public override bool Equals(object other) { 116 | return Equals(other as KV); 117 | } 118 | 119 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 120 | public bool Equals(KV other) { 121 | if (ReferenceEquals(other, null)) { 122 | return false; 123 | } 124 | if (ReferenceEquals(other, this)) { 125 | return true; 126 | } 127 | if (Namespace != other.Namespace) return false; 128 | if (Key != other.Key) return false; 129 | if (Value != other.Value) return false; 130 | return true; 131 | } 132 | 133 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 134 | public override int GetHashCode() { 135 | int hash = 1; 136 | if (Namespace.Length != 0) hash ^= Namespace.GetHashCode(); 137 | if (Key.Length != 0) hash ^= Key.GetHashCode(); 138 | if (Value.Length != 0) hash ^= Value.GetHashCode(); 139 | return hash; 140 | } 141 | 142 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 143 | public override string ToString() { 144 | return pb::JsonFormatter.ToDiagnosticString(this); 145 | } 146 | 147 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 148 | public void WriteTo(pb::CodedOutputStream output) { 149 | if (Namespace.Length != 0) { 150 | output.WriteRawTag(10); 151 | output.WriteString(Namespace); 152 | } 153 | if (Key.Length != 0) { 154 | output.WriteRawTag(18); 155 | output.WriteString(Key); 156 | } 157 | if (Value.Length != 0) { 158 | output.WriteRawTag(26); 159 | output.WriteBytes(Value); 160 | } 161 | } 162 | 163 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 164 | public int CalculateSize() { 165 | int size = 0; 166 | if (Namespace.Length != 0) { 167 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Namespace); 168 | } 169 | if (Key.Length != 0) { 170 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Key); 171 | } 172 | if (Value.Length != 0) { 173 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Value); 174 | } 175 | return size; 176 | } 177 | 178 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 179 | public void MergeFrom(KV other) { 180 | if (other == null) { 181 | return; 182 | } 183 | if (other.Namespace.Length != 0) { 184 | Namespace = other.Namespace; 185 | } 186 | if (other.Key.Length != 0) { 187 | Key = other.Key; 188 | } 189 | if (other.Value.Length != 0) { 190 | Value = other.Value; 191 | } 192 | } 193 | 194 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 195 | public void MergeFrom(pb::CodedInputStream input) { 196 | uint tag; 197 | while ((tag = input.ReadTag()) != 0) { 198 | switch(tag) { 199 | default: 200 | input.SkipLastField(); 201 | break; 202 | case 10: { 203 | Namespace = input.ReadString(); 204 | break; 205 | } 206 | case 18: { 207 | Key = input.ReadString(); 208 | break; 209 | } 210 | case 26: { 211 | Value = input.ReadBytes(); 212 | break; 213 | } 214 | } 215 | } 216 | } 217 | 218 | } 219 | 220 | /// 221 | /// KeyModification -- QueryResult for history query. Holds a transaction ID, value, 222 | /// timestamp, and delete marker which resulted from a history query. 223 | /// 224 | public sealed partial class KeyModification : pb::IMessage { 225 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new KeyModification()); 226 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 227 | public static pb::MessageParser Parser { get { return _parser; } } 228 | 229 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 230 | public static pbr::MessageDescriptor Descriptor { 231 | get { return global::Chaincode.NET.Protos.KvQueryResultReflection.Descriptor.MessageTypes[1]; } 232 | } 233 | 234 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 235 | pbr::MessageDescriptor pb::IMessage.Descriptor { 236 | get { return Descriptor; } 237 | } 238 | 239 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 240 | public KeyModification() { 241 | OnConstruction(); 242 | } 243 | 244 | partial void OnConstruction(); 245 | 246 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 247 | public KeyModification(KeyModification other) : this() { 248 | txId_ = other.txId_; 249 | value_ = other.value_; 250 | Timestamp = other.timestamp_ != null ? other.Timestamp.Clone() : null; 251 | isDelete_ = other.isDelete_; 252 | } 253 | 254 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 255 | public KeyModification Clone() { 256 | return new KeyModification(this); 257 | } 258 | 259 | /// Field number for the "tx_id" field. 260 | public const int TxIdFieldNumber = 1; 261 | private string txId_ = ""; 262 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 263 | public string TxId { 264 | get { return txId_; } 265 | set { 266 | txId_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 267 | } 268 | } 269 | 270 | /// Field number for the "value" field. 271 | public const int ValueFieldNumber = 2; 272 | private pb::ByteString value_ = pb::ByteString.Empty; 273 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 274 | public pb::ByteString Value { 275 | get { return value_; } 276 | set { 277 | value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 278 | } 279 | } 280 | 281 | /// Field number for the "timestamp" field. 282 | public const int TimestampFieldNumber = 3; 283 | private global::Google.Protobuf.WellKnownTypes.Timestamp timestamp_; 284 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 285 | public global::Google.Protobuf.WellKnownTypes.Timestamp Timestamp { 286 | get { return timestamp_; } 287 | set { 288 | timestamp_ = value; 289 | } 290 | } 291 | 292 | /// Field number for the "is_delete" field. 293 | public const int IsDeleteFieldNumber = 4; 294 | private bool isDelete_; 295 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 296 | public bool IsDelete { 297 | get { return isDelete_; } 298 | set { 299 | isDelete_ = value; 300 | } 301 | } 302 | 303 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 304 | public override bool Equals(object other) { 305 | return Equals(other as KeyModification); 306 | } 307 | 308 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 309 | public bool Equals(KeyModification other) { 310 | if (ReferenceEquals(other, null)) { 311 | return false; 312 | } 313 | if (ReferenceEquals(other, this)) { 314 | return true; 315 | } 316 | if (TxId != other.TxId) return false; 317 | if (Value != other.Value) return false; 318 | if (!object.Equals(Timestamp, other.Timestamp)) return false; 319 | if (IsDelete != other.IsDelete) return false; 320 | return true; 321 | } 322 | 323 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 324 | public override int GetHashCode() { 325 | int hash = 1; 326 | if (TxId.Length != 0) hash ^= TxId.GetHashCode(); 327 | if (Value.Length != 0) hash ^= Value.GetHashCode(); 328 | if (timestamp_ != null) hash ^= Timestamp.GetHashCode(); 329 | if (IsDelete != false) hash ^= IsDelete.GetHashCode(); 330 | return hash; 331 | } 332 | 333 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 334 | public override string ToString() { 335 | return pb::JsonFormatter.ToDiagnosticString(this); 336 | } 337 | 338 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 339 | public void WriteTo(pb::CodedOutputStream output) { 340 | if (TxId.Length != 0) { 341 | output.WriteRawTag(10); 342 | output.WriteString(TxId); 343 | } 344 | if (Value.Length != 0) { 345 | output.WriteRawTag(18); 346 | output.WriteBytes(Value); 347 | } 348 | if (timestamp_ != null) { 349 | output.WriteRawTag(26); 350 | output.WriteMessage(Timestamp); 351 | } 352 | if (IsDelete != false) { 353 | output.WriteRawTag(32); 354 | output.WriteBool(IsDelete); 355 | } 356 | } 357 | 358 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 359 | public int CalculateSize() { 360 | int size = 0; 361 | if (TxId.Length != 0) { 362 | size += 1 + pb::CodedOutputStream.ComputeStringSize(TxId); 363 | } 364 | if (Value.Length != 0) { 365 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Value); 366 | } 367 | if (timestamp_ != null) { 368 | size += 1 + pb::CodedOutputStream.ComputeMessageSize(Timestamp); 369 | } 370 | if (IsDelete != false) { 371 | size += 1 + 1; 372 | } 373 | return size; 374 | } 375 | 376 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 377 | public void MergeFrom(KeyModification other) { 378 | if (other == null) { 379 | return; 380 | } 381 | if (other.TxId.Length != 0) { 382 | TxId = other.TxId; 383 | } 384 | if (other.Value.Length != 0) { 385 | Value = other.Value; 386 | } 387 | if (other.timestamp_ != null) { 388 | if (timestamp_ == null) { 389 | timestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp(); 390 | } 391 | Timestamp.MergeFrom(other.Timestamp); 392 | } 393 | if (other.IsDelete != false) { 394 | IsDelete = other.IsDelete; 395 | } 396 | } 397 | 398 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 399 | public void MergeFrom(pb::CodedInputStream input) { 400 | uint tag; 401 | while ((tag = input.ReadTag()) != 0) { 402 | switch(tag) { 403 | default: 404 | input.SkipLastField(); 405 | break; 406 | case 10: { 407 | TxId = input.ReadString(); 408 | break; 409 | } 410 | case 18: { 411 | Value = input.ReadBytes(); 412 | break; 413 | } 414 | case 26: { 415 | if (timestamp_ == null) { 416 | timestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp(); 417 | } 418 | input.ReadMessage(timestamp_); 419 | break; 420 | } 421 | case 32: { 422 | IsDelete = input.ReadBool(); 423 | break; 424 | } 425 | } 426 | } 427 | } 428 | 429 | } 430 | 431 | #endregion 432 | 433 | } 434 | 435 | #endregion Designer generated code 436 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Protos/ProposalResponse.cs: -------------------------------------------------------------------------------- 1 | // Generated by the protocol buffer compiler. DO NOT EDIT! 2 | // source: peer/proposal_response.proto 3 | #pragma warning disable 1591, 0612, 3021 4 | #region Designer generated code 5 | 6 | using pb = global::Google.Protobuf; 7 | using pbr = global::Google.Protobuf.Reflection; 8 | 9 | namespace Chaincode.NET.Protos { 10 | 11 | /// Holder for reflection information generated from peer/proposal_response.proto 12 | public static partial class ProposalResponseReflection { 13 | 14 | #region Descriptor 15 | /// File descriptor for peer/proposal_response.proto 16 | public static pbr::FileDescriptor Descriptor { 17 | get { return descriptor; } 18 | } 19 | private static pbr::FileDescriptor descriptor; 20 | 21 | static ProposalResponseReflection() { 22 | byte[] descriptorData = global::System.Convert.FromBase64String( 23 | string.Concat( 24 | "ChxwZWVyL3Byb3Bvc2FsX3Jlc3BvbnNlLnByb3RvEgZwcm90b3MaH2dvb2ds", 25 | "ZS9wcm90b2J1Zi90aW1lc3RhbXAucHJvdG8isQEKEFByb3Bvc2FsUmVzcG9u", 26 | "c2USDwoHdmVyc2lvbhgBIAEoBRItCgl0aW1lc3RhbXAYAiABKAsyGi5nb29n", 27 | "bGUucHJvdG9idWYuVGltZXN0YW1wEiIKCHJlc3BvbnNlGAQgASgLMhAucHJv", 28 | "dG9zLlJlc3BvbnNlEg8KB3BheWxvYWQYBSABKAwSKAoLZW5kb3JzZW1lbnQY", 29 | "BiABKAsyEy5wcm90b3MuRW5kb3JzZW1lbnQiPAoIUmVzcG9uc2USDgoGc3Rh", 30 | "dHVzGAEgASgFEg8KB21lc3NhZ2UYAiABKAkSDwoHcGF5bG9hZBgDIAEoDCJD", 31 | "ChdQcm9wb3NhbFJlc3BvbnNlUGF5bG9hZBIVCg1wcm9wb3NhbF9oYXNoGAEg", 32 | "ASgMEhEKCWV4dGVuc2lvbhgCIAEoDCIyCgtFbmRvcnNlbWVudBIQCghlbmRv", 33 | "cnNlchgBIAEoDBIRCglzaWduYXR1cmUYAiABKAxCaAoib3JnLmh5cGVybGVk", 34 | "Z2VyLmZhYnJpYy5wcm90b3MucGVlckIXUHJvcG9zYWxSZXNwb25zZVBhY2th", 35 | "Z2VaKWdpdGh1Yi5jb20vaHlwZXJsZWRnZXIvZmFicmljL3Byb3Rvcy9wZWVy", 36 | "YgZwcm90bzM=")); 37 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 38 | new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, }, 39 | new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { 40 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.ProposalResponse), global::Chaincode.NET.Protos.ProposalResponse.Parser, new[]{ "Version", "Timestamp", "Response", "Payload", "Endorsement" }, null, null, null), 41 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.Response), global::Chaincode.NET.Protos.Response.Parser, new[]{ "Status", "Message", "Payload" }, null, null, null), 42 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.ProposalResponsePayload), global::Chaincode.NET.Protos.ProposalResponsePayload.Parser, new[]{ "ProposalHash", "Extension" }, null, null, null), 43 | new pbr::GeneratedClrTypeInfo(typeof(global::Chaincode.NET.Protos.Endorsement), global::Chaincode.NET.Protos.Endorsement.Parser, new[]{ "Endorser", "Signature" }, null, null, null) 44 | })); 45 | } 46 | #endregion 47 | 48 | } 49 | #region Messages 50 | /// 51 | /// A ProposalResponse is returned from an endorser to the proposal submitter. 52 | /// The idea is that this message contains the endorser's response to the 53 | /// request of a client to perform an action over a chaincode (or more 54 | /// generically on the ledger); the response might be success/error (conveyed in 55 | /// the Response field) together with a description of the action and a 56 | /// signature over it by that endorser. If a sufficient number of distinct 57 | /// endorsers agree on the same action and produce signature to that effect, a 58 | /// transaction can be generated and sent for ordering. 59 | /// 60 | public sealed partial class ProposalResponse : pb::IMessage { 61 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ProposalResponse()); 62 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 63 | public static pb::MessageParser Parser { get { return _parser; } } 64 | 65 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 66 | public static pbr::MessageDescriptor Descriptor { 67 | get { return global::Chaincode.NET.Protos.ProposalResponseReflection.Descriptor.MessageTypes[0]; } 68 | } 69 | 70 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 71 | pbr::MessageDescriptor pb::IMessage.Descriptor { 72 | get { return Descriptor; } 73 | } 74 | 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 76 | public ProposalResponse() { 77 | OnConstruction(); 78 | } 79 | 80 | partial void OnConstruction(); 81 | 82 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 83 | public ProposalResponse(ProposalResponse other) : this() { 84 | version_ = other.version_; 85 | Timestamp = other.timestamp_ != null ? other.Timestamp.Clone() : null; 86 | Response = other.response_ != null ? other.Response.Clone() : null; 87 | payload_ = other.payload_; 88 | Endorsement = other.endorsement_ != null ? other.Endorsement.Clone() : null; 89 | } 90 | 91 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 92 | public ProposalResponse Clone() { 93 | return new ProposalResponse(this); 94 | } 95 | 96 | /// Field number for the "version" field. 97 | public const int VersionFieldNumber = 1; 98 | private int version_; 99 | /// 100 | /// Version indicates message protocol version 101 | /// 102 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 103 | public int Version { 104 | get { return version_; } 105 | set { 106 | version_ = value; 107 | } 108 | } 109 | 110 | /// Field number for the "timestamp" field. 111 | public const int TimestampFieldNumber = 2; 112 | private global::Google.Protobuf.WellKnownTypes.Timestamp timestamp_; 113 | /// 114 | /// Timestamp is the time that the message 115 | /// was created as defined by the sender 116 | /// 117 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 118 | public global::Google.Protobuf.WellKnownTypes.Timestamp Timestamp { 119 | get { return timestamp_; } 120 | set { 121 | timestamp_ = value; 122 | } 123 | } 124 | 125 | /// Field number for the "response" field. 126 | public const int ResponseFieldNumber = 4; 127 | private global::Chaincode.NET.Protos.Response response_; 128 | /// 129 | /// A response message indicating whether the 130 | /// endorsement of the action was successful 131 | /// 132 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 133 | public global::Chaincode.NET.Protos.Response Response { 134 | get { return response_; } 135 | set { 136 | response_ = value; 137 | } 138 | } 139 | 140 | /// Field number for the "payload" field. 141 | public const int PayloadFieldNumber = 5; 142 | private pb::ByteString payload_ = pb::ByteString.Empty; 143 | /// 144 | /// The payload of response. It is the bytes of ProposalResponsePayload 145 | /// 146 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 147 | public pb::ByteString Payload { 148 | get { return payload_; } 149 | set { 150 | payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 151 | } 152 | } 153 | 154 | /// Field number for the "endorsement" field. 155 | public const int EndorsementFieldNumber = 6; 156 | private global::Chaincode.NET.Protos.Endorsement endorsement_; 157 | /// 158 | /// The endorsement of the proposal, basically 159 | /// the endorser's signature over the payload 160 | /// 161 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 162 | public global::Chaincode.NET.Protos.Endorsement Endorsement { 163 | get { return endorsement_; } 164 | set { 165 | endorsement_ = value; 166 | } 167 | } 168 | 169 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 170 | public override bool Equals(object other) { 171 | return Equals(other as ProposalResponse); 172 | } 173 | 174 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 175 | public bool Equals(ProposalResponse other) { 176 | if (ReferenceEquals(other, null)) { 177 | return false; 178 | } 179 | if (ReferenceEquals(other, this)) { 180 | return true; 181 | } 182 | if (Version != other.Version) return false; 183 | if (!object.Equals(Timestamp, other.Timestamp)) return false; 184 | if (!object.Equals(Response, other.Response)) return false; 185 | if (Payload != other.Payload) return false; 186 | if (!object.Equals(Endorsement, other.Endorsement)) return false; 187 | return true; 188 | } 189 | 190 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 191 | public override int GetHashCode() { 192 | int hash = 1; 193 | if (Version != 0) hash ^= Version.GetHashCode(); 194 | if (timestamp_ != null) hash ^= Timestamp.GetHashCode(); 195 | if (response_ != null) hash ^= Response.GetHashCode(); 196 | if (Payload.Length != 0) hash ^= Payload.GetHashCode(); 197 | if (endorsement_ != null) hash ^= Endorsement.GetHashCode(); 198 | return hash; 199 | } 200 | 201 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 202 | public override string ToString() { 203 | return pb::JsonFormatter.ToDiagnosticString(this); 204 | } 205 | 206 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 207 | public void WriteTo(pb::CodedOutputStream output) { 208 | if (Version != 0) { 209 | output.WriteRawTag(8); 210 | output.WriteInt32(Version); 211 | } 212 | if (timestamp_ != null) { 213 | output.WriteRawTag(18); 214 | output.WriteMessage(Timestamp); 215 | } 216 | if (response_ != null) { 217 | output.WriteRawTag(34); 218 | output.WriteMessage(Response); 219 | } 220 | if (Payload.Length != 0) { 221 | output.WriteRawTag(42); 222 | output.WriteBytes(Payload); 223 | } 224 | if (endorsement_ != null) { 225 | output.WriteRawTag(50); 226 | output.WriteMessage(Endorsement); 227 | } 228 | } 229 | 230 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 231 | public int CalculateSize() { 232 | int size = 0; 233 | if (Version != 0) { 234 | size += 1 + pb::CodedOutputStream.ComputeInt32Size(Version); 235 | } 236 | if (timestamp_ != null) { 237 | size += 1 + pb::CodedOutputStream.ComputeMessageSize(Timestamp); 238 | } 239 | if (response_ != null) { 240 | size += 1 + pb::CodedOutputStream.ComputeMessageSize(Response); 241 | } 242 | if (Payload.Length != 0) { 243 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Payload); 244 | } 245 | if (endorsement_ != null) { 246 | size += 1 + pb::CodedOutputStream.ComputeMessageSize(Endorsement); 247 | } 248 | return size; 249 | } 250 | 251 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 252 | public void MergeFrom(ProposalResponse other) { 253 | if (other == null) { 254 | return; 255 | } 256 | if (other.Version != 0) { 257 | Version = other.Version; 258 | } 259 | if (other.timestamp_ != null) { 260 | if (timestamp_ == null) { 261 | timestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp(); 262 | } 263 | Timestamp.MergeFrom(other.Timestamp); 264 | } 265 | if (other.response_ != null) { 266 | if (response_ == null) { 267 | response_ = new global::Chaincode.NET.Protos.Response(); 268 | } 269 | Response.MergeFrom(other.Response); 270 | } 271 | if (other.Payload.Length != 0) { 272 | Payload = other.Payload; 273 | } 274 | if (other.endorsement_ != null) { 275 | if (endorsement_ == null) { 276 | endorsement_ = new global::Chaincode.NET.Protos.Endorsement(); 277 | } 278 | Endorsement.MergeFrom(other.Endorsement); 279 | } 280 | } 281 | 282 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 283 | public void MergeFrom(pb::CodedInputStream input) { 284 | uint tag; 285 | while ((tag = input.ReadTag()) != 0) { 286 | switch(tag) { 287 | default: 288 | input.SkipLastField(); 289 | break; 290 | case 8: { 291 | Version = input.ReadInt32(); 292 | break; 293 | } 294 | case 18: { 295 | if (timestamp_ == null) { 296 | timestamp_ = new global::Google.Protobuf.WellKnownTypes.Timestamp(); 297 | } 298 | input.ReadMessage(timestamp_); 299 | break; 300 | } 301 | case 34: { 302 | if (response_ == null) { 303 | response_ = new global::Chaincode.NET.Protos.Response(); 304 | } 305 | input.ReadMessage(response_); 306 | break; 307 | } 308 | case 42: { 309 | Payload = input.ReadBytes(); 310 | break; 311 | } 312 | case 50: { 313 | if (endorsement_ == null) { 314 | endorsement_ = new global::Chaincode.NET.Protos.Endorsement(); 315 | } 316 | input.ReadMessage(endorsement_); 317 | break; 318 | } 319 | } 320 | } 321 | } 322 | 323 | } 324 | 325 | /// 326 | /// A response with a representation similar to an HTTP response that can 327 | /// be used within another message. 328 | /// 329 | public sealed partial class Response : pb::IMessage { 330 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Response()); 331 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 332 | public static pb::MessageParser Parser { get { return _parser; } } 333 | 334 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 335 | public static pbr::MessageDescriptor Descriptor { 336 | get { return global::Chaincode.NET.Protos.ProposalResponseReflection.Descriptor.MessageTypes[1]; } 337 | } 338 | 339 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 340 | pbr::MessageDescriptor pb::IMessage.Descriptor { 341 | get { return Descriptor; } 342 | } 343 | 344 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 345 | public Response() { 346 | OnConstruction(); 347 | } 348 | 349 | partial void OnConstruction(); 350 | 351 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 352 | public Response(Response other) : this() { 353 | status_ = other.status_; 354 | message_ = other.message_; 355 | payload_ = other.payload_; 356 | } 357 | 358 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 359 | public Response Clone() { 360 | return new Response(this); 361 | } 362 | 363 | /// Field number for the "status" field. 364 | public const int StatusFieldNumber = 1; 365 | private int status_; 366 | /// 367 | /// A status code that should follow the HTTP status codes. 368 | /// 369 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 370 | public int Status { 371 | get { return status_; } 372 | set { 373 | status_ = value; 374 | } 375 | } 376 | 377 | /// Field number for the "message" field. 378 | public const int MessageFieldNumber = 2; 379 | private string message_ = ""; 380 | /// 381 | /// A message associated with the response code. 382 | /// 383 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 384 | public string Message { 385 | get { return message_; } 386 | set { 387 | message_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 388 | } 389 | } 390 | 391 | /// Field number for the "payload" field. 392 | public const int PayloadFieldNumber = 3; 393 | private pb::ByteString payload_ = pb::ByteString.Empty; 394 | /// 395 | /// A payload that can be used to include metadata with this response. 396 | /// 397 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 398 | public pb::ByteString Payload { 399 | get { return payload_; } 400 | set { 401 | payload_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 402 | } 403 | } 404 | 405 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 406 | public override bool Equals(object other) { 407 | return Equals(other as Response); 408 | } 409 | 410 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 411 | public bool Equals(Response other) { 412 | if (ReferenceEquals(other, null)) { 413 | return false; 414 | } 415 | if (ReferenceEquals(other, this)) { 416 | return true; 417 | } 418 | if (Status != other.Status) return false; 419 | if (Message != other.Message) return false; 420 | if (Payload != other.Payload) return false; 421 | return true; 422 | } 423 | 424 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 425 | public override int GetHashCode() { 426 | int hash = 1; 427 | if (Status != 0) hash ^= Status.GetHashCode(); 428 | if (Message.Length != 0) hash ^= Message.GetHashCode(); 429 | if (Payload.Length != 0) hash ^= Payload.GetHashCode(); 430 | return hash; 431 | } 432 | 433 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 434 | public override string ToString() { 435 | return pb::JsonFormatter.ToDiagnosticString(this); 436 | } 437 | 438 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 439 | public void WriteTo(pb::CodedOutputStream output) { 440 | if (Status != 0) { 441 | output.WriteRawTag(8); 442 | output.WriteInt32(Status); 443 | } 444 | if (Message.Length != 0) { 445 | output.WriteRawTag(18); 446 | output.WriteString(Message); 447 | } 448 | if (Payload.Length != 0) { 449 | output.WriteRawTag(26); 450 | output.WriteBytes(Payload); 451 | } 452 | } 453 | 454 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 455 | public int CalculateSize() { 456 | int size = 0; 457 | if (Status != 0) { 458 | size += 1 + pb::CodedOutputStream.ComputeInt32Size(Status); 459 | } 460 | if (Message.Length != 0) { 461 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); 462 | } 463 | if (Payload.Length != 0) { 464 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Payload); 465 | } 466 | return size; 467 | } 468 | 469 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 470 | public void MergeFrom(Response other) { 471 | if (other == null) { 472 | return; 473 | } 474 | if (other.Status != 0) { 475 | Status = other.Status; 476 | } 477 | if (other.Message.Length != 0) { 478 | Message = other.Message; 479 | } 480 | if (other.Payload.Length != 0) { 481 | Payload = other.Payload; 482 | } 483 | } 484 | 485 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 486 | public void MergeFrom(pb::CodedInputStream input) { 487 | uint tag; 488 | while ((tag = input.ReadTag()) != 0) { 489 | switch(tag) { 490 | default: 491 | input.SkipLastField(); 492 | break; 493 | case 8: { 494 | Status = input.ReadInt32(); 495 | break; 496 | } 497 | case 18: { 498 | Message = input.ReadString(); 499 | break; 500 | } 501 | case 26: { 502 | Payload = input.ReadBytes(); 503 | break; 504 | } 505 | } 506 | } 507 | } 508 | 509 | } 510 | 511 | /// 512 | /// ProposalResponsePayload is the payload of a proposal response. This message 513 | /// is the "bridge" between the client's request and the endorser's action in 514 | /// response to that request. Concretely, for chaincodes, it contains a hashed 515 | /// representation of the proposal (proposalHash) and a representation of the 516 | /// chaincode state changes and events inside the extension field. 517 | /// 518 | public sealed partial class ProposalResponsePayload : pb::IMessage { 519 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ProposalResponsePayload()); 520 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 521 | public static pb::MessageParser Parser { get { return _parser; } } 522 | 523 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 524 | public static pbr::MessageDescriptor Descriptor { 525 | get { return global::Chaincode.NET.Protos.ProposalResponseReflection.Descriptor.MessageTypes[2]; } 526 | } 527 | 528 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 529 | pbr::MessageDescriptor pb::IMessage.Descriptor { 530 | get { return Descriptor; } 531 | } 532 | 533 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 534 | public ProposalResponsePayload() { 535 | OnConstruction(); 536 | } 537 | 538 | partial void OnConstruction(); 539 | 540 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 541 | public ProposalResponsePayload(ProposalResponsePayload other) : this() { 542 | proposalHash_ = other.proposalHash_; 543 | extension_ = other.extension_; 544 | } 545 | 546 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 547 | public ProposalResponsePayload Clone() { 548 | return new ProposalResponsePayload(this); 549 | } 550 | 551 | /// Field number for the "proposal_hash" field. 552 | public const int ProposalHashFieldNumber = 1; 553 | private pb::ByteString proposalHash_ = pb::ByteString.Empty; 554 | /// 555 | /// Hash of the proposal that triggered this response. The hash is used to 556 | /// link a response with its proposal, both for bookeeping purposes on an 557 | /// asynchronous system and for security reasons (accountability, 558 | /// non-repudiation). The hash usually covers the entire Proposal message 559 | /// (byte-by-byte). However this implies that the hash can only be verified 560 | /// if the entire proposal message is available when ProposalResponsePayload is 561 | /// included in a transaction or stored in the ledger. For confidentiality 562 | /// reasons, with chaincodes it might be undesirable to store the proposal 563 | /// payload in the ledger. If the type is CHAINCODE, this is handled by 564 | /// separating the proposal's header and 565 | /// the payload: the header is always hashed in its entirety whereas the 566 | /// payload can either be hashed fully, or only its hash may be hashed, or 567 | /// nothing from the payload can be hashed. The PayloadVisibility field in the 568 | /// Header's extension controls to which extent the proposal payload is 569 | /// "visible" in the sense that was just explained. 570 | /// 571 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 572 | public pb::ByteString ProposalHash { 573 | get { return proposalHash_; } 574 | set { 575 | proposalHash_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 576 | } 577 | } 578 | 579 | /// Field number for the "extension" field. 580 | public const int ExtensionFieldNumber = 2; 581 | private pb::ByteString extension_ = pb::ByteString.Empty; 582 | /// 583 | /// Extension should be unmarshaled to a type-specific message. The type of 584 | /// the extension in any proposal response depends on the type of the proposal 585 | /// that the client selected when the proposal was initially sent out. In 586 | /// particular, this information is stored in the type field of a Header. For 587 | /// chaincode, it's a ChaincodeAction message 588 | /// 589 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 590 | public pb::ByteString Extension { 591 | get { return extension_; } 592 | set { 593 | extension_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 594 | } 595 | } 596 | 597 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 598 | public override bool Equals(object other) { 599 | return Equals(other as ProposalResponsePayload); 600 | } 601 | 602 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 603 | public bool Equals(ProposalResponsePayload other) { 604 | if (ReferenceEquals(other, null)) { 605 | return false; 606 | } 607 | if (ReferenceEquals(other, this)) { 608 | return true; 609 | } 610 | if (ProposalHash != other.ProposalHash) return false; 611 | if (Extension != other.Extension) return false; 612 | return true; 613 | } 614 | 615 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 616 | public override int GetHashCode() { 617 | int hash = 1; 618 | if (ProposalHash.Length != 0) hash ^= ProposalHash.GetHashCode(); 619 | if (Extension.Length != 0) hash ^= Extension.GetHashCode(); 620 | return hash; 621 | } 622 | 623 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 624 | public override string ToString() { 625 | return pb::JsonFormatter.ToDiagnosticString(this); 626 | } 627 | 628 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 629 | public void WriteTo(pb::CodedOutputStream output) { 630 | if (ProposalHash.Length != 0) { 631 | output.WriteRawTag(10); 632 | output.WriteBytes(ProposalHash); 633 | } 634 | if (Extension.Length != 0) { 635 | output.WriteRawTag(18); 636 | output.WriteBytes(Extension); 637 | } 638 | } 639 | 640 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 641 | public int CalculateSize() { 642 | int size = 0; 643 | if (ProposalHash.Length != 0) { 644 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(ProposalHash); 645 | } 646 | if (Extension.Length != 0) { 647 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Extension); 648 | } 649 | return size; 650 | } 651 | 652 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 653 | public void MergeFrom(ProposalResponsePayload other) { 654 | if (other == null) { 655 | return; 656 | } 657 | if (other.ProposalHash.Length != 0) { 658 | ProposalHash = other.ProposalHash; 659 | } 660 | if (other.Extension.Length != 0) { 661 | Extension = other.Extension; 662 | } 663 | } 664 | 665 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 666 | public void MergeFrom(pb::CodedInputStream input) { 667 | uint tag; 668 | while ((tag = input.ReadTag()) != 0) { 669 | switch(tag) { 670 | default: 671 | input.SkipLastField(); 672 | break; 673 | case 10: { 674 | ProposalHash = input.ReadBytes(); 675 | break; 676 | } 677 | case 18: { 678 | Extension = input.ReadBytes(); 679 | break; 680 | } 681 | } 682 | } 683 | } 684 | 685 | } 686 | 687 | /// 688 | /// An endorsement is a signature of an endorser over a proposal response. By 689 | /// producing an endorsement message, an endorser implicitly "approves" that 690 | /// proposal response and the actions contained therein. When enough 691 | /// endorsements have been collected, a transaction can be generated out of a 692 | /// set of proposal responses. Note that this message only contains an identity 693 | /// and a signature but no signed payload. This is intentional because 694 | /// endorsements are supposed to be collected in a transaction, and they are all 695 | /// expected to endorse a single proposal response/action (many endorsements 696 | /// over a single proposal response) 697 | /// 698 | public sealed partial class Endorsement : pb::IMessage { 699 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Endorsement()); 700 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 701 | public static pb::MessageParser Parser { get { return _parser; } } 702 | 703 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 704 | public static pbr::MessageDescriptor Descriptor { 705 | get { return global::Chaincode.NET.Protos.ProposalResponseReflection.Descriptor.MessageTypes[3]; } 706 | } 707 | 708 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 709 | pbr::MessageDescriptor pb::IMessage.Descriptor { 710 | get { return Descriptor; } 711 | } 712 | 713 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 714 | public Endorsement() { 715 | OnConstruction(); 716 | } 717 | 718 | partial void OnConstruction(); 719 | 720 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 721 | public Endorsement(Endorsement other) : this() { 722 | endorser_ = other.endorser_; 723 | signature_ = other.signature_; 724 | } 725 | 726 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 727 | public Endorsement Clone() { 728 | return new Endorsement(this); 729 | } 730 | 731 | /// Field number for the "endorser" field. 732 | public const int EndorserFieldNumber = 1; 733 | private pb::ByteString endorser_ = pb::ByteString.Empty; 734 | /// 735 | /// Identity of the endorser (e.g. its certificate) 736 | /// 737 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 738 | public pb::ByteString Endorser { 739 | get { return endorser_; } 740 | set { 741 | endorser_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 742 | } 743 | } 744 | 745 | /// Field number for the "signature" field. 746 | public const int SignatureFieldNumber = 2; 747 | private pb::ByteString signature_ = pb::ByteString.Empty; 748 | /// 749 | /// Signature of the payload included in ProposalResponse concatenated with 750 | /// the endorser's certificate; ie, sign(ProposalResponse.payload + endorser) 751 | /// 752 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 753 | public pb::ByteString Signature { 754 | get { return signature_; } 755 | set { 756 | signature_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 757 | } 758 | } 759 | 760 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 761 | public override bool Equals(object other) { 762 | return Equals(other as Endorsement); 763 | } 764 | 765 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 766 | public bool Equals(Endorsement other) { 767 | if (ReferenceEquals(other, null)) { 768 | return false; 769 | } 770 | if (ReferenceEquals(other, this)) { 771 | return true; 772 | } 773 | if (Endorser != other.Endorser) return false; 774 | if (Signature != other.Signature) return false; 775 | return true; 776 | } 777 | 778 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 779 | public override int GetHashCode() { 780 | int hash = 1; 781 | if (Endorser.Length != 0) hash ^= Endorser.GetHashCode(); 782 | if (Signature.Length != 0) hash ^= Signature.GetHashCode(); 783 | return hash; 784 | } 785 | 786 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 787 | public override string ToString() { 788 | return pb::JsonFormatter.ToDiagnosticString(this); 789 | } 790 | 791 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 792 | public void WriteTo(pb::CodedOutputStream output) { 793 | if (Endorser.Length != 0) { 794 | output.WriteRawTag(10); 795 | output.WriteBytes(Endorser); 796 | } 797 | if (Signature.Length != 0) { 798 | output.WriteRawTag(18); 799 | output.WriteBytes(Signature); 800 | } 801 | } 802 | 803 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 804 | public int CalculateSize() { 805 | int size = 0; 806 | if (Endorser.Length != 0) { 807 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Endorser); 808 | } 809 | if (Signature.Length != 0) { 810 | size += 1 + pb::CodedOutputStream.ComputeBytesSize(Signature); 811 | } 812 | return size; 813 | } 814 | 815 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 816 | public void MergeFrom(Endorsement other) { 817 | if (other == null) { 818 | return; 819 | } 820 | if (other.Endorser.Length != 0) { 821 | Endorser = other.Endorser; 822 | } 823 | if (other.Signature.Length != 0) { 824 | Signature = other.Signature; 825 | } 826 | } 827 | 828 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 829 | public void MergeFrom(pb::CodedInputStream input) { 830 | uint tag; 831 | while ((tag = input.ReadTag()) != 0) { 832 | switch(tag) { 833 | default: 834 | input.SkipLastField(); 835 | break; 836 | case 10: { 837 | Endorser = input.ReadBytes(); 838 | break; 839 | } 840 | case 18: { 841 | Signature = input.ReadBytes(); 842 | break; 843 | } 844 | } 845 | } 846 | } 847 | 848 | } 849 | 850 | #endregion 851 | 852 | } 853 | 854 | #endregion Designer generated code 855 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Sample/AssetHolding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using Google.Protobuf; 5 | using Microsoft.Extensions.Logging; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 8 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 9 | 10 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 11 | { 12 | public class AssetHolding : IChaincode 13 | { 14 | private readonly ChaincodeInvocationMap _invocationMap; 15 | private readonly ILogger _logger; 16 | 17 | public AssetHolding(ILogger logger) 18 | { 19 | _logger = logger; 20 | _invocationMap = new ChaincodeInvocationMap 21 | { 22 | {"invoke", InternalInvoke}, 23 | {"query", InternalQuery} 24 | }; 25 | } 26 | 27 | public async Task Init(IChaincodeStub stub) 28 | { 29 | _logger.LogInformation("=================== Example Init ==================="); 30 | 31 | var functionAndParameters = stub.GetFunctionAndParameters(); 32 | 33 | var args = functionAndParameters.Parameters; 34 | 35 | args.AssertCount(4); 36 | 37 | if (!args.TryGet(1, out var aValue) || 38 | !args.TryGet(3, out var bValue)) 39 | return Shim.Error("Expecting integer value for asset holding"); 40 | 41 | 42 | if (await stub.PutState("a", aValue) && await stub.PutState("b", bValue)) return Shim.Success(); 43 | 44 | return Shim.Error("Error during Chaincode init!"); 45 | } 46 | 47 | public Task Invoke(IChaincodeStub stub) 48 | { 49 | _logger.LogInformation("=================== Example Invoke ==================="); 50 | return _invocationMap.Invoke(stub); 51 | } 52 | 53 | private async Task InternalQuery(IChaincodeStub stub, Parameters args) 54 | { 55 | args.AssertCount(1); 56 | 57 | var a = args[0]; 58 | 59 | var aValueBytes = await stub.GetState(a); 60 | 61 | if (aValueBytes == null) throw new Exception($"Failed to get state of asset holder {a}"); 62 | 63 | _logger.LogInformation($"Query Response: name={a}, value={aValueBytes.ToStringUtf8()}"); 64 | return aValueBytes; 65 | } 66 | 67 | private async Task InternalInvoke(IChaincodeStub stub, Parameters args) 68 | { 69 | args.AssertCount(3); 70 | 71 | var a = args.Get(0); 72 | var b = args.Get(1); 73 | 74 | if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) 75 | throw new Exception("Asset holding must not be empty"); 76 | 77 | var aValue = await stub.TryGetState(a); 78 | if (!aValue.HasValue) throw new Exception("Failed to get state of asset holder A"); 79 | 80 | var bValue = await stub.TryGetState(b); 81 | if (!bValue.HasValue) throw new Exception("Failed to get state of asset holder B"); 82 | 83 | if (!args.TryGet(2, out var amount)) 84 | throw new Exception("Expecting integer value for amount to be transferred"); 85 | 86 | aValue -= amount; 87 | bValue += amount; 88 | 89 | _logger.LogInformation($"aValue = {aValue}, bValue = {bValue}"); 90 | 91 | await stub.PutState(a, aValue); 92 | await stub.PutState(b, bValue); 93 | 94 | return ByteString.Empty; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Sample/Chaincode.NET.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7.3 7 | Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 8 | Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Sample/FabCar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Chaincode.NET.Protos; 5 | using Google.Protobuf; 6 | using Microsoft.Extensions.Logging; 7 | using Newtonsoft.Json; 8 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 9 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 10 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 11 | 12 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 13 | { 14 | public class Car 15 | { 16 | public Car() 17 | { 18 | } 19 | 20 | public Car(string make, string model, string color, string owner) 21 | { 22 | Make = make; 23 | Model = model; 24 | Color = color; 25 | Owner = owner; 26 | } 27 | 28 | public string Make { get; set; } 29 | public string Model { get; set; } 30 | public string Color { get; set; } 31 | public string Owner { get; set; } 32 | public string DocType { get; set; } = "car"; 33 | 34 | public override string ToString() 35 | { 36 | return $"{Make} {Model} in {Color} with owner {Owner}"; 37 | } 38 | } 39 | 40 | public class FabCar : IChaincode 41 | { 42 | private readonly ChaincodeInvocationMap _invocationMap; 43 | private readonly ILogger _logger; 44 | 45 | public FabCar(ILogger logger) 46 | { 47 | _logger = logger; 48 | _invocationMap = new LoggingChaincodeInvocationMap(logger) 49 | { 50 | {nameof(QueryCar), QueryCar}, 51 | {nameof(InitLedger), InitLedger}, 52 | {nameof(CreateCar), CreateCar}, 53 | {nameof(QueryAllCars), QueryAllCars}, 54 | {nameof(ChangeCarOwner), ChangeCarOwner} 55 | }; 56 | } 57 | 58 | public async Task Init(IChaincodeStub stub) 59 | { 60 | _logger.LogInformation("========== Instantiated FabCar Chaincode =========="); 61 | return Shim.Success(); 62 | } 63 | 64 | public Task Invoke(IChaincodeStub stub) 65 | { 66 | _logger.LogInformation("========== Invoking ChainCode FabCar Chaincode =========="); 67 | return _invocationMap.Invoke(stub); 68 | } 69 | 70 | private async Task QueryCar(IChaincodeStub stub, Parameters args) 71 | { 72 | args.AssertCount(1); 73 | 74 | var carNumber = args.Get(0); 75 | 76 | var carBytes = await stub.GetState(carNumber); 77 | 78 | if (carBytes == null || carBytes.Length <= 0) throw new Exception($"Car {carNumber} does not exist."); 79 | 80 | _logger.LogInformation(carBytes.ToStringUtf8()); 81 | 82 | return carBytes; 83 | } 84 | 85 | private async Task InitLedger(IChaincodeStub stub, Parameters args) 86 | { 87 | var cars = new List 88 | { 89 | new Car("Toyota", "Prius", "blue", "Tomoko"), 90 | new Car("Ford", "Mustang", "red", "Brad"), 91 | new Car("Hyundai", "Tucson", "green", "Jin Soo"), 92 | new Car("Volkswagen", "Passat", "yellow", "Max"), 93 | new Car("Tesla", "S", "black", "Michael"), 94 | new Car("Peugeot", "205", "purpe", "Michel"), 95 | new Car("Chery", "522L", "white", "Aarav"), 96 | new Car("Fiat", "Punto", "violet", "Pari"), 97 | new Car("Tata", "Nano", "indigo", "Valeria"), 98 | new Car("Holden", "Barina", "brown", "Shotaro") 99 | }; 100 | 101 | for (var index = 0; index < cars.Count; index++) 102 | { 103 | var car = cars[index]; 104 | if (await stub.PutStateJson($"CAR{index}", car)) 105 | _logger.LogInformation("Added car", car.ToString()); 106 | else 107 | _logger.LogError($"Error writing car {car} onto the ledger"); 108 | } 109 | 110 | return ByteString.Empty; 111 | } 112 | 113 | private async Task CreateCar(IChaincodeStub stub, Parameters args) 114 | { 115 | args.AssertCount(5); 116 | 117 | var car = new Car(args.Get(1), args.Get(2), args.Get(3), args.Get(4)); 118 | 119 | await stub.PutState(args.Get(0), JsonConvert.SerializeObject(car)); 120 | 121 | return ByteString.Empty; 122 | } 123 | 124 | private async Task QueryAllCars(IChaincodeStub stub, Parameters args) 125 | { 126 | var startKey = "CAR0"; 127 | var endKey = "CAR999"; 128 | 129 | var iterator = await stub.GetStateByRange(startKey, endKey); 130 | 131 | var result = new List(); 132 | 133 | while (true) 134 | { 135 | var iterationResult = await iterator.Next(); 136 | 137 | if (iterationResult.Value != null && iterationResult.Value.Value.Length > 0) 138 | { 139 | var queryResult = new CarQueryResult 140 | { 141 | Key = iterationResult.Value.Key 142 | }; 143 | 144 | try 145 | { 146 | queryResult.Record = 147 | JsonConvert.DeserializeObject(iterationResult.Value.Value.ToStringUtf8()); 148 | } 149 | catch (Exception ex) 150 | { 151 | _logger.LogError(ex, 152 | $"An error occured while trying to deserialize {iterationResult.Value.Value.ToStringUtf8()}"); 153 | } 154 | 155 | result.Add(queryResult); 156 | } 157 | 158 | if (iterationResult.Done) 159 | { 160 | _logger.LogInformation("End of data"); 161 | await iterator.Close(); 162 | _logger.LogInformation("Result", result); 163 | return JsonConvert.SerializeObject(result).ToByteString(); 164 | } 165 | } 166 | } 167 | 168 | private async Task ChangeCarOwner(IChaincodeStub stub, Parameters args) 169 | { 170 | args.AssertCount(2); 171 | 172 | var car = await stub.GetStateJson(args.Get(0)); 173 | car.Owner = args.Get(1); 174 | 175 | await stub.PutStateJson(args.Get(0), car); 176 | 177 | return ByteString.Empty; 178 | } 179 | } 180 | 181 | public class CarQueryResult 182 | { 183 | public string Key { get; set; } 184 | public Car Record { get; set; } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Sample/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 6 | { 7 | internal class Program 8 | { 9 | private static async Task Main(string[] args) 10 | { 11 | using (var providerConfiguration = ProviderConfiguration.Configure(args)) 12 | { 13 | var shim = providerConfiguration.GetRequiredService(); 14 | await shim.Start(); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Chaincode.NET.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | false 7 | 8 | Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 9 | 10 | Thinktecture.HyperledgerFabric.Chaincode.NET.Sample 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Chaincode/ClientIdentityTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Chaincode.NET.Protos; 3 | using FluentAssertions; 4 | using Moq; 5 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 7 | using Xunit; 8 | 9 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Chaincode 10 | { 11 | public class ClientIdentityTest 12 | { 13 | // Original certificates from Node.js tests 14 | private const string CertificateWithoutAttributes = 15 | "-----BEGIN CERTIFICATE-----" + 16 | "MIICXTCCAgSgAwIBAgIUeLy6uQnq8wwyElU/jCKRYz3tJiQwCgYIKoZIzj0EAwIw" + 17 | "eTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh" + 18 | "biBGcmFuY2lzY28xGTAXBgNVBAoTEEludGVybmV0IFdpZGdldHMxDDAKBgNVBAsT" + 19 | "A1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTcwOTA4MDAxNTAwWhcNMTgw" + 20 | "OTA4MDAxNTAwWjBdMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xp" + 21 | "bmExFDASBgNVBAoTC0h5cGVybGVkZ2VyMQ8wDQYDVQQLEwZGYWJyaWMxDjAMBgNV" + 22 | "BAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFq/90YMuH4tWugHa" + 23 | "oyZtt4Mbwgv6CkBSDfYulVO1CVInw1i/k16DocQ/KSDTeTfgJxrX1Ree1tjpaodG" + 24 | "1wWyM6OBhTCBgjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4E" + 25 | "FgQUhKs/VJ9IWJd+wer6sgsgtZmxZNwwHwYDVR0jBBgwFoAUIUd4i/sLTwYWvpVr" + 26 | "TApzcT8zv/kwIgYDVR0RBBswGYIXQW5pbHMtTWFjQm9vay1Qcm8ubG9jYWwwCgYI" + 27 | "KoZIzj0EAwIDRwAwRAIgCoXaCdU8ZiRKkai0QiXJM/GL5fysLnmG2oZ6XOIdwtsC" + 28 | "IEmCsI8Mhrvx1doTbEOm7kmIrhQwUVDBNXCWX1t3kJVN" + 29 | "-----END CERTIFICATE-----"; 30 | 31 | private const string CertificateWithAttributes = 32 | "-----BEGIN CERTIFICATE-----" + 33 | "MIIB6TCCAY+gAwIBAgIUHkmY6fRP0ANTvzaBwKCkMZZPUnUwCgYIKoZIzj0EAwIw" + 34 | "GzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0xNzA5MDgwMzQyMDBaFw0x" + 35 | "ODA5MDgwMzQyMDBaMB4xHDAaBgNVBAMTE015VGVzdFVzZXJXaXRoQXR0cnMwWTAT" + 36 | "BgcqhkjOPQIBBggqhkjOPQMBBwNCAATmB1r3CdWvOOP3opB3DjJnW3CnN8q1ydiR" + 37 | "dzmuA6A2rXKzPIltHvYbbSqISZJubsy8gVL6GYgYXNdu69RzzFF5o4GtMIGqMA4G" + 38 | "A1UdDwEB/wQEAwICBDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTYKLTAvJJK08OM" + 39 | "VGwIhjMQpo2DrjAfBgNVHSMEGDAWgBTEs/52DeLePPx1+65VhgTwu3/2ATAiBgNV" + 40 | "HREEGzAZghdBbmlscy1NYWNCb29rLVByby5sb2NhbDAmBggqAwQFBgcIAQQaeyJh" + 41 | "dHRycyI6eyJhdHRyMSI6InZhbDEifX0wCgYIKoZIzj0EAwIDSAAwRQIhAPuEqWUp" + 42 | "svTTvBqLR5JeQSctJuz3zaqGRqSs2iW+QB3FAiAIP0mGWKcgSGRMMBvaqaLytBYo" + 43 | "9v3hRt1r8j8vN0pMcg==" + 44 | "-----END CERTIFICATE-----"; 45 | 46 | private const string CertificateWithLongDNs = 47 | "-----BEGIN CERTIFICATE-----" + 48 | "MIICGjCCAcCgAwIBAgIRAIPRwJHVLhHK47XK0BbFZJswCgYIKoZIzj0EAwIwczEL" + 49 | "MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG" + 50 | "cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh" + 51 | "Lm9yZzIuZXhhbXBsZS5jb20wHhcNMTcwNjIzMTIzMzE5WhcNMjcwNjIxMTIzMzE5" + 52 | "WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN" + 53 | "U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWVXNlcjFAb3JnMi5leGFtcGxlLmNvbTBZ" + 54 | "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBd9SsEiFH1/JIb3qMEPLR2dygokFVKW" + 55 | "eINcB0Ni4TBRkfIWWUJeCANTUY11Pm/+5gs+fBTqBz8M2UzpJDVX7+2jTTBLMA4G" + 56 | "A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIKfUfvpGproH" + 57 | "cwyFD+0sE3XfJzYNcif0jNwvgOUFZ4AFMAoGCCqGSM49BAMCA0gAMEUCIQC8NIMw" + 58 | "e4ym/QRwCJb5umbONNLSVQuEpnPsJrM/ssBPvgIgQpe2oYa3yO3USro9nBHjpM3L" + 59 | "KsFQrpVnF8O6hoHOYZQ=" + 60 | "-----END CERTIFICATE-----"; 61 | 62 | private Mock CreateChaincodeStubMock(string certificate) 63 | { 64 | var mock = new Mock(); 65 | mock.Setup(m => m.Creator) 66 | .Returns(new SerializedIdentity 67 | { 68 | Mspid = "dummyId", 69 | IdBytes = certificate.ToByteString() 70 | }); 71 | 72 | return mock; 73 | } 74 | 75 | [Fact] 76 | public void ClientIdentity_throws_an_error_when_certificate_begin_line_is_missing() 77 | { 78 | var stubMock = CreateChaincodeStubMock("e4ym/QRwCJb5umbONNLSVQuEpnPsJrM/ssBPvgIgQpe2oYa3yO3USro9nBHjpM3L" + 79 | "KsFQrpVnF8O6hoHOYZQ=" + 80 | "-----END CERTIFICATE-----"); 81 | 82 | Action action = () => new ClientIdentity(stubMock.Object); 83 | 84 | action.Should().Throw() 85 | .WithMessage("Failed to find start line or end line of the certificate."); 86 | } 87 | 88 | [Fact] 89 | public void ClientIdentity_throws_an_error_when_certificate_end_line_is_missing() 90 | { 91 | var stubMock = CreateChaincodeStubMock("-----BEGIN CERTIFICATE-----" + 92 | "e4ym/QRwCJb5umbONNLSVQuEpnPsJrM/ssBPvgIgQpe2oYa3yO3USro9nBHjpM3L" + 93 | "KsFQrpVnF8O6hoHOYZQ="); 94 | 95 | Action action = () => new ClientIdentity(stubMock.Object); 96 | 97 | action.Should().Throw() 98 | .WithMessage("Failed to find start line or end line of the certificate."); 99 | } 100 | 101 | [Fact] 102 | public void ClientIdentity_throws_an_error_when_certificate_is_empty() 103 | { 104 | var stubMock = CreateChaincodeStubMock(string.Empty); 105 | 106 | Action action = () => new ClientIdentity(stubMock.Object); 107 | 108 | action.Should().Throw() 109 | .WithMessage("Failed to find start line or end line of the certificate."); 110 | } 111 | 112 | [Fact] 113 | public void ClientIdentity_with_valid_certificate_and_attributes_is_loaded_correctly() 114 | { 115 | var stubMock = CreateChaincodeStubMock(CertificateWithAttributes); 116 | 117 | var sut = new ClientIdentity(stubMock.Object); 118 | 119 | sut.Mspid.Should().Be("dummyId"); 120 | sut.Id.Should().Be("x509::/CN=MyTestUserWithAttrs::/CN=fabric-ca-server"); 121 | sut.X509Certificate.SerialNumber.Should().Be("1E4998E9F44FD00353BF3681C0A0A431964F5275"); 122 | sut.Attributes["attr1"].Should().Be("val1"); 123 | sut.GetAttributeValue("attr1").Should().Be("val1"); 124 | sut.GetAttributeValue("unknown").Should().BeNullOrEmpty(); 125 | sut.AssertAttributeValue("attr1", "val1").Should().BeTrue(); 126 | sut.AssertAttributeValue("unknown", "val1").Should().BeFalse(); 127 | sut.AssertAttributeValue("attr1", "wrongValue").Should().BeFalse(); 128 | } 129 | 130 | [Fact] 131 | public void ClientIdentity_with_valid_certificate_and_long_dns_is_loaded_correctly() 132 | { 133 | var stubMock = CreateChaincodeStubMock(CertificateWithLongDNs); 134 | 135 | var sut = new ClientIdentity(stubMock.Object); 136 | 137 | // TODO: Check if this is ok: The format of the subject string differs from Node.js 138 | sut.Mspid.Should().Be("dummyId"); 139 | sut.Id.Should() 140 | .Be( 141 | "x509::/CN=User1@org2.example.com, L=San Francisco, S=California, C=US::/CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, S=California, C=US"); 142 | } 143 | 144 | [Fact] 145 | public void ClientIdentity_with_valid_certificate_without_attributes_is_loaded_correctly() 146 | { 147 | var stubMock = CreateChaincodeStubMock(CertificateWithoutAttributes); 148 | 149 | var sut = new ClientIdentity(stubMock.Object); 150 | 151 | sut.Mspid.Should().Be("dummyId"); 152 | sut.Attributes.Count.Should().Be(0); 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Handler/IteratorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Chaincode.NET.Protos; 3 | using FluentAssertions; 4 | using Google.Protobuf; 5 | using Moq; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 8 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators; 9 | using Xunit; 10 | 11 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Handler 12 | { 13 | // All tests are done with StateQueryIterator, since they apply to the HistoryQueryIterator as well 14 | public class IteratorTest 15 | { 16 | [Fact] 17 | public async void Close_calls_the_handlers_HandleQueryCloseState() 18 | { 19 | var handlerMock = new Mock(); 20 | handlerMock.Setup(m => m.HandleQueryCloseState("Id", "ChannelId", "TxId")) 21 | .ReturnsAsync(new QueryResponse() {HasMore = false, Id = "Id"}); 22 | 23 | var response = new QueryResponse() {Id = "Id"}; 24 | 25 | var iterator = new StateQueryIterator(handlerMock.Object, "ChannelId", "TxId", response); 26 | 27 | var result = await iterator.Close(); 28 | 29 | handlerMock.VerifyAll(); 30 | result.Id.Should().Be("Id"); 31 | } 32 | 33 | [Fact] 34 | public async void Next_emits_OnEnd_when_no_more_result_sets_are_available() 35 | { 36 | var eventDispatched = false; 37 | 38 | var iterator = new StateQueryIterator(null, null, null, new QueryResponse() 39 | { 40 | HasMore = false 41 | }); 42 | 43 | iterator.End += () => eventDispatched = true; 44 | 45 | var result = await iterator.Next(); 46 | 47 | eventDispatched.Should().BeTrue(); 48 | result.Done.Should().BeTrue(); 49 | } 50 | 51 | [Fact] 52 | public async void Next_emits_OnData_when_data_is_available() 53 | { 54 | var response = new QueryResponse() {HasMore = true}; 55 | response.Results.AddRange(new[] 56 | { 57 | new QueryResultBytes() 58 | { 59 | ResultBytes = new KV() 60 | { 61 | Key = "key1", 62 | Namespace = "namespace1", 63 | Value = "foo".ToByteString() 64 | } 65 | .ToByteString() 66 | }, 67 | new QueryResultBytes() 68 | { 69 | ResultBytes = new KV() 70 | { 71 | Key = "key2", 72 | Namespace = "namespace2", 73 | Value = "bar".ToByteString() 74 | } 75 | .ToByteString() 76 | } 77 | }); 78 | 79 | QueryResult queryResult = null; 80 | 81 | var iterator = new StateQueryIterator(null, null, null, response); 82 | 83 | iterator.Data += result => queryResult = result; 84 | 85 | var iteratorResult = await iterator.Next(); 86 | 87 | iteratorResult.Should().NotBeNull(); 88 | queryResult.Should().NotBeNull(); 89 | iteratorResult.Should().BeSameAs(queryResult); 90 | 91 | queryResult.Value.Key.Should().Be("key1"); 92 | queryResult.Value.Namespace.Should().Be("namespace1"); 93 | queryResult.Value.Value.ToStringUtf8().Should().Be("foo"); 94 | 95 | iteratorResult = await iterator.Next(); 96 | 97 | iteratorResult.Should().NotBeNull(); 98 | queryResult.Should().NotBeNull(); 99 | iteratorResult.Should().BeSameAs(queryResult); 100 | 101 | queryResult.Value.Key.Should().Be("key2"); 102 | queryResult.Value.Namespace.Should().Be("namespace2"); 103 | queryResult.Value.Value.ToStringUtf8().Should().Be("bar"); 104 | } 105 | 106 | [Fact] 107 | public async void 108 | Next_calls_handlers_HandleQueryNextState_when_results_are_processed_but_more_results_are_available() 109 | { 110 | var response = new QueryResponse(); 111 | response.Results.Add(new QueryResultBytes() 112 | { 113 | ResultBytes = 114 | new KV() 115 | { 116 | Key = "key", 117 | Namespace = "namespace", 118 | Value = "value".ToByteString() 119 | }.ToByteString() 120 | }); 121 | 122 | var handlerMock = new Mock(); 123 | handlerMock.Setup(m => m.HandleQueryStateNext("Id", "ChannelId", "TxId")) 124 | .ReturnsAsync(response); 125 | 126 | var iterator = new StateQueryIterator(handlerMock.Object, "ChannelId", "TxId", 127 | new QueryResponse() {HasMore = true, Id = "Id"}); 128 | 129 | var result = await iterator.Next(); 130 | 131 | handlerMock.VerifyAll(); 132 | result.Value.Key.Should().Be("key"); 133 | result.Value.Namespace.Should().Be("namespace"); 134 | result.Value.Value.ToStringUtf8().Should().Be("value"); 135 | } 136 | 137 | [Fact] 138 | public void 139 | Next_throws_an_exception_on_next_if_getting_the_next_query_state_throws_and_no_error_handler_is_assigned() 140 | { 141 | var handlerMock = new Mock(); 142 | handlerMock.Setup(m => m.HandleQueryStateNext(It.IsAny(), It.IsAny(), It.IsAny())) 143 | .Throws(new Exception("unittest")); 144 | 145 | var iterator = new StateQueryIterator(handlerMock.Object, null, null, new QueryResponse() {HasMore = true}); 146 | 147 | iterator.Awaiting(i => i.Next()) 148 | .Should().Throw() 149 | .WithMessage("unittest"); 150 | } 151 | 152 | [Fact] 153 | public void 154 | Next_emits_error_on_next_if_getting_the_next_query_state_throws_and_an_error_handler_is_assigned() 155 | { 156 | var handlerMock = new Mock(); 157 | handlerMock.Setup(m => m.HandleQueryStateNext(It.IsAny(), It.IsAny(), It.IsAny())) 158 | .Throws(new Exception("unittest")); 159 | 160 | var iterator = new StateQueryIterator(handlerMock.Object, null, null, new QueryResponse() {HasMore = true}); 161 | 162 | Exception exception = null; 163 | 164 | iterator.Error += ex => exception = ex; 165 | 166 | iterator.Awaiting(i => i.Next()) 167 | .Should().NotThrow(); 168 | 169 | exception.Should().NotBeNull(); 170 | exception.Message.Should().Be("unittest"); 171 | } 172 | 173 | [Fact] 174 | public async void HistoryQueryIterator_next_emits_OnData_when_data_is_available() 175 | { 176 | var response = new QueryResponse() {HasMore = true}; 177 | response.Results.AddRange(new[] 178 | { 179 | new QueryResultBytes() 180 | { 181 | ResultBytes = new KeyModification() 182 | { 183 | TxId = "txid1", 184 | IsDelete = false, 185 | Value = "foo".ToByteString() 186 | } 187 | .ToByteString() 188 | }, 189 | new QueryResultBytes() 190 | { 191 | ResultBytes = new KeyModification() 192 | { 193 | TxId = "txid2", 194 | IsDelete = true, 195 | Value = "foo".ToByteString() 196 | } 197 | .ToByteString() 198 | } 199 | }); 200 | 201 | QueryResult queryResult = null; 202 | 203 | var iterator = new HistoryQueryIterator(null, null, null, response); 204 | 205 | iterator.Data += result => queryResult = result; 206 | 207 | var iteratorResult = await iterator.Next(); 208 | 209 | iteratorResult.Should().NotBeNull(); 210 | queryResult.Should().NotBeNull(); 211 | iteratorResult.Should().BeSameAs(queryResult); 212 | 213 | queryResult.Value.IsDelete.Should().BeFalse(); 214 | queryResult.Value.TxId.Should().Be("txid1"); 215 | queryResult.Value.Value.ToStringUtf8().Should().Be("foo"); 216 | 217 | iteratorResult = await iterator.Next(); 218 | 219 | iteratorResult.Should().NotBeNull(); 220 | queryResult.Should().NotBeNull(); 221 | iteratorResult.Should().BeSameAs(queryResult); 222 | 223 | queryResult.Value.IsDelete.Should().BeTrue(); 224 | queryResult.Value.TxId.Should().Be("txid2"); 225 | queryResult.Value.Value.ToStringUtf8().Should().Be("foo"); 226 | } 227 | } 228 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Handler/ShimTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using FluentAssertions; 5 | using Google.Protobuf; 6 | using Grpc.Core; 7 | using Grpc.Core.Logging; 8 | using Microsoft.Extensions.Logging; 9 | using Microsoft.Extensions.Options; 10 | using Moq; 11 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 12 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 13 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 14 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Settings; 15 | using Xunit; 16 | 17 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Handler 18 | { 19 | public class ShimTest 20 | { 21 | [Fact] 22 | public void Creates_a_success_response_with_payload() 23 | { 24 | var sut = Shim.Success("payload".ToByteString()); 25 | 26 | sut.Payload.ToStringUtf8().Should().Be("payload"); 27 | sut.Status.Should().Be((int) ResponseCodes.Ok); 28 | } 29 | 30 | [Fact] 31 | public void Creates_an_empty_success_response() 32 | { 33 | var sut = Shim.Success(); 34 | 35 | sut.Payload.Should().BeEmpty(); 36 | sut.Status.Should().Be((int) ResponseCodes.Ok); 37 | } 38 | 39 | [Fact] 40 | public void Creates_an_error_response() 41 | { 42 | var sut = Shim.Error("foobar"); 43 | 44 | sut.Message.Should().Be("foobar"); 45 | sut.Status.Should().Be((int) ResponseCodes.Error); 46 | } 47 | 48 | [Fact] 49 | public void GrpcEnvironment_sets_logger_to_console_if_option_is_set() 50 | { 51 | var options = Options.Create(new ChaincodeSettings 52 | { 53 | CORE_PEER_ADDRESS = "example.test:9999", 54 | CORE_CHAINCODE_ID_NAME = "unittest", 55 | CORE_LOG_GRPC = true 56 | }); 57 | 58 | var _ = new Shim(options, new Mock>().Object, new Mock().Object); 59 | 60 | GrpcEnvironment.Logger.Should().BeOfType(); 61 | } 62 | 63 | [Fact] 64 | public async void Start_calls_the_handlers_chat_method() 65 | { 66 | var options = Options.Create(new ChaincodeSettings 67 | { 68 | CORE_PEER_ADDRESS = "example.test:9999", 69 | CORE_CHAINCODE_ID_NAME = "unittest" 70 | }); 71 | 72 | var message = new ChaincodeMessage 73 | { 74 | Type = ChaincodeMessage.Types.Type.Register, 75 | Payload = new ChaincodeID {Name = "unittest"}.ToByteString() 76 | }; 77 | 78 | var handlerMock = new Mock(); 79 | handlerMock.Setup(m => m.Chat(message)).Returns(Task.CompletedTask); 80 | 81 | var handlerFactoryMock = new Mock(); 82 | handlerFactoryMock.Setup(m => m.Create("example.test", 9999)) 83 | .Returns(handlerMock.Object); 84 | 85 | var shim = new Shim(options, new Mock>().Object, handlerFactoryMock.Object); 86 | var result = await shim.Start(); 87 | 88 | result.Should().BeSameAs(handlerMock.Object); 89 | 90 | handlerFactoryMock.VerifyAll(); 91 | handlerMock.VerifyAll(); 92 | } 93 | 94 | [Fact] 95 | public void Start_throws_an_error_when_peer_address_contains_a_protocol() 96 | { 97 | var options = Options.Create(new ChaincodeSettings 98 | { 99 | CORE_PEER_ADDRESS = "grpcs://example.test" 100 | }); 101 | 102 | var shim = new Shim(options, new Mock>().Object, new Mock().Object); 103 | 104 | shim.Awaiting(m => m.Start()) 105 | .Should().Throw("Peer Address should not contain any protocol information."); 106 | } 107 | 108 | [Fact] 109 | public void Start_throws_an_error_when_peer_address_port_is_missing() 110 | { 111 | var options = Options.Create(new ChaincodeSettings 112 | { 113 | CORE_PEER_ADDRESS = "example.test" 114 | }); 115 | 116 | var shim = new Shim(options, new Mock>().Object, new Mock().Object); 117 | 118 | shim.Awaiting(m => m.Start()) 119 | .Should().Throw("Please provide peer address in the format of host:port"); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Messaging/MessageQueueTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using FluentAssertions; 5 | using Grpc.Core; 6 | using Microsoft.Extensions.Logging; 7 | using Moq; 8 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 9 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 10 | using Xunit; 11 | 12 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Messaging 13 | { 14 | public class MessageQueueTest 15 | { 16 | [Fact] 17 | public async Task Correctly_handles_a_response_message() 18 | { 19 | var handlerMock = new Mock(); 20 | var streamMock = new Mock>(); 21 | 22 | handlerMock.SetupGet(m => m.WriteStream).Returns(streamMock.Object); 23 | 24 | handlerMock.Setup(m => m.ParseResponse(It.IsAny(), It.IsAny())) 25 | .Returns("foobar"); 26 | 27 | var sut = new MessageQueue(handlerMock.Object, new Mock>().Object); 28 | 29 | var taskCompletionSource = new TaskCompletionSource(); 30 | var queueMessage = new QueueMessage(new ChaincodeMessage 31 | { 32 | ChannelId = "chaincode", 33 | Txid = "message" 34 | }, 0, taskCompletionSource); 35 | 36 | await sut.QueueMessage(queueMessage); 37 | 38 | sut.HandleMessageResponse(new ChaincodeMessage 39 | { 40 | ChannelId = "chaincode", 41 | Txid = "message" 42 | }); 43 | 44 | var result = await taskCompletionSource.Task; 45 | 46 | result.Should().Be("foobar"); 47 | } 48 | 49 | [Fact] 50 | public async void Directly_sends_a_single_message() 51 | { 52 | var handlerMock = new Mock(); 53 | var streamMock = new Mock>(); 54 | 55 | handlerMock.SetupGet(m => m.WriteStream).Returns(streamMock.Object); 56 | 57 | var sut = new MessageQueue(handlerMock.Object, new Mock>().Object); 58 | 59 | var chaincodeMessage = new ChaincodeMessage 60 | { 61 | Txid = "bar", 62 | ChannelId = "foo" 63 | }; 64 | 65 | await sut.QueueMessage(new QueueMessage(chaincodeMessage, 0)); 66 | 67 | streamMock.Verify(m => m.WriteAsync(chaincodeMessage), Times.Once); 68 | } 69 | 70 | [Fact] 71 | public async void Does_not_send_two_queued_message_at_the_same_time() 72 | { 73 | var handlerMock = new Mock(); 74 | var streamMock = new Mock>(); 75 | 76 | handlerMock.SetupGet(m => m.WriteStream).Returns(streamMock.Object); 77 | 78 | var sut = new MessageQueue(handlerMock.Object, new Mock>().Object); 79 | 80 | var chaincodeMessage = new ChaincodeMessage 81 | { 82 | Txid = "bar", 83 | ChannelId = "foo" 84 | }; 85 | 86 | await sut.QueueMessage(new QueueMessage(chaincodeMessage, 0)); 87 | await sut.QueueMessage(new QueueMessage(new ChaincodeMessage(), 0)); 88 | 89 | streamMock.Verify(m => m.WriteAsync(chaincodeMessage), Times.Once); 90 | } 91 | 92 | [Fact] 93 | public async Task Throws_an_exception_when_message_could_not_be_parsed() 94 | { 95 | var handlerMock = new Mock(); 96 | var streamMock = new Mock>(); 97 | 98 | handlerMock.SetupGet(m => m.WriteStream).Returns(streamMock.Object); 99 | 100 | handlerMock.Setup(m => m.ParseResponse(It.IsAny(), It.IsAny())) 101 | .Throws(); 102 | 103 | var sut = new MessageQueue(handlerMock.Object, new Mock>().Object); 104 | 105 | var taskCompletionSource = new TaskCompletionSource(); 106 | var queueMessage = new QueueMessage(new ChaincodeMessage 107 | { 108 | ChannelId = "chaincode", 109 | Txid = "message" 110 | }, 0, taskCompletionSource); 111 | 112 | await sut.QueueMessage(queueMessage); 113 | 114 | sut.HandleMessageResponse(new ChaincodeMessage 115 | { 116 | ChannelId = "chaincode", 117 | Txid = "message" 118 | }); 119 | 120 | taskCompletionSource.Awaiting(t => t.Task) 121 | .Should().Throw(); 122 | } 123 | 124 | [Fact] 125 | public void Throws_an_exception_when_message_is_not_found() 126 | { 127 | var sut = new MessageQueue(new Mock().Object, new Mock>().Object); 128 | sut.Invoking(s => s.HandleMessageResponse(new ChaincodeMessage 129 | { 130 | ChannelId = "foo", 131 | Txid = "bar" 132 | })) 133 | .Should().Throw(); 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Messaging/QueueMessageTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using FluentAssertions; 5 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 6 | using Xunit; 7 | 8 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Messaging 9 | { 10 | public class QueueMessageTest 11 | { 12 | [Fact] 13 | public void Fail_throws_the_given_exception() 14 | { 15 | var sut = new QueueMessage(null, 0); 16 | 17 | sut.Invoking(s => s.Fail(new Exception("something went wrong"))) 18 | .Should().Throw() 19 | .WithMessage("something went wrong"); 20 | } 21 | 22 | [Fact] 23 | public void MessageTxContextId_is_built_by_MessageChannelId_and_MessageTxid() 24 | { 25 | var sut = new QueueMessage(new ChaincodeMessage 26 | { 27 | ChannelId = "foo", 28 | Txid = "bar" 29 | }, 0); 30 | 31 | sut.MessageTxContextId.Should().Be("foobar"); 32 | } 33 | } 34 | 35 | public class QueueMessageGenericTest 36 | { 37 | [Fact] 38 | public void Fail_should_set_exception_of_TaskCompletionSource() 39 | { 40 | var taskCompletionSource = new TaskCompletionSource(); 41 | var sut = new QueueMessage(null, 0, taskCompletionSource); 42 | sut.Fail(new Exception("something went wrong")); 43 | 44 | taskCompletionSource.Awaiting(t => t.Task) 45 | .Should().Throw() 46 | .WithMessage("something went wrong"); 47 | } 48 | 49 | [Fact] 50 | public async Task Success_should_result_of_TaskCompletionSource() 51 | { 52 | var taskCompletionSource = new TaskCompletionSource(); 53 | var sut = new QueueMessage(null, 0, taskCompletionSource); 54 | sut.Success(100); 55 | 56 | var result = await taskCompletionSource.Task; 57 | result.Should().Be(100); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.Test/Settings/ChaincodeSettingsTest.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Settings; 3 | using Xunit; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Sample.Settings 6 | { 7 | public class ChaincodeSettingsTest 8 | { 9 | [Fact] 10 | public void ChaincodeIdName_maps_to_external_CORE_CHAINCODE_ID_NAME() 11 | { 12 | var sut = new ChaincodeSettings {CORE_CHAINCODE_ID_NAME = "foobar"}; 13 | sut.ChaincodeIdName.Should().Be("foobar"); 14 | } 15 | 16 | [Fact] 17 | public void PeerAddress_maps_to_external_CORE_PEER_ADDRESS() 18 | { 19 | var sut = new ChaincodeSettings {CORE_PEER_ADDRESS = "foobar"}; 20 | sut.PeerAddress.Should().Be("foobar"); 21 | } 22 | 23 | [Fact] 24 | public void PeerId_maps_to_external_CORE_PEER_ID() 25 | { 26 | var sut = new ChaincodeSettings {CORE_PEER_ID = "foobar"}; 27 | sut.PeerId.Should().Be("foobar"); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaincode.NET", "Chaincode.NET\Chaincode.NET.csproj", "{32B78E58-CD8E-4F81-B89D-CE7F0DF04796}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaincode.NET.Sample", "Chaincode.NET.Sample\Chaincode.NET.Sample.csproj", "{6ECC1AAA-CD6B-43C4-9FCF-047FEEAAFFAB}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaincode.NET.Protos", "Chaincode.NET.Protos\Chaincode.NET.Protos.csproj", "{A9B96694-80F5-4D76-9BA7-17A6A22C6255}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaincode.NET.Test", "Chaincode.NET.Test\Chaincode.NET.Test.csproj", "{40046F67-B568-4AA4-A58E-65BF4423AE20}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Release|Any CPU = Release|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {32B78E58-CD8E-4F81-B89D-CE7F0DF04796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {32B78E58-CD8E-4F81-B89D-CE7F0DF04796}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {32B78E58-CD8E-4F81-B89D-CE7F0DF04796}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {32B78E58-CD8E-4F81-B89D-CE7F0DF04796}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {6ECC1AAA-CD6B-43C4-9FCF-047FEEAAFFAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {6ECC1AAA-CD6B-43C4-9FCF-047FEEAAFFAB}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {6ECC1AAA-CD6B-43C4-9FCF-047FEEAAFFAB}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {6ECC1AAA-CD6B-43C4-9FCF-047FEEAAFFAB}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {A9B96694-80F5-4D76-9BA7-17A6A22C6255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {A9B96694-80F5-4D76-9BA7-17A6A22C6255}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {A9B96694-80F5-4D76-9BA7-17A6A22C6255}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {A9B96694-80F5-4D76-9BA7-17A6A22C6255}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {40046F67-B568-4AA4-A58E-65BF4423AE20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {40046F67-B568-4AA4-A58E-65BF4423AE20}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {40046F67-B568-4AA4-A58E-65BF4423AE20}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {40046F67-B568-4AA4-A58E-65BF4423AE20}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode.NET.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 7.3 6 | Thinktecture.HyperledgerFabric.Chaincode.NET 7 | Thinktecture.HyperledgerFabric.Chaincode.NET 8 | Thinktecture.HyperledgerFabric.Chaincode.NET 9 | 1.0.0 10 | Manuel Rauber 11 | Thinktecture AG 12 | Chaincode.NET Shim for Hyperledger Fabric 13 | Chaincode.NET is a chaincode shim for Hyperledger Fabric. 14 | https://github.com/thinktecture/fabric-chaincode-netcore 15 | http://thinktecture.com/images/Logo_T_Nontransparent.png 16 | git@github.com:thinktecture/fabric-chaincode-netcore.git 17 | git 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/ChaincodeFunctionParameterInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 5 | { 6 | public class Parameters : List 7 | { 8 | public void AssertCount(int count) 9 | { 10 | if (Count != count) throw new Exception($"Incorrect number of arguments. Expecting {count}, got {Count}"); 11 | } 12 | } 13 | 14 | public class ChaincodeFunctionParameterInformation 15 | { 16 | public string Function { get; set; } 17 | public Parameters Parameters { get; set; } = new Parameters(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/ChaincodeInvocationMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Chaincode.NET.Protos; 5 | using Google.Protobuf; 6 | using Microsoft.Extensions.Logging; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 8 | 9 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 10 | { 11 | public delegate Task ChaincodeInvocationDelegate(IChaincodeStub stub, Parameters parameters); 12 | 13 | public class ChaincodeInvocationMap : Dictionary 14 | { 15 | public ChaincodeInvocationMap() 16 | : base(StringComparer.OrdinalIgnoreCase) 17 | { 18 | } 19 | 20 | public virtual async Task Invoke(IChaincodeStub stub) 21 | { 22 | var functionParameterInformation = stub.GetFunctionAndParameters(); 23 | 24 | if (!ContainsKey(functionParameterInformation.Function)) 25 | return Shim.Error( 26 | $"Chaincode invoked with unknown method name: {functionParameterInformation.Function}"); 27 | 28 | try 29 | { 30 | return Shim.Success( 31 | await this[functionParameterInformation.Function](stub, functionParameterInformation.Parameters) 32 | ); 33 | } 34 | catch (Exception ex) 35 | { 36 | return Shim.Error(ex); 37 | } 38 | } 39 | } 40 | 41 | public class LoggingChaincodeInvocationMap : ChaincodeInvocationMap 42 | { 43 | private readonly ILogger _logger; 44 | 45 | public LoggingChaincodeInvocationMap(ILogger logger) 46 | { 47 | _logger = logger; 48 | } 49 | 50 | public override async Task Invoke(IChaincodeStub stub) 51 | { 52 | var function = stub.GetFunctionAndParameters().Function; 53 | 54 | _logger.LogInformation($"========== START: {function} =========="); 55 | var result = await base.Invoke(stub); 56 | _logger.LogInformation($"========== END: {function} =========="); 57 | 58 | return result; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/ChaincodeStub.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Threading.Tasks; 6 | using Chaincode.NET.Protos; 7 | using Google.Protobuf; 8 | using Google.Protobuf.Collections; 9 | using Google.Protobuf.WellKnownTypes; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Logging; 12 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 13 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 14 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators; 15 | 16 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 17 | { 18 | public class ChaincodeStubFactory : IChaincodeStubFactory 19 | { 20 | private readonly IServiceProvider _serviceProvider; 21 | 22 | public ChaincodeStubFactory(IServiceProvider serviceProvider) 23 | { 24 | _serviceProvider = serviceProvider; 25 | } 26 | 27 | public IChaincodeStub Create( 28 | IHandler handler, 29 | string channelId, 30 | string txId, 31 | ChaincodeInput chaincodeInput, 32 | SignedProposal signedProposal 33 | ) 34 | { 35 | using (var scope = _serviceProvider.GetRequiredService().CreateScope()) 36 | { 37 | return new ChaincodeStub(handler, channelId, txId, chaincodeInput, signedProposal, 38 | scope.ServiceProvider.GetRequiredService>()); 39 | } 40 | } 41 | } 42 | 43 | public class ChaincodeStub : IChaincodeStub 44 | { 45 | private const char EmptyKeySubstitute = '\x01'; 46 | private const char MinUnicodeRuneValue = '\u0000'; 47 | private const char CompositekeyNs = '\x00'; 48 | 49 | private readonly IHandler _handler; 50 | private readonly ILogger _logger; 51 | private readonly string MaxUnicodeRuneValue = char.ConvertFromUtf32(0x10ffff); 52 | private Proposal _proposal; 53 | 54 | public ChaincodeStub( 55 | IHandler handler, 56 | string channelId, 57 | string txId, 58 | ChaincodeInput chaincodeInput, 59 | SignedProposal signedProposal, 60 | ILogger logger 61 | ) 62 | { 63 | _handler = handler; 64 | ChannelId = channelId; 65 | TxId = txId; 66 | _logger = logger; 67 | 68 | Args = chaincodeInput.Args.Select(entry => entry.ToStringUtf8()).ToList(); 69 | 70 | DecodedSignedProposal = ValidateSignedProposal(signedProposal); 71 | } 72 | 73 | public ChaincodeFunctionParameterInformation GetFunctionAndParameters() 74 | { 75 | if (Args.Count < 1) return null; 76 | 77 | var result = new ChaincodeFunctionParameterInformation 78 | { 79 | Function = Args.First().ToLower() 80 | // TODO: For usage later wrap this into a class and provide nice methods for access 81 | }; 82 | 83 | result.Parameters.AddRange(Args.Skip(1)); 84 | 85 | return result; 86 | } 87 | 88 | public Task GetState(string key) 89 | { 90 | _logger.LogInformation($"{nameof(GetState)} called with key: {key}"); 91 | 92 | return _handler.HandleGetState(string.Empty, key, ChannelId, TxId); 93 | } 94 | 95 | public Task PutState(string key, ByteString value) 96 | { 97 | return _handler.HandlePutState(string.Empty, key, value, ChannelId, TxId); 98 | } 99 | 100 | public Task DeleteState(string key) 101 | { 102 | return _handler.HandleDeleteState(string.Empty, key, ChannelId, TxId); 103 | } 104 | 105 | public Task GetStateByRange(string startKey, string endKey) 106 | { 107 | if (string.IsNullOrEmpty(startKey)) startKey = EmptyKeySubstitute.ToString(); 108 | 109 | return _handler.HandleGetStateByRange(string.Empty, startKey, endKey, ChannelId, TxId); 110 | } 111 | 112 | public Task GetQueryResult(string query) 113 | { 114 | return _handler.HandleGetQueryResult(string.Empty, query, ChannelId, TxId); 115 | } 116 | 117 | public Task GetHistoryForKey(string key) 118 | { 119 | return _handler.HandleGetHistoryForKey(key, ChannelId, TxId); 120 | } 121 | 122 | public Task InvokeChaincode(string chaincodeName, IEnumerable args, string channel = "") 123 | { 124 | if (!string.IsNullOrEmpty(channel)) chaincodeName = $"{chaincodeName}/{channel}"; 125 | 126 | return _handler.HandleInvokeChaincode(chaincodeName, args, ChannelId, TxId); 127 | } 128 | 129 | public void SetEvent(string name, ByteString payload) 130 | { 131 | if (string.IsNullOrEmpty(name)) throw new Exception("Event name must be a non-empty string"); 132 | 133 | var @event = new ChaincodeEvent 134 | { 135 | EventName = name, 136 | Payload = payload 137 | }; 138 | 139 | ChaincodeEvent = @event; 140 | } 141 | 142 | public string CreateCompositeKey(string objectType, IEnumerable attributes) 143 | { 144 | ValidateCompositeKeyAttribute(objectType); 145 | 146 | var compositeKey = CompositekeyNs + objectType + MinUnicodeRuneValue; 147 | 148 | foreach (var attribute in attributes) 149 | { 150 | ValidateCompositeKeyAttribute(attribute); 151 | compositeKey += attribute + MinUnicodeRuneValue; 152 | } 153 | 154 | return compositeKey; 155 | } 156 | 157 | public (string ObjectType, IList Attributes) SplitCompositeKey(string compositeKey) 158 | { 159 | if (string.IsNullOrEmpty(compositeKey) || compositeKey[0] != CompositekeyNs) 160 | return (null, new string[] { }); 161 | 162 | var splitKey = compositeKey.Substring(1).Split(MinUnicodeRuneValue).ToList(); 163 | string objectType = null; 164 | var attributes = new List(); 165 | 166 | if (splitKey.Count > 0 && !string.IsNullOrEmpty(splitKey[0])) 167 | { 168 | objectType = splitKey[0]; 169 | splitKey.RemoveAt(splitKey.Count - 1); 170 | 171 | if (splitKey.Count > 1) 172 | { 173 | splitKey.RemoveAt(0); 174 | attributes = splitKey; 175 | } 176 | } 177 | 178 | return (objectType, attributes); 179 | } 180 | 181 | public Task GetStateByPartialCompositeKey(string objectType, IList attributes) 182 | { 183 | var partialCompositeKey = CreateCompositeKey(objectType, attributes); 184 | 185 | return GetStateByRange(partialCompositeKey, partialCompositeKey + MaxUnicodeRuneValue); 186 | } 187 | 188 | public Task GetPrivateData(string collection, string key) 189 | { 190 | ValidateCollection(collection); 191 | return _handler.HandleGetState(collection, key, ChannelId, TxId); 192 | } 193 | 194 | 195 | public Task PutPrivateData(string collection, string key, ByteString value) 196 | { 197 | ValidateCollection(collection); 198 | return _handler.HandlePutState(collection, key, value, ChannelId, TxId); 199 | } 200 | 201 | public Task DeletePrivateData(string collection, string key) 202 | { 203 | ValidateCollection(collection); 204 | return _handler.HandleDeleteState(collection, key, ChannelId, TxId); 205 | } 206 | 207 | public Task GetPrivateDataByRange(string collection, string startKey, string endKey) 208 | { 209 | ValidateCollection(collection); 210 | 211 | if (string.IsNullOrEmpty(startKey)) startKey = EmptyKeySubstitute.ToString(); 212 | 213 | return _handler.HandleGetStateByRange(collection, startKey, endKey, ChannelId, TxId); 214 | } 215 | 216 | public Task GetPrivateDataByPartialCompositeKey( 217 | string collection, 218 | string objectType, 219 | IList attributes 220 | ) 221 | { 222 | ValidateCollection(collection); 223 | var partialCompositeKey = CreateCompositeKey(objectType, attributes); 224 | 225 | return GetPrivateDataByRange(collection, partialCompositeKey, partialCompositeKey + MaxUnicodeRuneValue); 226 | } 227 | 228 | public Task GetPrivateDataQueryResult(string collection, string query) 229 | { 230 | ValidateCollection(collection); 231 | return _handler.HandleGetQueryResult(collection, query, ChannelId, TxId); 232 | } 233 | 234 | private DecodedSignedProposal ValidateSignedProposal(SignedProposal signedProposal) 235 | { 236 | if (signedProposal == null) return null; 237 | 238 | var decodedSignedProposal = new DecodedSignedProposal 239 | { 240 | Signature = signedProposal.Signature 241 | }; 242 | 243 | try 244 | { 245 | _proposal = Proposal.Parser.ParseFrom(signedProposal.ProposalBytes); 246 | decodedSignedProposal.Proposal = new ChaincodeProposal(); 247 | } 248 | catch (Exception ex) 249 | { 250 | throw new Exception($"Failed extracting proposal from signedProposal: {ex}"); 251 | } 252 | 253 | if (_proposal.Header == null || _proposal.Header.Length == 0) 254 | throw new Exception("Proposal header is empty"); 255 | 256 | if (_proposal.Payload == null || _proposal.Payload.Length == 0) 257 | throw new Exception("Proposal payload is empty"); 258 | 259 | Header header; 260 | 261 | try 262 | { 263 | header = Header.Parser.ParseFrom(_proposal.Header); 264 | decodedSignedProposal.Proposal.Header = new ChaincodeProposalHeader(); 265 | } 266 | catch (Exception ex) 267 | { 268 | throw new Exception($"Could not extract the header from the proposal: {ex}"); 269 | } 270 | 271 | SignatureHeader signatureHeader; 272 | 273 | try 274 | { 275 | signatureHeader = SignatureHeader.Parser.ParseFrom(header.SignatureHeader); 276 | decodedSignedProposal.Proposal.Header.SignatureHeader = 277 | new ChaincodeProposalHeaderSignatureHeader {Nonce = signatureHeader.Nonce}; 278 | } 279 | catch (Exception ex) 280 | { 281 | throw new Exception($"Decoding SignatureHeader failed: {ex}"); 282 | } 283 | 284 | try 285 | { 286 | var creator = SerializedIdentity.Parser.ParseFrom(signatureHeader.Creator); 287 | decodedSignedProposal.Proposal.Header.SignatureHeader.Creator = creator; 288 | Creator = creator; 289 | } 290 | catch (Exception ex) 291 | { 292 | throw new Exception($"Decoding SerializedIdentity failed: {ex}"); 293 | } 294 | 295 | try 296 | { 297 | var channelHeader = ChannelHeader.Parser.ParseFrom(header.ChannelHeader); 298 | decodedSignedProposal.Proposal.Header.ChannelHeader = channelHeader; 299 | 300 | TxTimestamp = channelHeader.Timestamp; 301 | } 302 | catch (Exception ex) 303 | { 304 | throw new Exception($"Decoding ChannelHeader failed: {ex}"); 305 | } 306 | 307 | ChaincodeProposalPayload payload; 308 | 309 | try 310 | { 311 | payload = ChaincodeProposalPayload.Parser.ParseFrom(_proposal.Payload); 312 | decodedSignedProposal.Proposal.Payload = payload; 313 | } 314 | catch (Exception ex) 315 | { 316 | throw new Exception($"Decoding ChaincodeProposalPayload failed: {ex}"); 317 | } 318 | 319 | TransientMap = payload.TransientMap; 320 | 321 | Binding = ComputeProposalBinding(decodedSignedProposal); 322 | 323 | return decodedSignedProposal; 324 | } 325 | 326 | private string ComputeProposalBinding(DecodedSignedProposal decodedSignedProposal) 327 | { 328 | var nonce = decodedSignedProposal.Proposal.Header.SignatureHeader.Nonce.ToByteArray(); 329 | var creator = decodedSignedProposal.Proposal.Header.SignatureHeader.Creator.ToByteArray(); 330 | var epoch = decodedSignedProposal.Proposal.Header.ChannelHeader.Epoch; 331 | 332 | var epochBuffer = BitConverter.GetBytes(epoch); 333 | 334 | var total = nonce.Concat(creator).Concat(epochBuffer); 335 | 336 | using (var sha256 = SHA256.Create()) 337 | { 338 | return sha256.ComputeHash(total.ToArray()).ByteArrayToHexString(); 339 | } 340 | } 341 | 342 | private void ValidateCollection(string collection) 343 | { 344 | if (string.IsNullOrEmpty(collection)) throw new Exception("collection must be a valid string"); 345 | } 346 | 347 | private void ValidateCompositeKeyAttribute(string objectType) 348 | { 349 | if (string.IsNullOrEmpty(objectType)) 350 | throw new Exception("objectType or attribute not a non-zero length string"); 351 | } 352 | 353 | // ReSharper disable MemberCanBePrivate.Global 354 | public ChaincodeEvent ChaincodeEvent { get; private set; } 355 | public string Binding { get; private set; } 356 | public Timestamp TxTimestamp { get; private set; } 357 | public DecodedSignedProposal DecodedSignedProposal { get; } 358 | public MapField TransientMap { get; private set; } 359 | public string ChannelId { get; } 360 | public string TxId { get; } 361 | public IList Args { get; } 362 | 363 | public SerializedIdentity Creator { get; private set; } 364 | // ReSharper restore MemberCanBePrivate.Global 365 | } 366 | 367 | public class ChaincodeProposalHeader 368 | { 369 | public ChaincodeProposalHeaderSignatureHeader SignatureHeader { get; set; } 370 | public ChannelHeader ChannelHeader { get; set; } 371 | } 372 | 373 | public class ChaincodeProposalHeaderSignatureHeader 374 | { 375 | public ByteString Nonce { get; set; } 376 | public SerializedIdentity Creator { get; set; } 377 | } 378 | 379 | public class ChaincodeProposal 380 | { 381 | public ChaincodeProposalHeader Header { get; set; } 382 | public ChaincodeProposalPayload Payload { get; set; } 383 | } 384 | 385 | public class DecodedSignedProposal 386 | { 387 | public ByteString Signature { get; set; } 388 | public ChaincodeProposal Proposal { get; set; } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/ClientIdentity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Cryptography.X509Certificates; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using Newtonsoft.Json; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 12 | { 13 | public class ClientIdentity : IClientIdentity 14 | { 15 | private const string FabricCertAttrOid = "1.2.3.4.5.6.7.8.1"; 16 | private readonly IChaincodeStub _chaincodeStub; 17 | 18 | public ClientIdentity(IChaincodeStub chaincodeStub) 19 | { 20 | _chaincodeStub = chaincodeStub; 21 | LoadFromStub(); 22 | } 23 | 24 | public string Id { get; private set; } 25 | public string Mspid { get; private set; } 26 | public X509Certificate2 X509Certificate { get; private set; } 27 | 28 | public IDictionary Attributes { get; private set; } = 29 | new ConcurrentDictionary(); 30 | 31 | public string GetAttributeValue(string attributeName) 32 | { 33 | return Attributes.TryGetValue(attributeName, out var value) ? value : null; 34 | } 35 | 36 | public bool AssertAttributeValue(string attributeName, string attributeValue) 37 | { 38 | return GetAttributeValue(attributeName) == attributeValue; 39 | } 40 | 41 | private void LoadFromStub() 42 | { 43 | var signingId = _chaincodeStub.Creator; 44 | 45 | Mspid = signingId.Mspid; 46 | 47 | var idBytes = signingId.IdBytes.ToByteArray(); 48 | var normalizedCertficate = NormalizeCertificate(Encoding.UTF8.GetString(idBytes)); 49 | X509Certificate = new X509Certificate2(Encoding.UTF8.GetBytes(normalizedCertficate)); 50 | 51 | var extension = X509Certificate?.Extensions[FabricCertAttrOid]; 52 | 53 | if (extension != null) 54 | { 55 | var attributesJsonString = Encoding.UTF8.GetString(extension.RawData); 56 | var attributeObject = JsonConvert.DeserializeObject(attributesJsonString); 57 | 58 | if (attributeObject.ContainsKey("attrs")) 59 | Attributes = attributeObject.Value("attrs") 60 | .Properties() 61 | .ToDictionary(token => token.Name, token => token.Value.ToString()); 62 | } 63 | 64 | Id = $"x509::/{X509Certificate.Subject}::/{X509Certificate.Issuer}"; 65 | } 66 | 67 | private string NormalizeCertificate(string raw) 68 | { 69 | var matches = Regex.Match(raw, 70 | @"(\-\-\-\-\-\s*BEGIN ?[^-]+?\-\-\-\-\-)([\s\S]*)(\-\-\-\-\-\s*END ?[^-]+?\-\-\-\-\-)"); 71 | 72 | if (!matches.Success || matches.Groups.Count != 4) 73 | throw new Exception("Failed to find start line or end line of the certificate."); 74 | 75 | var trimmedMatches = new string[3]; 76 | 77 | for (var i = 1; i < matches.Groups.Count; i++) trimmedMatches[i - 1] = matches.Groups[i].Value.Trim(); 78 | 79 | return $"{string.Join("\n", trimmedMatches)}\n"; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/IChaincode.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Chaincode.NET.Protos; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 5 | { 6 | public interface IChaincode 7 | { 8 | Task Init(IChaincodeStub stub); 9 | Task Invoke(IChaincodeStub stub); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/IChaincodeStub.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using Google.Protobuf; 5 | using Google.Protobuf.Collections; 6 | using Google.Protobuf.WellKnownTypes; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators; 8 | 9 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 10 | { 11 | public interface IChaincodeStub 12 | { 13 | ChaincodeEvent ChaincodeEvent { get; } 14 | string Binding { get; } 15 | Timestamp TxTimestamp { get; } 16 | DecodedSignedProposal DecodedSignedProposal { get; } 17 | MapField TransientMap { get; } 18 | string ChannelId { get; } 19 | string TxId { get; } 20 | IList Args { get; } 21 | SerializedIdentity Creator { get; } 22 | ChaincodeFunctionParameterInformation GetFunctionAndParameters(); 23 | Task GetState(string key); 24 | Task PutState(string key, ByteString value); 25 | Task DeleteState(string key); 26 | Task GetStateByRange(string startKey, string endKey); 27 | Task GetQueryResult(string query); 28 | Task GetHistoryForKey(string key); 29 | Task InvokeChaincode(string chaincodeName, IEnumerable args, string channel = ""); 30 | void SetEvent(string name, ByteString payload); 31 | string CreateCompositeKey(string objectType, IEnumerable attributes); 32 | (string ObjectType, IList Attributes) SplitCompositeKey(string compositeKey); 33 | Task GetStateByPartialCompositeKey(string objectType, IList attributes); 34 | Task GetPrivateData(string collection, string key); 35 | Task PutPrivateData(string collection, string key, ByteString value); 36 | Task DeletePrivateData(string collection, string key); 37 | Task GetPrivateDataByRange(string collection, string startKey, string endKey); 38 | 39 | Task GetPrivateDataByPartialCompositeKey( 40 | string collection, 41 | string objectType, 42 | IList attributes 43 | ); 44 | 45 | Task GetPrivateDataQueryResult(string collection, string query); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/IChaincodeStubFactory.cs: -------------------------------------------------------------------------------- 1 | using Chaincode.NET.Protos; 2 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 5 | { 6 | public interface IChaincodeStubFactory 7 | { 8 | IChaincodeStub Create( 9 | IHandler handler, 10 | string channelId, 11 | string txId, 12 | ChaincodeInput chaincodeInput, 13 | SignedProposal signedProposal 14 | ); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/IClientIdentity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Security.Cryptography.X509Certificates; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 5 | { 6 | public interface IClientIdentity 7 | { 8 | string Id { get; } 9 | string Mspid { get; } 10 | X509Certificate2 X509Certificate { get; } 11 | IDictionary Attributes { get; } 12 | string GetAttributeValue(string attributeName); 13 | bool AssertAttributeValue(string attributeName, string attributeValue); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Chaincode/ResponseCodes.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode 2 | { 3 | public enum ResponseCodes 4 | { 5 | Ok = 200, 6 | ErrorThreshold = 400, 7 | Error = 500 8 | } 9 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/ByteArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 4 | { 5 | public static class ByteArrayExtensions 6 | { 7 | // https://stackoverflow.com/a/311179/959687 8 | public static string ByteArrayToHexString(this byte[] ba) 9 | { 10 | var hex = new StringBuilder(ba.Length * 2); 11 | foreach (var b in ba) 12 | hex.AppendFormat("{0:x2}", b); 13 | return hex.ToString(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/ByteStringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Google.Protobuf; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 5 | { 6 | public static class ByteStringExtensions 7 | { 8 | public static T Convert(this ByteString byteString) 9 | { 10 | var stringValue = byteString.ToStringUtf8(); 11 | 12 | if (stringValue is T t) return t; 13 | 14 | return (T) System.Convert.ChangeType(stringValue, typeof(T), CultureInfo.InvariantCulture); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/ChaincodeFunctionParameterInformationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 6 | { 7 | public static class ChaincodeFunctionParameterInformationExtensions 8 | { 9 | /// 10 | /// Returns the given converted to . 11 | /// 12 | /// An instance of 13 | /// The numeric index to get. 14 | /// Result type 15 | /// Returns the value of the given 16 | public static T Get(this ChaincodeFunctionParameterInformation info, int index) 17 | { 18 | return info.Parameters.Get(index); 19 | } 20 | 21 | /// 22 | /// Returns the given converted to . 23 | /// 24 | /// An instance of 25 | /// The numeric index to get. 26 | /// Result type 27 | /// Returns the value of the given 28 | public static T Get(this Parameters parameters, int index) 29 | { 30 | if (index < 0) throw new ArgumentException("index can not be less than zero", nameof(index)); 31 | 32 | if (index > parameters.Count) return default; // TODO: or throw? 33 | 34 | var value = parameters[index]; 35 | 36 | if (value == null) // TODO: can this really happen? 37 | throw new InvalidCastException($"Can not cast null value to {typeof(T)}"); 38 | 39 | if (value is T t) return t; 40 | 41 | return (T) Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); 42 | } 43 | 44 | /// 45 | /// Tries to return the given converted to . 46 | /// 47 | /// An instance of 48 | /// The numeric index to get. 49 | /// The converted value 50 | /// Result type 51 | /// True, if conversion was successful. Otherwise false. 52 | public static bool TryGet( 53 | this ChaincodeFunctionParameterInformation info, 54 | int index, 55 | out T convertedValue 56 | ) 57 | { 58 | return info.Parameters.TryGet(index, out convertedValue); 59 | } 60 | 61 | /// 62 | /// Tries to return the given converted to . 63 | /// 64 | /// An instance of 65 | /// The numeric index to get. 66 | /// The converted value 67 | /// Result type 68 | /// True, if conversion was successful. Otherwise false. 69 | public static bool TryGet(this Parameters parameters, int index, out T convertedValue) 70 | { 71 | convertedValue = default; 72 | 73 | try 74 | { 75 | convertedValue = parameters.Get(index); 76 | return true; 77 | } 78 | catch 79 | { 80 | return false; 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/ChaincodeStubExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Newtonsoft.Json; 3 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 6 | { 7 | public static class ChaincodeStubExtensions 8 | { 9 | public static async Task GetStateJson(this IChaincodeStub stub, string key) 10 | where T : class 11 | { 12 | return JsonConvert.DeserializeObject(await stub.GetState(key)); 13 | } 14 | 15 | public static async Task GetState(this IChaincodeStub stub, string key) 16 | { 17 | var stateResult = await stub.GetState(key); 18 | return stateResult.Convert(); 19 | } 20 | 21 | public static async Task TryGetState(this IChaincodeStub stub, string key) 22 | where T : struct 23 | { 24 | try 25 | { 26 | var stateResult = await stub.GetState(key); 27 | return stateResult.Convert(); 28 | } 29 | catch 30 | { 31 | return null; 32 | } 33 | } 34 | 35 | public static async Task DeleteState(this IChaincodeStub stub, string key) 36 | { 37 | return await stub.DeleteState(key).InvokeSafe(); 38 | } 39 | 40 | public static Task PutStateJson(this IChaincodeStub stub, string key, T value) 41 | where T : class 42 | { 43 | return stub.PutState(key, JsonConvert.SerializeObject(value)); 44 | } 45 | 46 | public static async Task PutState(this IChaincodeStub stub, string key, T value) 47 | { 48 | return await stub.PutState(key, value.ToString().ToByteString()).InvokeSafe(); 49 | } 50 | 51 | public static async Task GetPrivateData(this IChaincodeStub stub, string collection, string key) 52 | { 53 | var stateResult = await stub.GetPrivateData(collection, key); 54 | return stateResult.Convert(); 55 | } 56 | 57 | public static async Task DeletePrivateData(this IChaincodeStub stub, string collection, string key) 58 | { 59 | return await stub.DeletePrivateData(collection, key).InvokeSafe(); 60 | } 61 | 62 | public static async Task PutPrivateData( 63 | this IChaincodeStub stub, 64 | string collection, 65 | string key, 66 | T value 67 | ) 68 | { 69 | return await stub.PutPrivateData(collection, key, value.ToString().ToByteString()).InvokeSafe(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/IntExtensions.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 4 | { 5 | public static class IntExtensions 6 | { 7 | public static ByteString ToByteString(this int value) 8 | { 9 | return value.ToString().ToByteString(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Google.Protobuf; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 6 | { 7 | public static class StringExtensions 8 | { 9 | public static ByteString ToByteString(this string input) 10 | { 11 | var base64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(input)); 12 | return ByteString.FromBase64(base64String); 13 | } 14 | 15 | // https://stackoverflow.com/a/311179/959687 16 | public static byte[] StringToByteArray(this string hex) 17 | { 18 | var numberChars = hex.Length; 19 | var bytes = new byte[numberChars / 2]; 20 | for (var i = 0; i < numberChars; i += 2) 21 | bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); 22 | return bytes; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Extensions/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions 4 | { 5 | public static class TaskExtensions 6 | { 7 | public static async Task InvokeSafe(this Task task) 8 | { 9 | try 10 | { 11 | await task; 12 | return true; 13 | } 14 | catch 15 | { 16 | return false; 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/ChaincodeSupportClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Chaincode.NET.Protos; 2 | using Grpc.Core; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 5 | { 6 | public class ChaincodeSupportClientFactory : IChaincodeSupportClientFactory 7 | { 8 | public ChaincodeSupport.ChaincodeSupportClient Create(Channel channel) 9 | { 10 | return new ChaincodeSupport.ChaincodeSupportClient(channel); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Handler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Chaincode.NET.Protos; 7 | using Google.Protobuf; 8 | using Google.Protobuf.Collections; 9 | using Grpc.Core; 10 | using Microsoft.Extensions.Logging; 11 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 12 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Extensions; 13 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators; 14 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 15 | 16 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 17 | { 18 | public class Handler : IHandler 19 | { 20 | private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); 21 | private readonly IChaincode _chaincode; 22 | private readonly IChaincodeStubFactory _chaincodeStubFactory; 23 | private readonly ChaincodeSupport.ChaincodeSupportClient _client; 24 | private readonly ILogger _logger; 25 | private readonly IMessageQueue _messageQueue; 26 | private AsyncDuplexStreamingCall _stream; 27 | 28 | public Handler( 29 | IChaincode chaincode, 30 | string host, 31 | int port, 32 | IChaincodeStubFactory chaincodeStubFactory, 33 | ILogger logger, 34 | IMessageQueueFactory messageQueueFactory, 35 | IChaincodeSupportClientFactory chaincodeSupportClientFactory 36 | ) 37 | { 38 | _chaincode = chaincode; 39 | _chaincodeStubFactory = chaincodeStubFactory; 40 | _logger = logger; 41 | 42 | // TODO: Secure channel? 43 | _client = chaincodeSupportClientFactory.Create(new Channel(host, port, ChannelCredentials.Insecure, 44 | new List 45 | { 46 | new ChannelOption("request-timeout", 30000) 47 | })); 48 | _messageQueue = messageQueueFactory.Create(this); 49 | } 50 | 51 | // For testing only, will be removed with the impl. of a message handler 52 | public States State { get; set; } = States.Created; 53 | 54 | public IClientStreamWriter WriteStream => _stream.RequestStream; 55 | 56 | public void Close() 57 | { 58 | _cancellationTokenSource.Cancel(); 59 | } 60 | 61 | public async Task Chat(ChaincodeMessage conversationStarterMessage) 62 | { 63 | _stream = _client.Register(null, null, _cancellationTokenSource.Token); 64 | 65 | // TODO: Write a message handler 66 | 67 | await _stream.RequestStream.WriteAsync(conversationStarterMessage); 68 | 69 | await Task.Run(async () => 70 | { 71 | while (await _stream.ResponseStream.MoveNext(_cancellationTokenSource.Token)) 72 | { 73 | var message = _stream.ResponseStream.Current; 74 | 75 | _logger.LogDebug($"Received chat message from peer: {message} {State}"); 76 | 77 | if (State == States.Ready) 78 | { 79 | var type = message.Type; 80 | 81 | if (type != ChaincodeMessage.Types.Type.Registered && type != ChaincodeMessage.Types.Type.Ready) 82 | { 83 | if (type == ChaincodeMessage.Types.Type.Response || 84 | type == ChaincodeMessage.Types.Type.Error) 85 | { 86 | _logger.LogDebug($"[{message.ChannelId}-{message.Txid}] Received {message.Type}, " + 87 | "handling good or error response"); 88 | _messageQueue.HandleMessageResponse(message); 89 | } 90 | else if (type == ChaincodeMessage.Types.Type.Init) 91 | { 92 | _logger.LogDebug($"[{message.ChannelId}-{message.Txid}], Received {message.Type}, " + 93 | "initializing chaincode"); 94 | #pragma warning disable 4014 95 | HandleInit(message); 96 | #pragma warning restore 4014 97 | } 98 | else if (type == ChaincodeMessage.Types.Type.Transaction) 99 | { 100 | _logger.LogDebug($"[{message.ChannelId}-{message.Txid}], Received {message.Type}, " + 101 | $"invoking transaction on chaincode (state: {State})"); 102 | #pragma warning disable 4014 103 | HandleTransaction(message); 104 | #pragma warning restore 4014 105 | } 106 | else 107 | { 108 | _logger.LogCritical("Received unknown message from peer, exiting"); 109 | _cancellationTokenSource.Cancel(); 110 | } 111 | } 112 | } 113 | 114 | if (State == States.Established) 115 | { 116 | if (message.Type == ChaincodeMessage.Types.Type.Ready) 117 | { 118 | _logger.LogInformation( 119 | "Successfully established communication with peer node. State transferred to \"ready\""); 120 | State = States.Ready; 121 | } 122 | else 123 | { 124 | _logger.LogError("Chaincode is in ready, can only process messages of type " + 125 | $"'established', but received {message.Type}"); 126 | #pragma warning disable 4014 127 | _stream.RequestStream.WriteAsync(NewErrorMessage(message, 128 | #pragma warning restore 4014 129 | State)); 130 | } 131 | } 132 | 133 | if (State == States.Created) 134 | { 135 | if (message.Type == ChaincodeMessage.Types.Type.Registered) 136 | { 137 | _logger.LogInformation( 138 | "Successfully registered with peer node. State transferred to \"established\""); 139 | State = States.Established; 140 | } 141 | else 142 | { 143 | _logger.LogError("Chaincode is in \"created\" state, can only process message of type " + 144 | $"\"registered\", but received {message.Type}"); 145 | #pragma warning disable 4014 146 | _stream.RequestStream.WriteAsync(NewErrorMessage(message, 147 | #pragma warning restore 4014 148 | State)); 149 | } 150 | } 151 | } 152 | 153 | _logger.LogCritical("Chaincode ended??"); 154 | }, _cancellationTokenSource.Token); 155 | } 156 | 157 | public object ParseResponse(ChaincodeMessage response, MessageMethod messageMethod) 158 | { 159 | if (response.Type == ChaincodeMessage.Types.Type.Response) 160 | { 161 | _logger.LogInformation( 162 | $"[{response.ChannelId}-{response.Txid}] Received {messageMethod} successful response"); 163 | 164 | switch (messageMethod) 165 | { 166 | case MessageMethod.GetStateByRange: 167 | case MessageMethod.GetQueryResult: 168 | return new StateQueryIterator(this, response.ChannelId, response.Txid, 169 | QueryResponse.Parser.ParseFrom(response.Payload)); 170 | 171 | case MessageMethod.GetHistoryForKey: 172 | return new HistoryQueryIterator(this, response.ChannelId, response.Txid, 173 | QueryResponse.Parser.ParseFrom(response.Payload)); 174 | 175 | case MessageMethod.QueryStateNext: 176 | case MessageMethod.QueryStateClose: 177 | return QueryResponse.Parser.ParseFrom(response.Payload); 178 | 179 | case MessageMethod.InvokeChaincode: 180 | return ChaincodeMessage.Parser.ParseFrom(response.Payload); 181 | 182 | default: 183 | return response.Payload; 184 | } 185 | } 186 | 187 | if (response.Type == ChaincodeMessage.Types.Type.Error) 188 | { 189 | _logger.LogInformation( 190 | $"[{response.ChannelId}-{response.Txid}] Received {messageMethod} error response"); 191 | throw new Exception(response.Payload.ToStringUtf8()); 192 | } 193 | 194 | var errorMessage = $"[{response.ChannelId}-{response.Txid}] Received incorrect chaincode " + 195 | $"in response to the {messageMethod} call: " + 196 | $"type={response.Type}, expecting \"RESPONSE\""; 197 | _logger.LogInformation(errorMessage); 198 | throw new Exception(errorMessage); 199 | } 200 | 201 | public Task HandleGetState(string collection, string key, string channelId, string txId) 202 | { 203 | var payload = new GetState 204 | { 205 | Key = key, 206 | Collection = collection 207 | }; 208 | return CreateMessageAndListen(MessageMethod.GetState, ChaincodeMessage.Types.Type.GetState, 209 | payload, channelId, txId); 210 | } 211 | 212 | public Task HandlePutState( 213 | string collection, 214 | string key, 215 | ByteString value, 216 | string channelId, 217 | string txId 218 | ) 219 | { 220 | var payload = new PutState 221 | { 222 | Key = key, 223 | Value = value, 224 | Collection = collection 225 | }; 226 | 227 | return CreateMessageAndListen(MessageMethod.PutState, ChaincodeMessage.Types.Type.PutState, 228 | payload, 229 | channelId, txId); 230 | } 231 | 232 | public Task HandleDeleteState(string collection, string key, string channelId, string txId) 233 | { 234 | var payload = new DelState 235 | { 236 | Key = key, 237 | Collection = collection 238 | }; 239 | 240 | return CreateMessageAndListen(MessageMethod.DelState, ChaincodeMessage.Types.Type.DelState, 241 | payload, channelId, txId); 242 | } 243 | 244 | public Task HandleGetStateByRange( 245 | string collection, 246 | string startKey, 247 | string endKey, 248 | string channelId, 249 | string txId 250 | ) 251 | { 252 | var payload = new GetStateByRange 253 | { 254 | StartKey = startKey, 255 | EndKey = endKey, 256 | Collection = collection 257 | }; 258 | 259 | return CreateMessageAndListen(MessageMethod.GetStateByRange, 260 | ChaincodeMessage.Types.Type.GetStateByRange, 261 | payload, channelId, txId); 262 | } 263 | 264 | public Task HandleQueryStateNext(string id, string channelId, string txId) 265 | { 266 | var payload = new QueryStateNext {Id = id}; 267 | 268 | return CreateMessageAndListen(MessageMethod.QueryStateNext, 269 | ChaincodeMessage.Types.Type.QueryStateNext, payload, channelId, txId); 270 | } 271 | 272 | public Task HandleQueryCloseState(string id, string channelId, string txId) 273 | { 274 | var payload = new QueryStateClose {Id = id}; 275 | 276 | return CreateMessageAndListen(MessageMethod.QueryStateClose, 277 | ChaincodeMessage.Types.Type.QueryStateClose, payload, channelId, txId); 278 | } 279 | 280 | public Task HandleGetQueryResult( 281 | string collection, 282 | string query, 283 | string channelId, 284 | string txId 285 | ) 286 | { 287 | var payload = new GetQueryResult 288 | { 289 | Query = query, 290 | Collection = collection 291 | }; 292 | 293 | return CreateMessageAndListen(MessageMethod.GetQueryResult, 294 | ChaincodeMessage.Types.Type.GetQueryResult, payload, channelId, txId); 295 | } 296 | 297 | public Task HandleGetHistoryForKey(string key, string channelId, string txId) 298 | { 299 | var payload = new GetHistoryForKey {Key = key}; 300 | 301 | return CreateMessageAndListen(MessageMethod.GetHistoryForKey, 302 | ChaincodeMessage.Types.Type.GetHistoryForKey, payload, channelId, txId); 303 | } 304 | 305 | public async Task HandleInvokeChaincode( 306 | string chaincodeName, 307 | IEnumerable args, 308 | string channelId, 309 | string txId 310 | ) 311 | { 312 | var chaincodeId = new ChaincodeID {Name = chaincodeName}; 313 | var inputArgs = new RepeatedField().Concat(args); 314 | 315 | var chaincodeInput = new ChaincodeInput(); 316 | chaincodeInput.Args.AddRange(inputArgs); 317 | 318 | var payload = new ChaincodeSpec 319 | { 320 | ChaincodeId = chaincodeId, 321 | Input = chaincodeInput 322 | }; 323 | 324 | var response = await CreateMessageAndListen(MessageMethod.InvokeChaincode, 325 | ChaincodeMessage.Types.Type.InvokeChaincode, payload, channelId, txId); 326 | 327 | if (response.Type == ChaincodeMessage.Types.Type.Completed) 328 | return Response.Parser.ParseFrom(response.Payload); 329 | 330 | if (response.Type == ChaincodeMessage.Types.Type.Error) 331 | throw new Exception(response.Payload.ToStringUtf8()); 332 | 333 | throw new Exception("Something went somewhere horribly wrong"); 334 | } 335 | 336 | private Task HandleTransaction(ChaincodeMessage message) 337 | { 338 | return HandleMessage(message, HandleMessageAction.Invoke); 339 | } 340 | 341 | private Task HandleInit(ChaincodeMessage message) 342 | { 343 | return HandleMessage(message, HandleMessageAction.Init); 344 | } 345 | 346 | private async Task HandleMessage(ChaincodeMessage message, HandleMessageAction action) 347 | { 348 | ChaincodeMessage nextMessage = null; 349 | ChaincodeInput input = null; 350 | 351 | try 352 | { 353 | input = ChaincodeInput.Parser.ParseFrom(message.Payload); 354 | } 355 | catch 356 | { 357 | _logger.LogError( 358 | $"{message.ChannelId}-{message.Txid} Incorrect payload format. Sending ERROR message back to peer"); 359 | nextMessage = new ChaincodeMessage 360 | { 361 | Txid = message.Txid, 362 | ChannelId = message.ChannelId, 363 | Type = ChaincodeMessage.Types.Type.Error, 364 | Payload = message.Payload 365 | }; 366 | } 367 | 368 | if (input == null) 369 | { 370 | await WriteStream.WriteAsync(nextMessage); 371 | return; 372 | } 373 | 374 | IChaincodeStub stub = null; 375 | try 376 | { 377 | stub = _chaincodeStubFactory.Create(this, message.ChannelId, message.Txid, input, 378 | message.Proposal); 379 | } 380 | catch (Exception ex) 381 | { 382 | _logger.LogError($"Failed to construct a chaincode stub instance for the INIT message: {ex}"); 383 | nextMessage = new ChaincodeMessage 384 | { 385 | Type = ChaincodeMessage.Types.Type.Error, 386 | Payload = ex.ToString().ToByteString(), 387 | Txid = message.Txid, 388 | ChannelId = message.ChannelId 389 | }; 390 | } 391 | 392 | if (stub == null) 393 | { 394 | await WriteStream.WriteAsync(nextMessage); 395 | return; 396 | } 397 | 398 | Response response; 399 | if (action == HandleMessageAction.Init) 400 | response = await _chaincode.Init(stub); 401 | else 402 | response = await _chaincode.Invoke(stub); 403 | 404 | if (response.Status == 0) 405 | { 406 | var errorMessage = $"[{message.ChannelId}-{message.Txid}] Calling chaincode {action} " + 407 | "has not called success or error"; 408 | _logger.LogError(errorMessage); 409 | response = Shim.Error(errorMessage); 410 | } 411 | 412 | _logger.LogInformation($"[{message.ChaincodeEvent}]-{message.Txid} Calling chaincode {action}, " + 413 | $"response status {response.Status}"); 414 | 415 | if (response.Status >= (int) ResponseCodes.Error) 416 | { 417 | _logger.LogError($"[{message.ChannelId}-{message.Txid}] Calling chaincode {action} " + 418 | $"returned error response {response.Message}. " + 419 | "Sending ERROR message back to peer"); 420 | 421 | nextMessage = new ChaincodeMessage 422 | { 423 | Type = ChaincodeMessage.Types.Type.Error, 424 | Payload = response.Message.ToByteString(), 425 | Txid = message.Txid, 426 | ChannelId = message.ChannelId 427 | }; 428 | } 429 | else 430 | { 431 | _logger.LogInformation($"[{message.ChannelId}-{message.Txid}] Calling chaincode {action} " + 432 | "succeeded. Sending COMPLETED message back to peer"); 433 | nextMessage = new ChaincodeMessage 434 | { 435 | Type = ChaincodeMessage.Types.Type.Completed, 436 | Payload = response.ToByteString(), 437 | Txid = message.Txid, 438 | ChannelId = message.ChannelId, 439 | ChaincodeEvent = stub.ChaincodeEvent 440 | }; 441 | } 442 | 443 | await WriteStream.WriteAsync(nextMessage); 444 | } 445 | 446 | private ChaincodeMessage NewErrorMessage(ChaincodeMessage message, States state) 447 | { 448 | var errorString = $"[{message.ChannelId}-{message.Txid}] Chaincode Handler FSM cannot " + 449 | $"handle message ({message.Type}, with payload size {message.Payload.Length} " + 450 | $"while in state {state}"; 451 | 452 | return new ChaincodeMessage 453 | { 454 | Type = ChaincodeMessage.Types.Type.Error, 455 | Payload = errorString.ToByteString(), 456 | Txid = message.Txid, 457 | ChannelId = message.ChannelId 458 | }; 459 | } 460 | 461 | private ChaincodeMessage CreateMessage( 462 | ChaincodeMessage.Types.Type type, 463 | IMessage payload, 464 | string channelId, 465 | string txId 466 | ) 467 | { 468 | return new ChaincodeMessage 469 | { 470 | Type = type, 471 | Payload = payload.ToByteString(), 472 | Txid = txId, 473 | ChannelId = channelId 474 | }; 475 | } 476 | 477 | private Task CreateMessageAndListen( 478 | MessageMethod method, 479 | ChaincodeMessage.Types.Type type, 480 | IMessage payload, 481 | string channelId, 482 | string txId 483 | ) 484 | { 485 | return AskPeerAndListen(CreateMessage(type, payload, channelId, txId), method); 486 | } 487 | 488 | private Task AskPeerAndListen(ChaincodeMessage message, MessageMethod method) 489 | { 490 | var taskCompletionSource = new TaskCompletionSource(); 491 | 492 | var queueMessage = new QueueMessage(message, method, taskCompletionSource); 493 | _messageQueue.QueueMessage(queueMessage); 494 | 495 | return taskCompletionSource.Task; 496 | } 497 | } 498 | 499 | internal enum HandleMessageAction 500 | { 501 | Init, 502 | Invoke 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/HandlerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 5 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 6 | 7 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 8 | { 9 | public class HandlerFactory : IHandlerFactory 10 | { 11 | private readonly IServiceProvider _serviceProvider; 12 | 13 | public HandlerFactory(IServiceProvider serviceProvider) 14 | { 15 | _serviceProvider = serviceProvider; 16 | } 17 | 18 | public IHandler Create(string host, int port) 19 | { 20 | using (var scope = _serviceProvider.GetRequiredService().CreateScope()) 21 | { 22 | return new Handler( 23 | scope.ServiceProvider.GetRequiredService(), 24 | host, 25 | port, 26 | scope.ServiceProvider.GetRequiredService(), 27 | scope.ServiceProvider.GetRequiredService>(), 28 | scope.ServiceProvider.GetRequiredService(), 29 | scope.ServiceProvider.GetRequiredService() 30 | ); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/IChaincodeSupportClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Chaincode.NET.Protos; 2 | using Grpc.Core; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 5 | { 6 | public interface IChaincodeSupportClientFactory 7 | { 8 | ChaincodeSupport.ChaincodeSupportClient Create(Channel channel); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/IHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using Google.Protobuf; 5 | using Grpc.Core; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 8 | 9 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 10 | { 11 | public interface IHandler 12 | { 13 | IClientStreamWriter WriteStream { get; } 14 | 15 | States State { get; set; } // For testing only, will be removed with the impl. of a message handler 16 | void Close(); 17 | Task Chat(ChaincodeMessage conversationStarterMessage); 18 | object ParseResponse(ChaincodeMessage response, MessageMethod messageMethod); 19 | Task HandleGetState(string collection, string key, string channelId, string txId); 20 | Task HandlePutState(string collection, string key, ByteString value, string channelId, string txId); 21 | Task HandleDeleteState(string collection, string key, string channelId, string txId); 22 | 23 | Task HandleGetStateByRange( 24 | string collection, 25 | string startKey, 26 | string endKey, 27 | string channelId, 28 | string txId 29 | ); 30 | 31 | Task HandleQueryStateNext(string id, string channelId, string txId); 32 | Task HandleQueryCloseState(string id, string channelId, string txId); 33 | Task HandleGetQueryResult(string collection, string query, string channelId, string txId); 34 | Task HandleGetHistoryForKey(string key, string channelId, string txId); 35 | 36 | Task HandleInvokeChaincode( 37 | string chaincodeName, 38 | IEnumerable args, 39 | string channelId, 40 | string txId 41 | ); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/IHandlerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 2 | { 3 | public interface IHandlerFactory 4 | { 5 | IHandler Create(string host, int port); 6 | } 7 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Iterators/CommonIterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators 6 | { 7 | public abstract class CommonIterator 8 | { 9 | private readonly string _channelId; 10 | private readonly IHandler _handler; 11 | private readonly string _txId; 12 | private readonly IteratorType _type; 13 | private int _currentLocation; 14 | private QueryResponse _response; 15 | 16 | protected CommonIterator( 17 | IHandler handler, 18 | string channelId, 19 | string txId, 20 | QueryResponse response, 21 | IteratorType type 22 | ) 23 | { 24 | _handler = handler; 25 | _channelId = channelId; 26 | _txId = txId; 27 | _response = response; 28 | _type = type; 29 | } 30 | 31 | // TODO: Change Node style events into more suitable C# stuff? 32 | public event Action> Data; 33 | public event Action End; 34 | public event Action Error; 35 | 36 | private void OnData(QueryResult obj) 37 | { 38 | Data?.Invoke(obj); 39 | } 40 | 41 | private void OnEnd() 42 | { 43 | End?.Invoke(); 44 | } 45 | 46 | private void OnError(Exception exception) 47 | { 48 | Error?.Invoke(exception); 49 | } 50 | 51 | public Task Close() 52 | { 53 | return _handler.HandleQueryCloseState(_response.Id, _channelId, _txId); 54 | } 55 | 56 | protected abstract T GetResultFromBytes(QueryResultBytes bytes); 57 | 58 | private QueryResult CreateAndEmitResult() 59 | { 60 | var result = new QueryResult 61 | { 62 | Value = GetResultFromBytes(_response.Results[_currentLocation]) 63 | }; 64 | 65 | _currentLocation++; 66 | 67 | result.Done = !(_currentLocation < _response.Results.Count || _response.HasMore); 68 | 69 | OnData(result); 70 | 71 | return result; 72 | } 73 | 74 | public async Task> Next() 75 | { 76 | if (_currentLocation < _response.Results.Count) return CreateAndEmitResult(); 77 | 78 | if (!_response.HasMore) 79 | { 80 | OnEnd(); 81 | return new QueryResult {Done = true}; 82 | } 83 | 84 | try 85 | { 86 | var response = await _handler.HandleQueryStateNext(_response.Id, _channelId, _txId); 87 | _currentLocation = 0; 88 | _response = response; 89 | return CreateAndEmitResult(); 90 | } 91 | catch (Exception ex) 92 | { 93 | // Only throw if we don't have a handle for the error event, otherwise emit an error 94 | if (Error == null) throw; 95 | 96 | OnError(ex); 97 | return null; 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Iterators/HistoryQueryIterator.cs: -------------------------------------------------------------------------------- 1 | using Chaincode.NET.Protos; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators 4 | { 5 | public class HistoryQueryIterator : CommonIterator 6 | { 7 | public HistoryQueryIterator(IHandler handler, string channelId, string txId, QueryResponse response) 8 | : base(handler, channelId, txId, response, IteratorType.History) 9 | { 10 | } 11 | 12 | protected override KeyModification GetResultFromBytes(QueryResultBytes bytes) => 13 | KeyModification.Parser.ParseFrom(bytes.ResultBytes); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Iterators/IteratorTypes.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators 2 | { 3 | public enum IteratorType 4 | { 5 | Query, 6 | History 7 | } 8 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Iterators/QueryResult.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators 2 | { 3 | public class QueryResult 4 | { 5 | public T Value { get; set; } 6 | public bool Done { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Iterators/StateQueryIterator.cs: -------------------------------------------------------------------------------- 1 | using Chaincode.NET.Protos; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler.Iterators 4 | { 5 | public class StateQueryIterator : CommonIterator 6 | { 7 | public StateQueryIterator(IHandler handler, string channelId, string txId, QueryResponse response) 8 | : base(handler, channelId, txId, response, IteratorType.Query) 9 | { 10 | } 11 | 12 | protected override KV GetResultFromBytes(QueryResultBytes bytes) 13 | { 14 | return KV.Parser.ParseFrom(bytes.ResultBytes); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/Shim.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | using Google.Protobuf; 5 | using Grpc.Core; 6 | using Grpc.Core.Logging; 7 | using Microsoft.Extensions.Logging; 8 | using Microsoft.Extensions.Options; 9 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 10 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Settings; 11 | 12 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 13 | { 14 | public class Shim 15 | { 16 | private readonly ChaincodeSettings _chaincodeSettings; 17 | private readonly IHandlerFactory _handlerFactory; 18 | private readonly ILogger _logger; 19 | 20 | public Shim( 21 | IOptions chaincodeSettings, 22 | ILogger logger, 23 | IHandlerFactory handlerFactory 24 | ) 25 | { 26 | if (chaincodeSettings == null) throw new ArgumentNullException(nameof(chaincodeSettings)); 27 | 28 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 29 | _handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory)); 30 | _chaincodeSettings = chaincodeSettings.Value; 31 | 32 | if (_chaincodeSettings.LogGrpc) GrpcEnvironment.SetLogger(new ConsoleLogger()); 33 | } 34 | 35 | public async Task Start() 36 | { 37 | var url = ParseUrl(_chaincodeSettings.PeerAddress); 38 | 39 | // TODO: TLS Stuff? 40 | 41 | var handler = _handlerFactory.Create(url.Host, url.Port); 42 | 43 | var chaincodeId = new ChaincodeID {Name = _chaincodeSettings.ChaincodeIdName}; 44 | 45 | _logger.LogInformation("Registering with peer " + 46 | $"{url.Host}:{url.Port} as chaincode " + 47 | $"{_chaincodeSettings.ChaincodeIdName}"); 48 | 49 | await handler.Chat(new ChaincodeMessage 50 | { 51 | Type = ChaincodeMessage.Types.Type.Register, 52 | Payload = chaincodeId.ToByteString() 53 | }); 54 | 55 | return handler; 56 | } 57 | 58 | private (string Host, int Port) ParseUrl(string peerAddress) 59 | { 60 | if (peerAddress.Contains("://")) 61 | throw new Exception("Peer Address should not contain any protocol information."); 62 | 63 | var split = peerAddress.Split(':'); 64 | 65 | if (split.Length != 2) 66 | throw new ArgumentException("Please provide peer address in the format of host:port"); 67 | 68 | return (split[0], int.Parse(split[1])); 69 | } 70 | 71 | public static Response Success() 72 | { 73 | return Success(ByteString.Empty); 74 | } 75 | 76 | public static Response Success(ByteString payload) 77 | { 78 | return new Response 79 | { 80 | Status = (int) ResponseCodes.Ok, 81 | Payload = payload 82 | }; 83 | } 84 | 85 | public static Response Error(string message) 86 | { 87 | return new Response 88 | { 89 | Status = (int) ResponseCodes.Error, 90 | Message = message 91 | }; 92 | } 93 | 94 | public static Response Error(Exception exception) 95 | { 96 | return Error(exception.ToString()); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Handler/States.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Handler 2 | { 3 | public enum States 4 | { 5 | Created, 6 | Established, 7 | Ready 8 | } 9 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/IMessageQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Chaincode.NET.Protos; 3 | 4 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 5 | { 6 | public interface IMessageQueue 7 | { 8 | Task QueueMessage(QueueMessage queueMessage); 9 | void HandleMessageResponse(ChaincodeMessage response); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/IMessageQueueFactory.cs: -------------------------------------------------------------------------------- 1 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 2 | 3 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 4 | { 5 | public interface IMessageQueueFactory 6 | { 7 | IMessageQueue Create(IHandler handler); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/MessageMethod.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 2 | { 3 | public enum MessageMethod 4 | { 5 | GetState, 6 | GetStateByRange, 7 | GetQueryResult, 8 | GetHistoryForKey, 9 | QueryStateNext, 10 | QueryStateClose, 11 | InvokeChaincode, 12 | PutState, 13 | DelState 14 | } 15 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/MessageQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading.Tasks; 4 | using Chaincode.NET.Protos; 5 | using Microsoft.Extensions.Logging; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 7 | 8 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 9 | { 10 | public class MessageQueue : IMessageQueue 11 | { 12 | private readonly IHandler _handler; 13 | private readonly ILogger _logger; 14 | 15 | private readonly ConcurrentDictionary> _txQueues = 16 | new ConcurrentDictionary>(); 17 | 18 | public MessageQueue(IHandler handler, ILogger logger) 19 | { 20 | _handler = handler; 21 | _logger = logger; 22 | } 23 | 24 | public Task QueueMessage(QueueMessage queueMessage) 25 | { 26 | var messageQueue = _txQueues.GetOrAdd(queueMessage.MessageTxContextId, new ConcurrentQueue()); 27 | 28 | messageQueue.Enqueue(queueMessage); 29 | 30 | if (messageQueue.Count == 1) return SendMessage(queueMessage.MessageTxContextId); 31 | 32 | return Task.CompletedTask; 33 | } 34 | 35 | public void HandleMessageResponse(ChaincodeMessage response) 36 | { 37 | var messageTxContextId = response.ChannelId + response.Txid; 38 | 39 | var message = GetCurrentMessage(messageTxContextId) as dynamic; // TODO: This needs to be done better 40 | 41 | HandleResponseMessage(message, messageTxContextId, response); 42 | } 43 | 44 | private Task SendMessage(string messageTxContextId) 45 | { 46 | var message = GetCurrentMessage(messageTxContextId); 47 | 48 | if (message == null) return Task.CompletedTask; 49 | 50 | try 51 | { 52 | return _handler.WriteStream.WriteAsync(message.Message); 53 | } 54 | catch (Exception ex) 55 | { 56 | message.Fail(ex); 57 | return Task.CompletedTask; 58 | } 59 | } 60 | 61 | private QueueMessage GetCurrentMessage(string messageTxContextId) 62 | { 63 | if (_txQueues.TryGetValue(messageTxContextId, out var messageQueue) && 64 | messageQueue.TryPeek(out var message)) 65 | return message; 66 | 67 | _logger.LogError($"Failed to find a message for transaction context id {messageTxContextId}"); 68 | return null; 69 | } 70 | 71 | private void RemoveCurrentAndSendNextMessage(string messageTxContextId) 72 | { 73 | if (!_txQueues.TryGetValue(messageTxContextId, out var messageQueue) || messageQueue.Count <= 0) return; 74 | 75 | messageQueue.TryDequeue(out _); 76 | 77 | if (messageQueue.Count == 0) 78 | { 79 | _txQueues.TryRemove(messageTxContextId, out _); 80 | return; 81 | } 82 | 83 | SendMessage(messageTxContextId); 84 | } 85 | 86 | private void HandleResponseMessage( 87 | QueueMessage message, 88 | string messageTxContextId, 89 | ChaincodeMessage response 90 | ) 91 | { 92 | try 93 | { 94 | var parsedResponse = _handler.ParseResponse(response, message.Method); 95 | message.Success((T) parsedResponse); 96 | } 97 | catch (Exception ex) 98 | { 99 | message.Fail(ex); 100 | } 101 | 102 | RemoveCurrentAndSendNextMessage(messageTxContextId); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/MessageQueueFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 5 | 6 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 7 | { 8 | public class MessageQueueFactory : IMessageQueueFactory 9 | { 10 | private readonly IServiceProvider _serviceProvider; 11 | 12 | public MessageQueueFactory(IServiceProvider serviceProvider) 13 | { 14 | _serviceProvider = serviceProvider; 15 | } 16 | 17 | public IMessageQueue Create(IHandler handler) 18 | { 19 | using (var scope = _serviceProvider.GetRequiredService().CreateScope()) 20 | { 21 | return new MessageQueue(handler, scope.ServiceProvider.GetRequiredService>()); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Messaging/QueueMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Chaincode.NET.Protos; 4 | 5 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging 6 | { 7 | public class QueueMessage 8 | { 9 | public QueueMessage(ChaincodeMessage message, MessageMethod method) 10 | { 11 | Message = message; 12 | Method = method; 13 | } 14 | 15 | public ChaincodeMessage Message { get; } 16 | public string MessageTxContextId => Message.ChannelId + Message.Txid; 17 | public MessageMethod Method { get; } 18 | 19 | public virtual void Fail(Exception exception) 20 | { 21 | throw exception; 22 | } 23 | } 24 | 25 | public class QueueMessage : QueueMessage 26 | { 27 | private readonly TaskCompletionSource _taskCompletionSource; 28 | 29 | public QueueMessage( 30 | ChaincodeMessage message, 31 | MessageMethod method, 32 | TaskCompletionSource taskCompletionSource 33 | ) 34 | : base(message, method) 35 | { 36 | _taskCompletionSource = taskCompletionSource; 37 | } 38 | 39 | public void Success(T result) 40 | { 41 | _taskCompletionSource.SetResult(result); 42 | } 43 | 44 | public override void Fail(Exception exception) 45 | { 46 | _taskCompletionSource.SetException(exception); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/ProviderConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Logging; 4 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Chaincode; 5 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Handler; 6 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Messaging; 7 | using Thinktecture.HyperledgerFabric.Chaincode.NET.Settings; 8 | 9 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET 10 | { 11 | public static class ProviderConfiguration 12 | { 13 | public static ServiceProvider Configure(string[] args) 14 | where TChaincode : class, IChaincode 15 | { 16 | var serviceCollection = new ServiceCollection(); 17 | 18 | ConfigureLogging(serviceCollection); 19 | ConfigureSettings(serviceCollection, args); 20 | ConfigureServices(serviceCollection); 21 | 22 | return serviceCollection.BuildServiceProvider(); 23 | } 24 | 25 | private static void ConfigureServices(ServiceCollection serviceCollection) 26 | where TChaincode : class, IChaincode 27 | { 28 | serviceCollection.AddSingleton(); 29 | serviceCollection.AddSingleton(); 30 | serviceCollection.AddSingleton(); 31 | serviceCollection.AddSingleton(); 32 | serviceCollection.AddSingleton(); 33 | serviceCollection.AddSingleton(); 34 | serviceCollection.AddSingleton(); 35 | } 36 | 37 | private static void ConfigureSettings(ServiceCollection serviceCollection, string[] args) 38 | { 39 | var configuration = new ConfigurationBuilder() 40 | .AddEnvironmentVariables() 41 | .AddCommandLine(args) 42 | .Build(); 43 | 44 | serviceCollection.AddOptions(); 45 | serviceCollection.Configure(configuration); 46 | } 47 | 48 | private static void ConfigureLogging(ServiceCollection serviceCollection) 49 | { 50 | serviceCollection.AddLogging(builder => 51 | builder.SetMinimumLevel(LogLevel.Trace) 52 | .AddDebug() 53 | .AddConsole() 54 | ); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/README.md: -------------------------------------------------------------------------------- 1 | # Chaincode.NET - Hyperledger Fabric Chaincode Adapter for .NET Core 2 | 3 | ## Prerequirements for development 4 | 5 | * Golang Installation 6 | * $GOPATH is correctly set 7 | * `go get github.com/hyperledger/fabric/protos` 8 | * `go get github.com/gogo/protobuf/protobuf` 9 | * Run once (or after update of the protofiles): `./generate-protos` 10 | -------------------------------------------------------------------------------- /src/Chaincode.NET/Chaincode.NET/Settings/ChaincodeSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Thinktecture.HyperledgerFabric.Chaincode.NET.Settings 2 | { 3 | public class ChaincodeSettings 4 | { 5 | public string CORE_PEER_ADDRESS { get; set; } 6 | public string CORE_PEER_ID { get; set; } 7 | public string CORE_CHAINCODE_ID_NAME { get; set; } 8 | public bool CORE_LOG_GRPC { get; set; } 9 | public string PeerAddress => CORE_PEER_ADDRESS; 10 | public string ChaincodeIdName => CORE_CHAINCODE_ID_NAME; 11 | public string PeerId => CORE_PEER_ID; 12 | public bool LogGrpc => CORE_LOG_GRPC; 13 | } 14 | } -------------------------------------------------------------------------------- /src/generate_proto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # https://github.com/grpc/grpc/blob/master/examples/csharp/helloworld/generate_protos.bat 4 | 5 | NUGET_PATH=$HOME/.nuget 6 | TOOLS_PATH=$NUGET_PATH/packages/grpc.tools/1.11.1/tools/macosx_x64/ 7 | PROTO_PATH=$GOPATH/src/github.com/hyperledger/fabric/protos 8 | TARGET_PATH=./protos 9 | 10 | rm -rf $TARGET_PATH 11 | mkdir $TARGET_PATH 12 | mkdir $TARGET_PATH/common 13 | mkdir $TARGET_PATH/msp 14 | mkdir $TARGET_PATH/ledger 15 | mkdir $TARGET_PATH/ledger/queryresult 16 | mkdir $TARGET_PATH/peer 17 | 18 | cp $PROTO_PATH/common/common.proto $TARGET_PATH/common/ 19 | cp $PROTO_PATH/msp/identities.proto $TARGET_PATH/msp/ 20 | cp $PROTO_PATH/ledger/queryresult/kv_query_result.proto $TARGET_PATH/ledger/queryresult/ 21 | cp $PROTO_PATH/peer/chaincode.proto $TARGET_PATH/peer/ 22 | cp $PROTO_PATH/peer/chaincode_event.proto $TARGET_PATH/peer/ 23 | cp $PROTO_PATH/peer/chaincode_shim.proto $TARGET_PATH/peer/ 24 | cp $PROTO_PATH/peer/proposal.proto $TARGET_PATH/peer/ 25 | cp $PROTO_PATH/peer/proposal_response.proto $TARGET_PATH/peer/ 26 | 27 | $TOOLS_PATH/protoc \ 28 | -I=./protos \ 29 | -I=$GOPATH/src/github.com/gogo/protobuf/protobuf \ 30 | --csharp_out ./Chaincode.NET/Chaincode.NET.Protos \ 31 | --grpc_out ./Chaincode.NET/Chaincode.NET.Protos \ 32 | ./protos/common/common.proto \ 33 | ./protos/msp/identities.proto \ 34 | ./protos/ledger/queryresult/kv_query_result.proto \ 35 | ./protos/peer/chaincode.proto \ 36 | ./protos/peer/chaincode_event.proto \ 37 | ./protos/peer/chaincode_shim.proto \ 38 | ./protos/peer/proposal.proto \ 39 | ./protos/peer/proposal_response.proto \ 40 | --plugin="protoc-gen-grpc=$TOOLS_PATH/grpc_csharp_plugin" 41 | --------------------------------------------------------------------------------