Repository: hyperledger/fabric-chaincode-java Branch: main Commit: 5cd4f4588b58 Files: 334 Total size: 953.0 KB Directory structure: gitextract_fa6p6lwj/ ├── .github/ │ ├── dependabot.yml │ ├── settings.yml │ └── workflows/ │ ├── pull_request.yml │ ├── push.yml │ ├── release.yml │ ├── scan.yml │ ├── schedule.yml │ ├── scheduled-scan.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── COMPATIBILITY.md ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── RELEASING.md ├── SECURITY.md ├── build.gradle ├── docs/ │ ├── 404.md │ ├── _config.yml │ ├── _includes/ │ │ ├── footer.html │ │ ├── header.html │ │ └── javadocs.html │ └── index.md ├── examples/ │ ├── fabric-contract-example-as-service/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── example/ │ │ │ └── contract/ │ │ │ ├── MyAsset.java │ │ │ └── MyAssetContract.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── example/ │ │ └── MyAssetContractTest.java │ ├── fabric-contract-example-gradle/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── example/ │ │ │ ├── MyAsset.java │ │ │ └── MyAssetContract.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── example/ │ │ └── MyAssetContractTest.java │ ├── fabric-contract-example-gradle-kotlin/ │ │ ├── .fabricignore │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle.kts │ │ └── src/ │ │ ├── main/ │ │ │ └── kotlin/ │ │ │ └── org/ │ │ │ └── example/ │ │ │ ├── MyAsset.kt │ │ │ └── MyAssetContract.kt │ │ └── test/ │ │ └── kotlin/ │ │ └── org/ │ │ └── example/ │ │ └── MyAssetContractTest.kt │ ├── fabric-contract-example-maven/ │ │ ├── .gitignore │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── example/ │ │ │ ├── MyAsset.java │ │ │ └── MyAssetContract.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── example/ │ │ └── MyAssetContractTest.java │ └── ledger-api/ │ ├── .gitignore │ ├── README.md │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── example/ │ ├── LedgerAPIContract.java │ └── MyAsset.java ├── fabric-chaincode-docker/ │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── build.gradle │ ├── build.sh │ └── start ├── fabric-chaincode-integration-test/ │ ├── .gitignore │ ├── build.gradle │ ├── chaincodebootstrap.gradle │ └── src/ │ ├── contracts/ │ │ ├── bare-gradle/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── hyperledger/ │ │ │ │ └── fabric/ │ │ │ │ └── example/ │ │ │ │ └── BareGradle.java │ │ │ └── resources/ │ │ │ └── config.props │ │ ├── bare-maven/ │ │ │ ├── .gitignore │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── hyperledger/ │ │ │ │ └── fabric/ │ │ │ │ └── example/ │ │ │ │ └── BareMaven.java │ │ │ └── resources/ │ │ │ └── config.props │ │ ├── fabric-ledger-api/ │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── settings.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── hyperledger/ │ │ │ └── fabric/ │ │ │ └── example/ │ │ │ └── AllLedgerAPI.java │ │ ├── fabric-shim-api/ │ │ │ ├── build.gradle │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── settings.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── hyperledger/ │ │ │ │ └── fabric/ │ │ │ │ └── example/ │ │ │ │ ├── AllAPI.java │ │ │ │ └── EndorsementCC.java │ │ │ └── resources/ │ │ │ └── config.props │ │ └── wrapper-maven/ │ │ ├── .gitignore │ │ ├── .mvn/ │ │ │ └── wrapper/ │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── hyperledger/ │ │ │ └── fabric/ │ │ │ └── example/ │ │ │ └── WrapperMaven.java │ │ └── resources/ │ │ └── config.props │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── hyperleder/ │ │ └── fabric/ │ │ └── shim/ │ │ └── integration/ │ │ ├── contractinstall/ │ │ │ └── ContractInstallTest.java │ │ ├── ledgertests/ │ │ │ └── LedgerIntegrationTest.java │ │ ├── shimtests/ │ │ │ ├── SACCIntegrationTest.java │ │ │ └── SBECCIntegrationTest.java │ │ └── util/ │ │ ├── Bash.java │ │ ├── Command.java │ │ ├── Docker.java │ │ ├── DockerCompose.java │ │ ├── FabricState.java │ │ ├── InvokeHelper.java │ │ └── Peer.java │ └── resources/ │ ├── docker-compose-microfab.yaml │ └── scripts/ │ ├── ccutils.sh │ ├── collection_config.json │ └── mfsetup.sh ├── fabric-chaincode-shim/ │ ├── build.gradle │ ├── javabuild.sh │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── hyperledger/ │ │ │ └── fabric/ │ │ │ ├── Logger.java │ │ │ ├── Logging.java │ │ │ ├── contract/ │ │ │ │ ├── ClientIdentity.java │ │ │ │ ├── Context.java │ │ │ │ ├── ContextFactory.java │ │ │ │ ├── ContractInterface.java │ │ │ │ ├── ContractRouter.java │ │ │ │ ├── ContractRuntimeException.java │ │ │ │ ├── annotation/ │ │ │ │ │ ├── Contact.java │ │ │ │ │ ├── Contract.java │ │ │ │ │ ├── DataType.java │ │ │ │ │ ├── Default.java │ │ │ │ │ ├── Info.java │ │ │ │ │ ├── License.java │ │ │ │ │ ├── Property.java │ │ │ │ │ ├── Serializer.java │ │ │ │ │ ├── Transaction.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── execution/ │ │ │ │ │ ├── ExecutionFactory.java │ │ │ │ │ ├── ExecutionService.java │ │ │ │ │ ├── InvocationRequest.java │ │ │ │ │ ├── JSONTransactionSerializer.java │ │ │ │ │ ├── SerializerInterface.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── ContractExecutionService.java │ │ │ │ │ │ ├── ContractInvocationRequest.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── metadata/ │ │ │ │ │ ├── MetadataBuilder.java │ │ │ │ │ ├── TypeSchema.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── package-info.java │ │ │ │ ├── routing/ │ │ │ │ │ ├── ContractDefinition.java │ │ │ │ │ ├── DataTypeDefinition.java │ │ │ │ │ ├── ParameterDefinition.java │ │ │ │ │ ├── PropertyDefinition.java │ │ │ │ │ ├── RoutingRegistry.java │ │ │ │ │ ├── TransactionType.java │ │ │ │ │ ├── TxFunction.java │ │ │ │ │ ├── TypeRegistry.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── ContractDefinitionImpl.java │ │ │ │ │ │ ├── DataTypeDefinitionImpl.java │ │ │ │ │ │ ├── ParameterDefinitionImpl.java │ │ │ │ │ │ ├── PropertyDefinitionImpl.java │ │ │ │ │ │ ├── RoutingRegistryImpl.java │ │ │ │ │ │ ├── SerializerRegistryImpl.java │ │ │ │ │ │ ├── TxFunctionImpl.java │ │ │ │ │ │ ├── TypeRegistryImpl.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ └── package-info.java │ │ │ │ └── systemcontract/ │ │ │ │ ├── SystemContract.java │ │ │ │ └── package-info.java │ │ │ ├── ledger/ │ │ │ │ ├── Collection.java │ │ │ │ ├── Ledger.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── LedgerImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── metrics/ │ │ │ │ ├── Metrics.java │ │ │ │ ├── MetricsProvider.java │ │ │ │ ├── TaskMetricsCollector.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── DefaultProvider.java │ │ │ │ │ ├── NullProvider.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ ├── overview.html │ │ │ ├── package-info.java │ │ │ ├── shim/ │ │ │ │ ├── Chaincode.java │ │ │ │ ├── ChaincodeBase.java │ │ │ │ ├── ChaincodeException.java │ │ │ │ ├── ChaincodeServer.java │ │ │ │ ├── ChaincodeServerProperties.java │ │ │ │ ├── ChaincodeStub.java │ │ │ │ ├── ChatChaincodeWithPeer.java │ │ │ │ ├── GrpcServer.java │ │ │ │ ├── NettyChaincodeServer.java │ │ │ │ ├── NettyGrpcServer.java │ │ │ │ ├── ResponseUtils.java │ │ │ │ ├── ext/ │ │ │ │ │ └── sbe/ │ │ │ │ │ ├── StateBasedEndorsement.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── StateBasedEndorsementFactory.java │ │ │ │ │ │ ├── StateBasedEndorsementImpl.java │ │ │ │ │ │ ├── StateBasedEndorsementUtils.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ChaincodeInvocationTask.java │ │ │ │ │ ├── ChaincodeMessageFactory.java │ │ │ │ │ ├── ChaincodeSupportClient.java │ │ │ │ │ ├── InvocationStubImpl.java │ │ │ │ │ ├── InvocationTaskExecutor.java │ │ │ │ │ ├── InvocationTaskManager.java │ │ │ │ │ ├── KeyModificationImpl.java │ │ │ │ │ ├── KeyValueImpl.java │ │ │ │ │ ├── QueryResultsIteratorImpl.java │ │ │ │ │ ├── QueryResultsIteratorWithMetadataImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── ledger/ │ │ │ │ │ ├── CompositeKey.java │ │ │ │ │ ├── CompositeKeyFormatException.java │ │ │ │ │ ├── KeyModification.java │ │ │ │ │ ├── KeyValue.java │ │ │ │ │ ├── QueryResultsIterator.java │ │ │ │ │ ├── QueryResultsIteratorWithMetadata.java │ │ │ │ │ └── package-info.java │ │ │ │ └── package-info.java │ │ │ └── traces/ │ │ │ ├── Traces.java │ │ │ ├── TracesProvider.java │ │ │ ├── impl/ │ │ │ │ ├── DefaultTracesProvider.java │ │ │ │ ├── NullProvider.java │ │ │ │ ├── OpenTelemetryProperties.java │ │ │ │ ├── OpenTelemetryTracesProvider.java │ │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ └── resources/ │ │ ├── contract-schema.json │ │ └── json-schema-draft-04-schema.json │ └── test/ │ ├── java/ │ │ ├── ChaincodeWithoutPackageTest.java │ │ ├── EmptyChaincodeWithoutPackage.java │ │ ├── contract/ │ │ │ ├── Greeting.java │ │ │ └── SampleContract.java │ │ └── org/ │ │ └── hyperledger/ │ │ └── fabric/ │ │ ├── LoggerTest.java │ │ ├── LoggingTest.java │ │ ├── TestUtil.java │ │ ├── contract/ │ │ │ ├── AllTypesAsset.java │ │ │ ├── ChaincodeStubNaiveImpl.java │ │ │ ├── ClientIdentityTest.java │ │ │ ├── ContextFactoryTest.java │ │ │ ├── ContextTest.java │ │ │ ├── ContractInterfaceTest.java │ │ │ ├── ContractRouterTest.java │ │ │ ├── MyType.java │ │ │ ├── MyType2.java │ │ │ ├── TransactionExceptionTest.java │ │ │ ├── execution/ │ │ │ │ ├── ContractExecutionServiceTest.java │ │ │ │ └── JSONTransactionSerializerTest.java │ │ │ ├── metadata/ │ │ │ │ ├── MetadataBuilderTest.java │ │ │ │ └── TypeSchemaTest.java │ │ │ ├── routing/ │ │ │ │ ├── ContractDefinitionTest.java │ │ │ │ ├── DataTypeDefinitionTest.java │ │ │ │ ├── ParameterDefinitionTest.java │ │ │ │ ├── PropertyDefinitionTest.java │ │ │ │ ├── TxFunctionTest.java │ │ │ │ └── TypeRegistryTest.java │ │ │ └── simplepath/ │ │ │ └── ContractSimplePathTest.java │ │ ├── ledger/ │ │ │ └── LedgerTest.java │ │ ├── metrics/ │ │ │ ├── MetricsTest.java │ │ │ └── impl/ │ │ │ └── DefaultProviderTest.java │ │ ├── shim/ │ │ │ ├── ChaincodeBaseTest.java │ │ │ ├── ChaincodeServerImplTest.java │ │ │ ├── ChaincodeStubTest.java │ │ │ ├── ChaincodeTest.java │ │ │ ├── ChatChaincodeWithPeerTest.java │ │ │ ├── NettyGrpcServerTest.java │ │ │ ├── chaincode/ │ │ │ │ └── EmptyChaincode.java │ │ │ ├── ext/ │ │ │ │ └── sbe/ │ │ │ │ ├── StateBasedEndorsementTest.java │ │ │ │ └── impl/ │ │ │ │ ├── StateBasedEndorsementFactoryTest.java │ │ │ │ └── StateBasedEndorsementImplTest.java │ │ │ ├── fvt/ │ │ │ │ └── ChaincodeFVTest.java │ │ │ ├── impl/ │ │ │ │ ├── ChaincodeMessageFactoryTest.java │ │ │ │ ├── ChaincodeSupportClientTest.java │ │ │ │ ├── InnvocationTaskManagerTest.java │ │ │ │ ├── InvocationStubImplTest.java │ │ │ │ ├── InvocationTaskManagerTest.java │ │ │ │ ├── KeyModificationImplTest.java │ │ │ │ ├── KeyValueImplTest.java │ │ │ │ └── QueryResultsIteratorWithMetadataImplTest.java │ │ │ ├── ledger/ │ │ │ │ └── CompositeKeyTest.java │ │ │ ├── mock/ │ │ │ │ └── peer/ │ │ │ │ ├── ChaincodeMockPeer.java │ │ │ │ ├── CompleteStep.java │ │ │ │ ├── DelValueStep.java │ │ │ │ ├── ErrorResponseStep.java │ │ │ │ ├── GetHistoryForKeyStep.java │ │ │ │ ├── GetQueryResultStep.java │ │ │ │ ├── GetStateByRangeStep.java │ │ │ │ ├── GetStateMetadata.java │ │ │ │ ├── GetValueStep.java │ │ │ │ ├── InvokeChaincodeStep.java │ │ │ │ ├── PurgeValueStep.java │ │ │ │ ├── PutStateMetadata.java │ │ │ │ ├── PutValueStep.java │ │ │ │ ├── QueryCloseStep.java │ │ │ │ ├── QueryNextStep.java │ │ │ │ ├── QueryResultStep.java │ │ │ │ ├── RegisterStep.java │ │ │ │ └── ScenarioStep.java │ │ │ └── utils/ │ │ │ ├── MessageUtil.java │ │ │ └── TimeoutUtil.java │ │ └── traces/ │ │ ├── TracesTest.java │ │ └── impl/ │ │ ├── DefaultProviderTest.java │ │ ├── OpenTelemetryPropertiesTest.java │ │ ├── OpenTelemetryTracesProviderTest.java │ │ └── TestSpanExporterProvider.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ └── io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider │ ├── ca.crt │ ├── client.crt │ ├── client.crt.enc │ ├── client.key │ ├── client.key.enc │ └── client.key.password-protected ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pmd-ruleset.xml ├── release_notes/ │ ├── v1.3.0.txt │ ├── v1.4.0.txt │ ├── v2.0.0-alpha.txt │ ├── v2.0.0-beta.txt │ ├── v2.0.0.txt │ ├── v2.0.1.txt │ ├── v2.1.0.txt │ ├── v2.1.1.txt │ ├── v2.2.0.txt │ ├── v2.2.1.txt │ ├── v2.3.0.txt │ ├── v2.3.1.txt │ ├── v2.4.0-beta.txt │ ├── v2.4.0.txt │ ├── v2.4.1.txt │ └── v2.5.0.txt └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 multi-ecosystem-groups: java: schedule: interval: daily updates: - package-ecosystem: docker directories: - "/fabric-chaincode-docker" - "/examples/fabric-contract-example-as-service" schedule: interval: daily - package-ecosystem: "github-actions" directory: "/" schedule: interval: daily - package-ecosystem: gradle multi-ecosystem-group: java patterns: - "*" directories: - "/" - "/fabric-chaincode-docker" - "/examples/fabric-contract-example-as-service" - "/examples/fabric-contract-example-gradle" - "/examples/fabric-contract-example-gradle-kotlin" - "/examples/ledger-api" - "/fabric-chaincode-integration-test" - "/fabric-chaincode-integration-test/src/contracts/bare-gradle" - "/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api" - "/fabric-chaincode-integration-test/src/contracts/fabric-shim-api" - "/fabric-chaincode-shim" - package-ecosystem: maven multi-ecosystem-group: java patterns: - "*" directories: - "/examples/fabric-contract-example-maven" - "/fabric-chaincode-integration-test/src/contracts/bare-maven" - "/fabric-chaincode-integration-test/src/contracts/wrapper-maven" ================================================ FILE: .github/settings.yml ================================================ # # SPDX-License-Identifier: Apache-2.0 # repository: name: fabric-chaincode-java description: Hyperledger Fabric Contract and Chaincode implementation for Java https://wiki.hyperledger.org/display/fabric homepage: https://jira.hyperledger.org/issues/?jql=project+%3D+FAB+AND+component+%3D+fabric-chaincode-java default_branch: main has_downloads: true has_issues: false has_projects: false has_wiki: false archived: false private: false allow_squash_merge: false allow_merge_commit: true allow_rebase_merge: true ================================================ FILE: .github/workflows/pull_request.yml ================================================ # Copyright the Hyperledger Fabric contributors. All rights reserved. # SPDX-License-Identifier: Apache-2.0 name: Pull request on: pull_request: branches: - main workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: test: uses: ./.github/workflows/test.yml scan: uses: ./.github/workflows/scan.yml pull-request: needs: test name: Pull request success runs-on: ubuntu-latest steps: - run: "true" ================================================ FILE: .github/workflows/push.yml ================================================ # Copyright the Hyperledger Fabric contributors. All rights reserved. # SPDX-License-Identifier: Apache-2.0 name: Push on: push: branches: - main workflow_dispatch: jobs: test: uses: ./.github/workflows/test.yml ================================================ FILE: .github/workflows/release.yml ================================================ # Copyright the Hyperledger Fabric contributors. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 name: Release on: push: tags: - "v[0-9]+.[0-9]+.[0-9]+" - "v[0-9]+.[0-9]+.[0-9]+-*" workflow_dispatch: env: IMAGE_NAME: ${{ github.repository_owner }}/fabric-javaenv jobs: publish-github: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: "temurin" java-version: 25 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Publish to GitHub Packages run: | ./gradlew publishAllPublicationsToGitHubRepository env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} publish-maven: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: "temurin" java-version: 25 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Publish to Maven Central run: | ./gradlew publishAggregationToCentralPortal env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVENCENTRAL_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVENCENTRAL_PASSWORD }} docker-build-push: name: Push Docker image runs-on: ${{ matrix.arch.runner }} permissions: contents: read packages: write strategy: fail-fast: false matrix: arch: - platform: linux-amd64 runner: ubuntu-24.04 - platform: linux-arm64 runner: ubuntu-24.04-arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: "temurin" java-version: 25 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Build the dependencies needed for the image run: ./gradlew :fabric-chaincode-docker:copyAllDeps - name: Get commit timestamp run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "${GITHUB_ENV}" - name: Login to GitHub Container Registry uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: docker.io username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build image id: build uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: file: fabric-chaincode-docker/Dockerfile context: fabric-chaincode-docker outputs: type=registry,"name=${{ format('ghcr.io/{0},docker.io/{0}', env.IMAGE_NAME) }}",push-by-digest=true,name-canonical=true env: SOURCE_DATE_EPOCH: ${{ env.SOURCE_DATE_EPOCH }} - name: Export digest run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: digest-${{ matrix.arch.platform }} path: ${{ runner.temp }}/digests/* if-no-files-found: error docker-meta: needs: docker-build-push name: Publish Docker metadata runs-on: ubuntu-latest permissions: contents: read packages: write strategy: fail-fast: false matrix: registry: - docker.io - ghcr.io steps: - name: Download digests uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: ${{ runner.temp }}/digests pattern: digest-* merge-multiple: true - name: Login to ${{ matrix.registry }} uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ${{ matrix.registry }} username: ${{ matrix.registry == 'docker.io' && secrets.DOCKERHUB_USERNAME || github.actor }} password: ${{ matrix.registry == 'docker.io' && secrets.DOCKERHUB_TOKEN || secrets.GITHUB_TOKEN }} - name: Docker metadata id: meta uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 with: images: ${{ matrix.registry }}/${{ env.IMAGE_NAME }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}.{{patch}} - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Create and push manifest list working-directory: ${{ runner.temp }}/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("--tag " + .) | join(" ")' <<< "${DOCKER_METADATA_OUTPUT_JSON}") \ $(printf '${{ matrix.registry }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) - name: Inspect image run: docker buildx imagetools inspect '${{ matrix.registry }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}' ================================================ FILE: .github/workflows/scan.yml ================================================ name: "Scheduled vulnerability scan" on: workflow_call: inputs: ref: description: Branch, tag or SHA to scan. type: string required: false default: "" permissions: contents: read jobs: osv-scanner: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} # Go needed for scanning of v2.5.5 and earlier - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: stable cache: false - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin # Releases v2.5.7 and earlier do not support Java 25 java-version: 21 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Scan run: make scan ================================================ FILE: .github/workflows/schedule.yml ================================================ name: Scheduled build on: schedule: - cron: "5 4 * * 0" workflow_dispatch: jobs: main: uses: ./.github/workflows/test.yml ================================================ FILE: .github/workflows/scheduled-scan.yml ================================================ name: "Scheduled vulnerability scan" on: schedule: - cron: "20 3 * * *" workflow_dispatch: permissions: contents: read jobs: release-version: name: Get latest release tag runs-on: ubuntu-latest outputs: tag_name: ${{ steps.tag-name.outputs.value }} steps: - id: tag-name run: echo "value=$(curl --location --silent --fail "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest" | jq --raw-output '.tag_name')" >> "${GITHUB_OUTPUT}" scan-release: name: Scan ${{ needs.release-version.outputs.tag_name }} needs: release-version uses: ./.github/workflows/scan.yml with: ref: ${{ needs.release-version.outputs.tag_name }} scan-latest: name: Scan latest uses: ./.github/workflows/scan.yml ================================================ FILE: .github/workflows/test.yml ================================================ # Copyright the Hyperledger Fabric contributors. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 name: Test on: workflow_call: inputs: ref: default: "" required: false type: string jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: 25 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Build and Unit test run: ./gradlew :fabric-chaincode-shim:build intergationtest: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: 25 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "lts/*" - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Populate chaincode with latest java-version run: | ./gradlew -I $GITHUB_WORKSPACE/fabric-chaincode-integration-test/chaincodebootstrap.gradle -PchaincodeRepoDir=$GITHUB_WORKSPACE/fabric-chaincode-integration-test/src/contracts/fabric-shim-api/repository publishShimPublicationToFabricRepository ./gradlew -I $GITHUB_WORKSPACE/fabric-chaincode-integration-test/chaincodebootstrap.gradle -PchaincodeRepoDir=$GITHUB_WORKSPACE/fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/repository publishShimPublicationToFabricRepository ./gradlew -I $GITHUB_WORKSPACE/fabric-chaincode-integration-test/chaincodebootstrap.gradle -PchaincodeRepoDir=$GITHUB_WORKSPACE/fabric-chaincode-integration-test/src/contracts/bare-gradle/repository publishShimPublicationToFabricRepository ./gradlew -I $GITHUB_WORKSPACE/fabric-chaincode-integration-test/chaincodebootstrap.gradle -PchaincodeRepoDir=$GITHUB_WORKSPACE/fabric-chaincode-integration-test/src/contracts/bare-maven/repository publishShimPublicationToFabricRepository ./gradlew -I $GITHUB_WORKSPACE/fabric-chaincode-integration-test/chaincodebootstrap.gradle -PchaincodeRepoDir=$GITHUB_WORKSPACE/fabric-chaincode-integration-test/src/contracts/wrapper-maven/repository publishShimPublicationToFabricRepository - name: Ensure that the Peer/weft tools are available run: | curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh | bash -s -- binary npm install -g @hyperledger-labs/weft # set the path and cfg env var for the rest of the step echo "FABRIC_CFG_PATH=$GITHUB_WORKSPACE/config" >> $GITHUB_ENV echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH - name: versions run: | peer version weft --version - name: Integration Tests run: ./gradlew :fabric-chaincode-integration-test:build docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version: 25 - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 - name: Build Docker image run: ./gradlew :fabric-chaincode-docker:buildImage ================================================ FILE: .gitignore ================================================ /.settings/ /.project *.log *.swp .gradletasknamecache .classpath **/bin/ /build/ build/* settings-gradle.lockfile config/ _cfg repository .env .gradle /build/ out/ gradle.lockfile !gradle/wrapper/gradle-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### nbproject/private/ build/ nbbuild/ dist/ nbdist/ .nb-gradle/ local-config.yaml gradle.properties .vscode/ ### Jekyll ### .sass-cache _site ================================================ FILE: CHANGELOG.md ================================================ # Changelog Notable changes in each release are documented on the project's [GitHub releases](https://github.com/hyperledger/fabric-chaincode-java/releases) page. Change history from previous releases is retained below. ## v2.5.0 Thu Dec 8 09:02:17 GMT 2022 * [94cfadee](https://github.com/hyperledger/fabric-chaincode-java/commit/94cfadee) Move inactive maintainers to emeritus status * [37bebee7](https://github.com/hyperledger/fabric-chaincode-java/commit/37bebee7) Adjust to using the Temurin JVM * [0c9f44fb](https://github.com/hyperledger/fabric-chaincode-java/commit/0c9f44fb) Basic github actions workflow * [d15f2cd1](https://github.com/hyperledger/fabric-chaincode-java/commit/d15f2cd1) Update pom.xml * [1fba41b2](https://github.com/hyperledger/fabric-chaincode-java/commit/1fba41b2) Remove unrequired Maven wrapper directory Update Maven wrapper to latest version * [3a2db82d](https://github.com/hyperledger/fabric-chaincode-java/commit/3a2db82d) Bump version to 2.5.0 * [35eabb0a](https://github.com/hyperledger/fabric-chaincode-java/commit/35eabb0a) PurgePrivateData * [80a36ee4](https://github.com/hyperledger/fabric-chaincode-java/commit/80a36ee4) Swap to using the published fabric-protos libraries * [dd551979](https://github.com/hyperledger/fabric-chaincode-java/commit/dd551979) Update MAINTAINERS.md * [f25df1b6](https://github.com/hyperledger/fabric-chaincode-java/commit/f25df1b6) Add test for Logger.debug * [d1d093b4](https://github.com/hyperledger/fabric-chaincode-java/commit/d1d093b4) Regular Maintainece Task * [22c722fe](https://github.com/hyperledger/fabric-chaincode-java/commit/22c722fe) Update README.md * [2c35c3ed](https://github.com/hyperledger/fabric-chaincode-java/commit/2c35c3ed) JSONPropertyIgnore only works on getter * [02d1485a](https://github.com/hyperledger/fabric-chaincode-java/commit/02d1485a) Change from RocketChat to Discord * [5608879e](https://github.com/hyperledger/fabric-chaincode-java/commit/5608879e) Temporarily remove nightly publish & Fix PR build * [d13d3070](https://github.com/hyperledger/fabric-chaincode-java/commit/d13d3070) Fix transaction serializer usage * [5388349d](https://github.com/hyperledger/fabric-chaincode-java/commit/5388349d) Remove the log4j dependency ## v2.4.1 Mon Dec 13 11:50:31 GMT 2021 * [bf054cb0](https://github.com/hyperledger/fabric-chaincode-java/commit/bf054cb0) Add a brief example for the -as-a-service * [f9ada8f5](https://github.com/hyperledger/fabric-chaincode-java/commit/f9ada8f5) Chaincode-as-a-service main bootstrap method ## 2.4.0 Fri Nov 26 08:21:40 GMT 2021 Release 2.4.0 ## v2.4.0-beta Fri 13 Aug 2021 16:43:31 CEST * [292c4ebe](https://github.com/hyperledger/fabric-chaincode-java/commit/292c4ebe) Update the build.gradle for publishing ## v2.3.1 Wed 21 Jul 2021 11:20:03 BST * [6be7a724](https://github.com/hyperledger/fabric-chaincode-java/commit/6be7a724) Integration tests extended * [66e25ea9](https://github.com/hyperledger/fabric-chaincode-java/commit/66e25ea9) NettyGrpcServer -- support mutual TLS * [1128e7b2](https://github.com/hyperledger/fabric-chaincode-java/commit/1128e7b2) NettyGrpcServer: logger->LOGGER * [b7b4ef12](https://github.com/hyperledger/fabric-chaincode-java/commit/b7b4ef12) NettyGrpcServer -- reformat code * [3a98fc5e](https://github.com/hyperledger/fabric-chaincode-java/commit/3a98fc5e) NettyGrpcServer -- configure ALPN * [63c12ffa](https://github.com/hyperledger/fabric-chaincode-java/commit/63c12ffa) NettyGrpcServerTest -- improve startAndStopTlsPassword, startAndStopTlsWithoutPassword test cases * [5046eb79](https://github.com/hyperledger/fabric-chaincode-java/commit/5046eb79) Bump logback-classic in /examples/fabric-contract-example-maven * [ab2a166d](https://github.com/hyperledger/fabric-chaincode-java/commit/ab2a166d) added tests * [25706938](https://github.com/hyperledger/fabric-chaincode-java/commit/25706938) Add additional contracts for deployment test * [e096d4f7](https://github.com/hyperledger/fabric-chaincode-java/commit/e096d4f7) Review ideas * [7645bb9d](https://github.com/hyperledger/fabric-chaincode-java/commit/7645bb9d) Move to use Maven Wrapper * [d212e2a7](https://github.com/hyperledger/fabric-chaincode-java/commit/d212e2a7) Upgrade gradle to v7 * [f1d6b7da](https://github.com/hyperledger/fabric-chaincode-java/commit/f1d6b7da) Gradle wrapper updated to the latest version * [3f190ef5](https://github.com/hyperledger/fabric-chaincode-java/commit/3f190ef5) Transaction metadata tags updated * [0d0c9280](https://github.com/hyperledger/fabric-chaincode-java/commit/0d0c9280) Update "master" branch references to "main" * [ca69f15d](https://github.com/hyperledger/fabric-chaincode-java/commit/ca69f15d) Fix link in SECURITY.md * [4c6cbf47](https://github.com/hyperledger/fabric-chaincode-java/commit/4c6cbf47) Correct condition * [76c7fe45](https://github.com/hyperledger/fabric-chaincode-java/commit/76c7fe45) Cleanup files * [70bdd194](https://github.com/hyperledger/fabric-chaincode-java/commit/70bdd194) Add in publishing of the nightly master branch drivers * [b51eac20](https://github.com/hyperledger/fabric-chaincode-java/commit/b51eac20) Change FABRIC_VERSION to latest tag * [28dfb24b](https://github.com/hyperledger/fabric-chaincode-java/commit/28dfb24b) fix fabric-contract-example-maven/pom.xml * [ff48941c](https://github.com/hyperledger/fabric-chaincode-java/commit/ff48941c) Logging Tests Reset Correctly * [cc31ae01](https://github.com/hyperledger/fabric-chaincode-java/commit/cc31ae01) [FABCJ-214](https://jira.hyperledger.org/browse/FABCJ-214) - Java chaincode gRPC server * [06f72193](https://github.com/hyperledger/fabric-chaincode-java/commit/06f72193) Fix "build shadowJar" error * [278e9f8e](https://github.com/hyperledger/fabric-chaincode-java/commit/278e9f8e) [FABCJ-187](https://jira.hyperledger.org/browse/FABCJ-187) Add max inbound msg size configuration * [bf4f30aa](https://github.com/hyperledger/fabric-chaincode-java/commit/bf4f30aa) Fix javadoc build * [9e403b6d](https://github.com/hyperledger/fabric-chaincode-java/commit/9e403b6d) Fix JavaDoc link to Compatibility documentation * [57678eea](https://github.com/hyperledger/fabric-chaincode-java/commit/57678eea) Bump version to 2.3.1 * [91e6001d](https://github.com/hyperledger/fabric-chaincode-java/commit/91e6001d) [FABCJ-290](https://jira.hyperledger.org/browse/FABCJ-290) Add release guide * [0deb7e0d](https://github.com/hyperledger/fabric-chaincode-java/commit/0deb7e0d) [FABCJ-293](https://jira.hyperledger.org/browse/FABCJ-293) Remove gradle from image * [2dfe17c1](https://github.com/hyperledger/fabric-chaincode-java/commit/2dfe17c1) [FABCJ-291](https://jira.hyperledger.org/browse/FABCJ-291) Startkey needs additional checks For the open ended query, the start and empty keys are empty from the chaincode. However, in the shim, if the start key is an empty key, it is replaced with 0x01 which is nothing but a namespace for the non-composite key. * [21d81193](https://github.com/hyperledger/fabric-chaincode-java/commit/21d81193) Update doc links * [78ed0157](https://github.com/hyperledger/fabric-chaincode-java/commit/78ed0157) [FABCJ-288](https://jira.hyperledger.org/browse/FABCJ-288) fix: simple key end of range * [90f6c3c2](https://github.com/hyperledger/fabric-chaincode-java/commit/90f6c3c2) Update dependencies * [526d1cc8](https://github.com/hyperledger/fabric-chaincode-java/commit/526d1cc8) [FABCJ-284](https://jira.hyperledger.org/browse/FABCJ-284) Broken link * [7f722053](https://github.com/hyperledger/fabric-chaincode-java/commit/7f722053) force rebuild * [9a42effb](https://github.com/hyperledger/fabric-chaincode-java/commit/9a42effb) [FABCJ-285](https://jira.hyperledger.org/browse/FABCJ-285) Remove incorrect log point * [c2367768](https://github.com/hyperledger/fabric-chaincode-java/commit/c2367768) Clean up Fabric Version Methodology * [88da28f6](https://github.com/hyperledger/fabric-chaincode-java/commit/88da28f6) [FAB-17777](https://jira.hyperledger.org/browse/FAB-17777) Create basic settings.yaml * [a6b00f2d](https://github.com/hyperledger/fabric-chaincode-java/commit/a6b00f2d) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Update javadoc 2.x link * [96fa4d6e](https://github.com/hyperledger/fabric-chaincode-java/commit/96fa4d6e) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Update docs to handle new branch * [ff031f2d](https://github.com/hyperledger/fabric-chaincode-java/commit/ff031f2d) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Bump version number to 3.0.0 - in order to not contradict release-2.x branch * [35a5159d](https://github.com/hyperledger/fabric-chaincode-java/commit/35a5159d) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Update docker image version to 2.1 * [67af7977](https://github.com/hyperledger/fabric-chaincode-java/commit/67af7977) [FABCJ-280](https://jira.hyperledger.org/browse/FABCJ-280) Copy chaincode into temporary directory before building * [e69168cb](https://github.com/hyperledger/fabric-chaincode-java/commit/e69168cb) [FABCJ-276](https://jira.hyperledger.org/browse/FABCJ-276) Access localmspid * [cc673036](https://github.com/hyperledger/fabric-chaincode-java/commit/cc673036) [FABCN-359](https://jira.hyperledger.org/browse/FABCN-359) Ledger Class * [f0689360](https://github.com/hyperledger/fabric-chaincode-java/commit/f0689360) [FABCJ-273](https://jira.hyperledger.org/browse/FABCJ-273) Show release-2.0 Javadoc * [364cae9d](https://github.com/hyperledger/fabric-chaincode-java/commit/364cae9d) [FABCJ-269](https://jira.hyperledger.org/browse/FABCJ-269) Compatibility Matrix * [89ffb592](https://github.com/hyperledger/fabric-chaincode-java/commit/89ffb592) [FABCJ-273](https://jira.hyperledger.org/browse/FABCJ-273) Prepare next release v2.1.0 * [6c1cdba0](https://github.com/hyperledger/fabric-chaincode-java/commit/6c1cdba0) Java chaincode gRPC server for run external chaincode add NettyGrpcServer with method /Connect for start chat with peer without TLS add example external chaincode ## v2.3.0 Tue 3 Nov 2020 14:20:11 GMT * [081d5c8](https://github.com/hyperledger/fabric-chaincode-java/commit/081d5c8) Bump version to 2.2.2 * [b494824](https://github.com/hyperledger/fabric-chaincode-java/commit/b494824) [FABCJ-290](https://jira.hyperledger.org/browse/FABCJ-290) Add release guide ## v2.2.1 Wed 7 Oct 2020 16:21:53 BST * [cef231c](https://github.com/hyperledger/fabric-chaincode-java/commit/cef231c) [FABCJ-293](https://jira.hyperledger.org/browse/FABCJ-293) Remove gradle from image * [d2643ef](https://github.com/hyperledger/fabric-chaincode-java/commit/d2643ef) Minor Performance Improvements * [703558c](https://github.com/hyperledger/fabric-chaincode-java/commit/703558c) [FABCJ-291](https://jira.hyperledger.org/browse/FABCJ-291) Startkey needs additional checks For the open ended query, the start and empty keys are empty from the chaincode. However, in the shim, if the start key is an empty key, it is replaced with 0x01 which is nothing but a namespace for the non-composite key. * [f35ae08](https://github.com/hyperledger/fabric-chaincode-java/commit/f35ae08) Fix tagging of fabric-javaenv image * [cb31d36](https://github.com/hyperledger/fabric-chaincode-java/commit/cb31d36) Bump version to 2.2.1 ## v2.2.0 Thu 2 Jul 11:28:13 BST 2020 * [32c8201](https://github.com/hyperledger/fabric-chaincode-java/commit/32c8201) [FABCJ-289](https://jira.hyperledger.org/browse/FABCJ-289) release: 2.2.0 LTS * [0948234](https://github.com/hyperledger/fabric-chaincode-java/commit/0948234) [FABCJ-288](https://jira.hyperledger.org/browse/FABCJ-288) fix: simple key end of range * [8b06be2](https://github.com/hyperledger/fabric-chaincode-java/commit/8b06be2) [FABCJ-286](https://jira.hyperledger.org/browse/FABCJ-286) Prepare 2.1.2 ## v2.1.1 Mon 18 May 09:09:24 BST 2020 * [f0f958e](https://github.com/hyperledger/fabric-chaincode-java/commit/f0f958e) [FABCJ-284](https://jira.hyperledger.org/browse/FABCJ-284) Broken docs link * [b89c464](https://github.com/hyperledger/fabric-chaincode-java/commit/b89c464) [FABCJ-285](https://jira.hyperledger.org/browse/FABCJ-285) Remove incorrect log point * [93ff6bb](https://github.com/hyperledger/fabric-chaincode-java/commit/93ff6bb) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Bump version number to 2.1.1 ## v2.1.0 Thu 9 Apr 2020 14:13:36 BST * [72e6f78](https://github.com/hyperledger/fabric-chaincode-java/commit/72e6f78) [FABCJ-283](https://jira.hyperledger.org/browse/FABCJ-283) Update docker image version * [54606f8](https://github.com/hyperledger/fabric-chaincode-java/commit/54606f8) [FABCJ-269](https://jira.hyperledger.org/browse/FABCJ-269) Compatibility Matrix ## v2.0.1 Wed 4 Mar 16:38:58 GMT 2020 * [8cb6a25](https://github.com/hyperledger/fabric-chaincode-java/commit/8cb6a25) [FAB-16136](https://jira.hyperledger.org/browse/FAB-16136) Do not run tests in chaincode container * [3710641](https://github.com/hyperledger/fabric-chaincode-java/commit/3710641) [FABCJ-280](https://jira.hyperledger.org/browse/FABCJ-280) Copy chaincode into temporary directory before building * [a25a7d6](https://github.com/hyperledger/fabric-chaincode-java/commit/a25a7d6) [FABCJ-276](https://jira.hyperledger.org/browse/FABCJ-276) Access localmspid ## v2.0.0 Fri 24 Jan 10:26:03 GMT 2020 * [659a1c4](https://github.com/hyperledger/fabric-chaincode-java/commit/659a1c4) Port fixes from master branch for Coverage & typos * [9cf1e6a](https://github.com/hyperledger/fabric-chaincode-java/commit/9cf1e6a) [FABCI-482](https://jira.hyperledger.org/browse/FABCI-482) Update Nexus URL's to Artifactory * [7ab7145](https://github.com/hyperledger/fabric-chaincode-java/commit/7ab7145) Fix typos in README.md * [67fdc40](https://github.com/hyperledger/fabric-chaincode-java/commit/67fdc40) [FABCJ-259](https://jira.hyperledger.org/browse/FABCJ-259) Pagination Fix * [1609425](https://github.com/hyperledger/fabric-chaincode-java/commit/1609425) Update maintainers list * [1a45e3d](https://github.com/hyperledger/fabric-chaincode-java/commit/1a45e3d) [FABCJ-95](https://jira.hyperledger.org/browse/FABCJ-95) Checkstyle ## v2.0.0-beta Thu 12 Dec 12:45:44 GMT 2019 * [4a13009](https://github.com/hyperledger/fabric-chaincode-java/commit/4a13009) Add OWASP dependency checks to build * [44c96d7](https://github.com/hyperledger/fabric-chaincode-java/commit/44c96d7) FABCJ-258 Add latest image tag in AZP pipeline * [e1d53bf](https://github.com/hyperledger/fabric-chaincode-java/commit/e1d53bf) Remove -SNAPSHOT from package version variable * [fedc2ef](https://github.com/hyperledger/fabric-chaincode-java/commit/fedc2ef) Improve integration test reliability * [6fd5507](https://github.com/hyperledger/fabric-chaincode-java/commit/6fd5507) [FABCJ-257] Rationalize examples * [5d3cd53](https://github.com/hyperledger/fabric-chaincode-java/commit/5d3cd53) [FABCJ-160] Char support * [e32e404](https://github.com/hyperledger/fabric-chaincode-java/commit/e32e404) [FABCJ-183] Extra properties appear in JSON from Java Objects * [7f3c4e3](https://github.com/hyperledger/fabric-chaincode-java/commit/7f3c4e3) [FAB-17138](https://jira.hyperledger.org/browse/FAB-17138) Publish docker images to nexus * [b52d26a](https://github.com/hyperledger/fabric-chaincode-java/commit/b52d26a) [FAB-15634](https://jira.hyperledger.org/browse/FAB-15634) Annotations for Serializer * [207bd94](https://github.com/hyperledger/fabric-chaincode-java/commit/207bd94) [FAB-17100](https://jira.hyperledger.org/browse/FAB-17100) Default Thread Pool not set correctly * [f50a4ed](https://github.com/hyperledger/fabric-chaincode-java/commit/f50a4ed) [FAB-16712](https://jira.hyperledger.org/browse/FAB-16712) Disable stalebot * [ee8a1f4](https://github.com/hyperledger/fabric-chaincode-java/commit/ee8a1f4) Update javadoc location in readme * [3951dca](https://github.com/hyperledger/fabric-chaincode-java/commit/3951dca) Fix git user email/name config in azure pipeline * [55ad1b9](https://github.com/hyperledger/fabric-chaincode-java/commit/55ad1b9) Update javadoc build to use Hyperledger Bot * [4680efc](https://github.com/hyperledger/fabric-chaincode-java/commit/4680efc) Publish javadoc * [d4981a3](https://github.com/hyperledger/fabric-chaincode-java/commit/d4981a3) Fix the name of the docker image * [6553ffc](https://github.com/hyperledger/fabric-chaincode-java/commit/6553ffc) Update readme * [3b029a8](https://github.com/hyperledger/fabric-chaincode-java/commit/3b029a8) Fix the name of the docker image * [d01eeff](https://github.com/hyperledger/fabric-chaincode-java/commit/d01eeff) Add artifacts to build result * [3c2c27c](https://github.com/hyperledger/fabric-chaincode-java/commit/3c2c27c) [FAB-16712](https://jira.hyperledger.org/browse/FAB-16712) Update contributing guide * [d13e0c0](https://github.com/hyperledger/fabric-chaincode-java/commit/d13e0c0) Next steps in publishing * [75a0bed](https://github.com/hyperledger/fabric-chaincode-java/commit/75a0bed) Next steps in publishing * [fd2e21a](https://github.com/hyperledger/fabric-chaincode-java/commit/fd2e21a) Next steps in publishing * [5618de6](https://github.com/hyperledger/fabric-chaincode-java/commit/5618de6) Push the snapshot docker images to nexus * [5a11c6e](https://github.com/hyperledger/fabric-chaincode-java/commit/5a11c6e) Push the snapshot docker images to nexus * [4eff463](https://github.com/hyperledger/fabric-chaincode-java/commit/4eff463) [FAB-16315](https://jira.hyperledger.org/browse/FAB-16315) Improved Load Ability * [11a26ce](https://github.com/hyperledger/fabric-chaincode-java/commit/11a26ce) Fix Azure Pipelines * [7b3e509](https://github.com/hyperledger/fabric-chaincode-java/commit/7b3e509) [FAB-16711](https://jira.hyperledger.org/browse/FAB-16711) Azure pipelines * [a3304ec](https://github.com/hyperledger/fabric-chaincode-java/commit/a3304ec) [FAB-16707](https://jira.hyperledger.org/browse/FAB-16707) Remove Gradle warnings * [af3dec0](https://github.com/hyperledger/fabric-chaincode-java/commit/af3dec0) [IN-68] Add default GitHub SECURITY policy * [88b9397](https://github.com/hyperledger/fabric-chaincode-java/commit/88b9397) [FAB-16680](https://jira.hyperledger.org/browse/FAB-16680) Fix cloudflare error on jitpack.io * [f6076eb](https://github.com/hyperledger/fabric-chaincode-java/commit/f6076eb) [FAB-16669](https://jira.hyperledger.org/browse/FAB-16669) WIP: Use native Java 11 ALPN support * [5520053](https://github.com/hyperledger/fabric-chaincode-java/commit/5520053) [FAB-16669](https://jira.hyperledger.org/browse/FAB-16669) Update gRPC and Protocol Buffers code * [abff28f](https://github.com/hyperledger/fabric-chaincode-java/commit/abff28f) [FAB-16655](https://jira.hyperledger.org/browse/FAB-16655) Warnings as errors for Java chaincode docs * [50e75bf](https://github.com/hyperledger/fabric-chaincode-java/commit/50e75bf) [FAB-16655](https://jira.hyperledger.org/browse/FAB-16655) Warnings as errors for Java chaincode * [d79f5a6](https://github.com/hyperledger/fabric-chaincode-java/commit/d79f5a6) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Remove tests from Docker image build * [35d5884](https://github.com/hyperledger/fabric-chaincode-java/commit/35d5884) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Various chaincode deployment updates * [12b5243](https://github.com/hyperledger/fabric-chaincode-java/commit/12b5243) [FAB-16493](https://jira.hyperledger.org/browse/FAB-16493) Fixed gradle build on windows * [2f6be19](https://github.com/hyperledger/fabric-chaincode-java/commit/2f6be19) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Remove cglib dependency * [56c533e](https://github.com/hyperledger/fabric-chaincode-java/commit/56c533e) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Upgrade Docker image to Java 11 * [5dbbea7](https://github.com/hyperledger/fabric-chaincode-java/commit/5dbbea7) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Replace org.reflections with classgraph * [059d043](https://github.com/hyperledger/fabric-chaincode-java/commit/059d043) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Add javax.xml.bind dependency for Java 11 * [cbe663b](https://github.com/hyperledger/fabric-chaincode-java/commit/cbe663b) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Upgrade to Gradle 5.6.2 and Maven 3.6.2 * [887153c](https://github.com/hyperledger/fabric-chaincode-java/commit/887153c) [FAB-6415](https://jira.hyperledger.org/browse/FAB-6415) Add javax.annotation dependency for Java 11 * [66e9079](https://github.com/hyperledger/fabric-chaincode-java/commit/66e9079) [FAB-16489](https://jira.hyperledger.org/browse/FAB-16489) Add CODEOWNERS * [182c050](https://github.com/hyperledger/fabric-chaincode-java/commit/182c050) [FAB-15507](https://jira.hyperledger.org/browse/FAB-15507) Add doc * [1a38b84](https://github.com/hyperledger/fabric-chaincode-java/commit/1a38b84) [FABN-1320] Remove sed from git_tag.sh script * [25ed6c1](https://github.com/hyperledger/fabric-chaincode-java/commit/25ed6c1) [FABN-1320] Fix git_tag.sh * [f47f601](https://github.com/hyperledger/fabric-chaincode-java/commit/f47f601) [FAB-15929](https://jira.hyperledger.org/browse/FAB-15929) Add getPrivateDataHash support * [4371d07](https://github.com/hyperledger/fabric-chaincode-java/commit/4371d07) [FAB-16217](https://jira.hyperledger.org/browse/FAB-16217) Do not load JSON Schema schema from network * [44d76f4](https://github.com/hyperledger/fabric-chaincode-java/commit/44d76f4) [FAB-15895](https://jira.hyperledger.org/browse/FAB-15895) Added client identity to context * [9169e14](https://github.com/hyperledger/fabric-chaincode-java/commit/9169e14) New maintainer application * [12309b5](https://github.com/hyperledger/fabric-chaincode-java/commit/12309b5) [FAB-16091](https://jira.hyperledger.org/browse/FAB-16091) Handle invalid contract name * [e8ca970](https://github.com/hyperledger/fabric-chaincode-java/commit/e8ca970) [FAB-15647](https://jira.hyperledger.org/browse/FAB-15647) Fix markup of README * [360d75a](https://github.com/hyperledger/fabric-chaincode-java/commit/360d75a) [FAB-15883](https://jira.hyperledger.org/browse/FAB-15883) Removed swagger annotations * [25c5be6](https://github.com/hyperledger/fabric-chaincode-java/commit/25c5be6) [FAB-15615](https://jira.hyperledger.org/browse/FAB-15615) Add ChaincodeException support * [9077581](https://github.com/hyperledger/fabric-chaincode-java/commit/9077581) [FAB-15823](https://jira.hyperledger.org/browse/FAB-15823) Fix getStringPayload npe * [e163d3f](https://github.com/hyperledger/fabric-chaincode-java/commit/e163d3f) [FAB-15743](https://jira.hyperledger.org/browse/FAB-15743) Align Context * [5ad6ed8](https://github.com/hyperledger/fabric-chaincode-java/commit/5ad6ed8) [FAB-15507](https://jira.hyperledger.org/browse/FAB-15507) Exclude unnecessary contract javadoc * [9cc6553](https://github.com/hyperledger/fabric-chaincode-java/commit/9cc6553) [FAB-15720](https://jira.hyperledger.org/browse/FAB-15720) prevent duplicate transactions * [f87de8e](https://github.com/hyperledger/fabric-chaincode-java/commit/f87de8e) [FAB-15632](https://jira.hyperledger.org/browse/FAB-15632) Add name element to transaction annotation * [d726175](https://github.com/hyperledger/fabric-chaincode-java/commit/d726175) [FAB-13803](https://jira.hyperledger.org/browse/FAB-13803) Parameter Marshalling * [72dc716](https://github.com/hyperledger/fabric-chaincode-java/commit/72dc716) [FAB-15214](https://jira.hyperledger.org/browse/FAB-15214) Remove init annotation * [509bfbf](https://github.com/hyperledger/fabric-chaincode-java/commit/509bfbf) [FAB-13802](https://jira.hyperledger.org/browse/FAB-13802) Metadata Support * [0467032](https://github.com/hyperledger/fabric-chaincode-java/commit/0467032) Application to be consider for maintainer of this repo * [526f36d](https://github.com/hyperledger/fabric-chaincode-java/commit/526f36d) [FAB-13802](https://jira.hyperledger.org/browse/FAB-13802) Return types update * [1662390](https://github.com/hyperledger/fabric-chaincode-java/commit/1662390) [FAB-13912](https://jira.hyperledger.org/browse/FAB-13912) Implement new interfaces * [bd1c269](https://github.com/hyperledger/fabric-chaincode-java/commit/bd1c269) [FAB-14961](https://jira.hyperledger.org/browse/FAB-14961) Update to the FAQ * [dc02c3b](https://github.com/hyperledger/fabric-chaincode-java/commit/dc02c3b) [FAB-14995](https://jira.hyperledger.org/browse/FAB-14995) Prepare for next release ## v2.0.0-alpha Sun Apr 9 15:37:09 IDT 2019 * [b62740c](https://github.com/hyperledger/fabric-chaincode-java/commit/b62740c) [FAB-13798](https://jira.hyperledger.org/browse/FAB-13798) New interfaces * [8235149](https://github.com/hyperledger/fabric-chaincode-java/commit/8235149) [FAB-13795](https://jira.hyperledger.org/browse/FAB-13795) Contract definition * [762a955](https://github.com/hyperledger/fabric-chaincode-java/commit/762a955) [FAB-13800](https://jira.hyperledger.org/browse/FAB-13800) Transaction Context * [351acb5](https://github.com/hyperledger/fabric-chaincode-java/commit/351acb5) [FAB-13794](https://jira.hyperledger.org/browse/FAB-13794) New Annotations * [3e13a57](https://github.com/hyperledger/fabric-chaincode-java/commit/3e13a57) [FAB-12960](https://jira.hyperledger.org/browse/FAB-12960) Adding cc without main method * [40bf2c1](https://github.com/hyperledger/fabric-chaincode-java/commit/40bf2c1) [FAB-14533](https://jira.hyperledger.org/browse/FAB-14533) Updating integration tests * [b17d8a2](https://github.com/hyperledger/fabric-chaincode-java/commit/b17d8a2) [FAB-12504](https://jira.hyperledger.org/browse/FAB-12504) Added maven integration test. * [0e76b72](https://github.com/hyperledger/fabric-chaincode-java/commit/0e76b72) [FAB-14522](https://jira.hyperledger.org/browse/FAB-14522) README update: sdk compatibility * [e5d3e40](https://github.com/hyperledger/fabric-chaincode-java/commit/e5d3e40) [FAB-14460](https://jira.hyperledger.org/browse/FAB-14460) Better chaincode source check * [0adaf35](https://github.com/hyperledger/fabric-chaincode-java/commit/0adaf35) [FAB-13490](https://jira.hyperledger.org/browse/FAB-13490) javaenv image based on openjdk:slim-8 * [e33b4bd](https://github.com/hyperledger/fabric-chaincode-java/commit/e33b4bd) [FAB-13672](https://jira.hyperledger.org/browse/FAB-13672) stop using deprecated left closure * [c3e342a](https://github.com/hyperledger/fabric-chaincode-java/commit/c3e342a) [FAB-13097](https://jira.hyperledger.org/browse/FAB-13097) Getting correct jar * [cc76d2f](https://github.com/hyperledger/fabric-chaincode-java/commit/cc76d2f) [FAB-13097](https://jira.hyperledger.org/browse/FAB-13097) Adding shim 1.3.0 to docker image * [b22858b](https://github.com/hyperledger/fabric-chaincode-java/commit/b22858b) Configure Stale ProBot * [1279d2c](https://github.com/hyperledger/fabric-chaincode-java/commit/1279d2c) [FAB-13436](https://jira.hyperledger.org/browse/FAB-13436) multistage docker build * [035a7d2](https://github.com/hyperledger/fabric-chaincode-java/commit/035a7d2) [FAB-13243](https://jira.hyperledger.org/browse/FAB-13243) fix javaenv docker build tag * [ad89bbb](https://github.com/hyperledger/fabric-chaincode-java/commit/ad89bbb) [FAB-13248](https://jira.hyperledger.org/browse/FAB-13248) Update dependencies for sonatype * [cea4db0](https://github.com/hyperledger/fabric-chaincode-java/commit/cea4db0) [FAB-13229](https://jira.hyperledger.org/browse/FAB-13229) Prepare for next release (2.0.0 on master) * [4c6cd6c](https://github.com/hyperledger/fabric-chaincode-java/commit/4c6cd6c) [FAB-13236](https://jira.hyperledger.org/browse/FAB-13236) Publish jars to sonatype ## v1.4.0-rc1 Sun Apr 7 15:37:00 IDT 2019 * [2d26e5d](https://github.com/hyperledger/fabric-chaincode-java/commit/2d26e5d) [FAB-13117](https://jira.hyperledger.org/browse/FAB-13117) Release java chaincode 1.4.0-rc1 * [3628832](https://github.com/hyperledger/fabric-chaincode-java/commit/3628832) [FAB-13194](https://jira.hyperledger.org/browse/FAB-13194) Script to publish javaenv multiarch image * [455100c](https://github.com/hyperledger/fabric-chaincode-java/commit/455100c) [FAB-12426](https://jira.hyperledger.org/browse/FAB-12426) Fix java chaincode return status. * [ea26118](https://github.com/hyperledger/fabric-chaincode-java/commit/ea26118) [FAB-12197](https://jira.hyperledger.org/browse/FAB-12197) Integration tests * [3b61085](https://github.com/hyperledger/fabric-chaincode-java/commit/3b61085) [FAB-12110](https://jira.hyperledger.org/browse/FAB-12110) SimpleAsset java chaincode example * [b46fc70](https://github.com/hyperledger/fabric-chaincode-java/commit/b46fc70) [FAB-12328](https://jira.hyperledger.org/browse/FAB-12328) java cc pagination * [68685dc](https://github.com/hyperledger/fabric-chaincode-java/commit/68685dc) [FAB-12469](https://jira.hyperledger.org/browse/FAB-12469) Java sbe tests * [748ea9f](https://github.com/hyperledger/fabric-chaincode-java/commit/748ea9f) [FAB-12468](https://jira.hyperledger.org/browse/FAB-12468) Utility classes for java sbe * [b4b3a87](https://github.com/hyperledger/fabric-chaincode-java/commit/b4b3a87) [FAB-12467](https://jira.hyperledger.org/browse/FAB-12467) State-based endorsement * [c32c4c4](https://github.com/hyperledger/fabric-chaincode-java/commit/c32c4c4) [FAB-12568](https://jira.hyperledger.org/browse/FAB-12568) Adding identities to JavaCC * [fb81132](https://github.com/hyperledger/fabric-chaincode-java/commit/fb81132) Fix LFID for gennadyl * [7522309](https://github.com/hyperledger/fabric-chaincode-java/commit/7522309) Retire dormant maintainers, suggest new nomination * [321db66](https://github.com/hyperledger/fabric-chaincode-java/commit/321db66) [FAB-12444](https://jira.hyperledger.org/browse/FAB-12444) Update java cc to baseimage 0.4.14 * [aa177d2](https://github.com/hyperledger/fabric-chaincode-java/commit/aa177d2) [FAB-12347](https://jira.hyperledger.org/browse/FAB-12347) Update java cc to baseimage 0.4.13 * [e9c5602](https://github.com/hyperledger/fabric-chaincode-java/commit/e9c5602) [FAB-12223](https://jira.hyperledger.org/browse/FAB-12223) Adding FAQ file * [af01201](https://github.com/hyperledger/fabric-chaincode-java/commit/af01201) [FAB-12278](https://jira.hyperledger.org/browse/FAB-12278) Fixing java cc build script * [52cc6bd](https://github.com/hyperledger/fabric-chaincode-java/commit/52cc6bd) [FAB-12157](https://jira.hyperledger.org/browse/FAB-12157) Hadling chaincode in default package * [8dcd819](https://github.com/hyperledger/fabric-chaincode-java/commit/8dcd819) [FAB-12152](https://jira.hyperledger.org/browse/FAB-12152) Prepare for next release (1.4.0 on master) * [cc91af5](https://github.com/hyperledger/fabric-chaincode-java/commit/cc91af5) [FAB-12152](https://jira.hyperledger.org/browse/FAB-12152) Prepare for next release (1.3.0 on master) * [9cbb36c](https://github.com/hyperledger/fabric-chaincode-java/commit/9cbb36c) [FAB-10525](https://jira.hyperledger.org/browse/FAB-10525) Fix the bug getStateByPartialCompositeKey ## v1.3.0-rc1 Tue Sep 25 15:25:05 EDT 2018 * [224bc4d](https://github.com/hyperledger/fabric-chaincode-java/commit/224bc4d) [FAB-12160](https://jira.hyperledger.org/browse/FAB-12160) Release fabric-chaincode-java v1.3.0-rc1 * [333d91b](https://github.com/hyperledger/fabric-chaincode-java/commit/333d91b) [FAB-12129](https://jira.hyperledger.org/browse/FAB-12129) Update baseimage version * [01dd207](https://github.com/hyperledger/fabric-chaincode-java/commit/01dd207) [FAB-12115](https://jira.hyperledger.org/browse/FAB-12115) Fix groupId in shim jars * [5d7ed1c](https://github.com/hyperledger/fabric-chaincode-java/commit/5d7ed1c) [FAB-9519](https://jira.hyperledger.org/browse/FAB-9519) Java shim readmy and tutorial * [44a1367](https://github.com/hyperledger/fabric-chaincode-java/commit/44a1367) [FAB-12017](https://jira.hyperledger.org/browse/FAB-12017) Adding java docs * [56e2a11](https://github.com/hyperledger/fabric-chaincode-java/commit/56e2a11) [FAB-11839](https://jira.hyperledger.org/browse/FAB-11839) Adding FVT tests * [ac70a6f](https://github.com/hyperledger/fabric-chaincode-java/commit/ac70a6f) [FAB-10032](https://jira.hyperledger.org/browse/FAB-10032) Adding unit test * [ac6c906](https://github.com/hyperledger/fabric-chaincode-java/commit/ac6c906) [FAB-11750](https://jira.hyperledger.org/browse/FAB-11750) Updating version to 1.3.0 * [e80ba7c](https://github.com/hyperledger/fabric-chaincode-java/commit/e80ba7c) [FAB-11288](https://jira.hyperledger.org/browse/FAB-11288) Adding private data functions * [79497bc](https://github.com/hyperledger/fabric-chaincode-java/commit/79497bc) [FAB-11741](https://jira.hyperledger.org/browse/FAB-11741) Reformat code * [71ca2a2](https://github.com/hyperledger/fabric-chaincode-java/commit/71ca2a2) [FAB-11664](https://jira.hyperledger.org/browse/FAB-11664) Enable separate docker build * [52f2c8b](https://github.com/hyperledger/fabric-chaincode-java/commit/52f2c8b) [FAB-11485](https://jira.hyperledger.org/browse/FAB-11485) Fixing empty proposal handling * [2b35a46](https://github.com/hyperledger/fabric-chaincode-java/commit/2b35a46) [FAB-11304](https://jira.hyperledger.org/browse/FAB-11304) Handling READY message from peer * [4b27549](https://github.com/hyperledger/fabric-chaincode-java/commit/4b27549) [FAB-9380](https://jira.hyperledger.org/browse/FAB-9380) remove FSM from java chaincode shim * [f5e6fb4](https://github.com/hyperledger/fabric-chaincode-java/commit/f5e6fb4) [FAB-9920](https://jira.hyperledger.org/browse/FAB-9920) Java shim mutual TLS * [d295c96](https://github.com/hyperledger/fabric-chaincode-java/commit/d295c96) [FAB-9424](https://jira.hyperledger.org/browse/FAB-9424) Adding docker build support * [1cb0493](https://github.com/hyperledger/fabric-chaincode-java/commit/1cb0493) [FAB-10845](https://jira.hyperledger.org/browse/FAB-10845) Remove protos files * [6c8abf4](https://github.com/hyperledger/fabric-chaincode-java/commit/6c8abf4) [FAB-9416](https://jira.hyperledger.org/browse/FAB-9416): Add gradle task to update protos * [e3be99e](https://github.com/hyperledger/fabric-chaincode-java/commit/e3be99e) [FAB-9407](https://jira.hyperledger.org/browse/FAB-9407): Extract JavaCC shim protos * [e4c9867](https://github.com/hyperledger/fabric-chaincode-java/commit/e4c9867) [FAB-9359](https://jira.hyperledger.org/browse/FAB-9359) add CODE_OF_CONDUCT.md and other docs * [8772201](https://github.com/hyperledger/fabric-chaincode-java/commit/8772201) [FAB-9063](https://jira.hyperledger.org/browse/FAB-9063) Provide link to Fabric committers * [4a9406b](https://github.com/hyperledger/fabric-chaincode-java/commit/4a9406b) Add Maintainers to fabric-chaincode-java * [091a081](https://github.com/hyperledger/fabric-chaincode-java/commit/091a081) [FAB-8986](https://jira.hyperledger.org/browse/FAB-8986): Harden gradle build configuration * [bb958eb](https://github.com/hyperledger/fabric-chaincode-java/commit/bb958eb) [FAB-8346](https://jira.hyperledger.org/browse/FAB-8346) sync chaincode proto files w/ fabric * [c399853](https://github.com/hyperledger/fabric-chaincode-java/commit/c399853) [FAB-7989](https://jira.hyperledger.org/browse/FAB-7989) --peerAddress should be --peer.address * [308eb63](https://github.com/hyperledger/fabric-chaincode-java/commit/308eb63) [FAB-7626](https://jira.hyperledger.org/browse/FAB-7626) Move to Gradle 4.4.1 * [d73edd6](https://github.com/hyperledger/fabric-chaincode-java/commit/d73edd6) [FAB-7294](https://jira.hyperledger.org/browse/FAB-7294) enable shim jar publication * [d9c463e](https://github.com/hyperledger/fabric-chaincode-java/commit/d9c463e) [FAB-7091](https://jira.hyperledger.org/browse/FAB-7091) Extract StreamObserver out of ChaincodeBase * [e3644f4](https://github.com/hyperledger/fabric-chaincode-java/commit/e3644f4) [FAB-6889](https://jira.hyperledger.org/browse/FAB-6889) add channel ID to chaincode message * [2eda08b](https://github.com/hyperledger/fabric-chaincode-java/commit/2eda08b) [FAB-6726](https://jira.hyperledger.org/browse/FAB-6726) move to gradle 4.2.1 * [5cf4f73](https://github.com/hyperledger/fabric-chaincode-java/commit/5cf4f73) [FAB-3650](https://jira.hyperledger.org/browse/FAB-3650) add getBinding() API * [a3b7d71](https://github.com/hyperledger/fabric-chaincode-java/commit/a3b7d71) [FAB-3649](https://jira.hyperledger.org/browse/FAB-3649) add getTransient() API * [3cb86b0](https://github.com/hyperledger/fabric-chaincode-java/commit/3cb86b0) [FAB-3648](https://jira.hyperledger.org/browse/FAB-3648) add getCreator() API * [5982e40](https://github.com/hyperledger/fabric-chaincode-java/commit/5982e40) [FAB-3653](https://jira.hyperledger.org/browse/FAB-3653) add getTxTimestamp() API * [5566a62](https://github.com/hyperledger/fabric-chaincode-java/commit/5566a62) [FAB-6681](https://jira.hyperledger.org/browse/FAB-6681) Add code coverage report and verification * [64de43a](https://github.com/hyperledger/fabric-chaincode-java/commit/64de43a) [FAB-6637](https://jira.hyperledger.org/browse/FAB-6637) add license check to fabric-chaincode-java * [9f2f4f9](https://github.com/hyperledger/fabric-chaincode-java/commit/9f2f4f9) [FAB-3470](https://jira.hyperledger.org/browse/FAB-3470) update composite key format * [61b8fc2](https://github.com/hyperledger/fabric-chaincode-java/commit/61b8fc2) [FAB-3651](https://jira.hyperledger.org/browse/FAB-3651) add getSignedProposal() API * [8c2352a](https://github.com/hyperledger/fabric-chaincode-java/commit/8c2352a) [FAB-6494](https://jira.hyperledger.org/browse/FAB-6494) Add unit tests for ChaincodeStubImpl * [b58bcc2](https://github.com/hyperledger/fabric-chaincode-java/commit/b58bcc2) Update Jim's RC ID in MAINTAINERS.rst * [02520ac](https://github.com/hyperledger/fabric-chaincode-java/commit/02520ac) Initial MAINTAINERS file * [915824f](https://github.com/hyperledger/fabric-chaincode-java/commit/915824f) [FAB-6435](https://jira.hyperledger.org/browse/FAB-6435) Copy Java shim from fabric repository * [067b712](https://github.com/hyperledger/fabric-chaincode-java/commit/067b712) [FAB-5928](https://jira.hyperledger.org/browse/FAB-5928) Initial gradle build script ================================================ FILE: CODEOWNERS ================================================ # SPDX-License-Identifier: Apache-2.0 # Fabric Chaincode Java Maintainers * @hyperledger/fabric-chaincode-java-maintainers ================================================ FILE: CODE_OF_CONDUCT.md ================================================ Code of Conduct Guidelines ========================== Please review the Hyperledger [Code of Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct) before participating. It is important that we keep things civil. Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. ================================================ FILE: COMPATIBILITY.md ================================================ # Support and Compatibility Github is used for code base management and issue tracking. ## Summary of Compatibility This table shows the summary of the compatibility of the Java chaincode libraries, together with the JVM version they require. | Java chaincode version | Minimum supported Java | Java runtime | Docker image platforms | |------------------------|------------------------|--------------|------------------------| | v1.4 | 8 | 8 | amd64 | | v2.2 | 11 | 11 | amd64 | | v2.5.0 - v2.5.4 | 11 | 11 | amd64, arm64 | | v2.5.5 - v2.5.7 | 11 | 21 | amd64, arm64 | | v2.5.8+ | 11 | 25 | amd64, arm64 | The Java runtime provided by the chaincode Docker image determines the maximum Java version (and features) that smart contract code can exploit when using the default Java chaincode container. Subject to a suitable runtime environment, the Java chaincode libraries can be used to communicate with Fabric peers at different LTS versions. The level of functionality is determined by the Fabric version in use and channel capabilities. All Docker images, chaincode libraries and tools are tested using amd64 (x86-64) only. ## Chaincode builder The default Fabric chaincode builder creates a Docker container to run deployed smart contracts. Java chaincode Docker containers are built using the `hyperledger/fabric-javaenv` Docker image, tagged with the same major and minor version as the Fabric peer version. For example, Fabric v2.5 creates Java chaincode containers using the `hyperledger/fabric-javaenv:2.5` Docker image. Fabric v3 continues to use the v2.5 Java chaincode image. A different chaincode Docker image can be specified using the `CORE_CHAINCODE_JAVA_RUNTIME` environment variable on the Fabric peer. For example, `CORE_CHAINCODE_JAVA_RUNTIME=example/customJavaRuntime:latest`. With Fabric v2 and later, an alternative chaincode builder can be configured on the Fabric peer. In this case the configured chaincode builder controls how chaincode is launched. See the [Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/release-2.5/cc_launcher.html) for further details. ## Chaincode packaging When using the `hyperledger/fabric-javaenv` Java chaincode Docker images, deployed chaincode is built as follows: - If both Gradle and Maven files are present, Gradle is used. - Gradle build files can be either Groovy or Kotlin. - If Gradle or Maven wrappers are present, they will be used instead of the preinstalled Gradle or Maven versions. Remember that when using the wrappers, code will be downloaded from the Internet. Keep this in mind for any installation with limited network access. Alternatively, it is recommended to package prebuilt JAR files, including the smart contract and all dependencies. In this case, no Internet access is required when deploying Java chaincode. ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to fabric-chaincode-java We welcome contributions to the [Hyperledger Fabric](https://hyperledger-fabric.readthedocs.io) Project. There's always plenty to do! If you have any questions about the project or how to contribute, you can find us in the [fabric-contracts-api](https://discordapp.com/channels/905194001349627914/943090527920877598) channel on [Discord](https://discord.lfdecentralizedtrust.org/). Here are a few guidelines to help you contribute successfully... ## Issues All issues are tracked in the issues tab in GitHub. If you find a bug which we don't already know about, you can help us by creating a new issue describing the problem. Please include as much detail as possible to help us track down the cause.If you want to begin contributing code, looking through our open issues is a good way to start. Try looking for recent issues with detailed descriptions first, or ask us on Discord if you're unsure which issue to choose. ## Enhancements Make sure you have the support of the Hyperledger Fabric community before investing a lot of effort in project enhancements. Please look up the [Fabric RFC](https://github.com/hyperledger/fabric-rfcs) process for large changes. ## Pull Requests We use our own forks and [Github Flow](https://docs.github.com/en/get-started/using-github/github-flow) to deliver changes to the code. Follow these steps to deliver your first pull request: 1. [Fork the repository](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project) and create a new branch from `main`. 2. If you've added code that should be tested, add tests! 3. If you've added any new features or made breaking changes, update the documentation. 4. Ensure all the tests pass. 5. Include the JIRA issue number, a descriptive message, and the [Developer Certificate of Origin (DCO) sign-off](https://github.com/dcoapp/app#how-it-works) on all commit messages. 6. [Issue a pull request](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-a-pull-request)! 7. [GitHub Actions](https://github.com/hyperledger/fabric-chaincode-java/actions) builds must succeed before the pull request can be reviewed and merged. ## Coding Style Please to try to be consistent with the rest of the code and conform to checkstyle rules where they are provided. [Spotless](https://github.com/diffplug/spotless) is used to enforce code formatting. You can run `./gradlew spotlessApply` to apply the mandated code formatting to the codebase before submitting changes to avoid failing the build with formatting violations. ## Code of Conduct Guidelines See our [Code of Conduct Guidelines](../blob/main/CODE_OF_CONDUCT.md). ## Maintainers Should you have any questions or concerns, please reach out to one of the project's [Maintainers](../blob/main/MAINTAINERS.md). ## How to work with the Codebase Some useful gradle commands to help with building. You can add or remove the `--no-daemon` as you wish; depending on the performance of you local machine. ```shell # build everything ./gradlew --no-daemon build # clean up to force tests and compile to rerun ./gradlew clean cleanTest ./gradlew --no-daemon :fabric-chaincode-shim:build # build docker image ./gradlew :fabric-chaincode-docker:buildImage ``` You can also scan for vulnerabilities in dependencies (requires [Make](https://www.gnu.org/software/make/) and [Go](https://go.dev/) to be installed): ```shell make scan ``` ## Hyperledger Fabric See the [Hyperledger Fabric contributors guide](http://hyperledger-fabric.readthedocs.io/en/latest/CONTRIBUTING.html) for more details, including other Hyperledger Fabric projects you may wish to contribute to. --- [![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/) This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/) ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MAINTAINERS.md ================================================ # Maintainers | Name | GitHub | Chat | email | | ------------ | ----------------------------------------------------- | --------------- | -------------------------- | | Dave Enyeart | [denyeart](https://github.com/denyeart) | denyeart | | | Mark Lewis | [bestbeforetoday](https://github.com/bestbeforetoday) | bestbeforetoday | | # Retired Maintainers | Name | GitHub | Chat | email | | ----------------------- | ------------------------------------------------------- | ------------- | ------------------------------------- | | Artem Barger | [c0rwin](https://github.com/c0rwin) | c0rwin | | | Matthew B White | [mbwhite](https://github.com/mbwhite) | mbwhite | | | James Taylor | [jt-nti](https://github.com/jt-nti) | jtonline | | | Gari Singh | [mastersingh24](https://github.com/mastersingh24) | mastersingh24 | | | Gennady Laventman | [gennadylaventman](https://github.com/gennadylaventman) | gennadyl | | | Jim Zhang | [jimthematrix](https://github.com/jimthematrix) | jimthematrix | | | Luis Sanchez | [sanchezl](https://github.com/sanchezl) | sanchezl | | | Srinivasan Muralidharan | [muralisrini](https://github.com/muralisrini) | muralisr | | | Yacov Manevich | [yacovm](https://github.com/yacovm) | yacovm | | Also: Please see the [Release Manager section](https://github.com/hyperledger/fabric/blob/main/MAINTAINERS.md) Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. ================================================ FILE: Makefile ================================================ # # SPDX-License-Identifier: Apache-2.0 # bin_dir := bin osv-scanner := $(bin_dir)/osv-scanner kernel_name := $(shell uname -s | tr '[:upper:]' '[:lower:]') machine_hardware := $(shell uname -m) ifeq ($(machine_hardware), x86_64) machine_hardware := amd64 endif ifeq ($(machine_hardware), aarch64) machine_hardware := arm64 endif .PHONY: scan scan: $(osv-scanner) rm -f fabric-chaincode-shim/gradle.lockfile ./gradlew --quiet :fabric-chaincode-shim:dependencies --write-locks --configuration runtimeClasspath bin/osv-scanner scan --lockfile=fabric-chaincode-shim/gradle.lockfile .PHONY: scan-all scan-all: $(osv-scanner) rm -f fabric-chaincode-shim/gradle.lockfile ./gradlew --quiet :fabric-chaincode-shim:dependencies --write-locks bin/osv-scanner scan --lockfile=fabric-chaincode-shim/gradle.lockfile .PHONY: install-osv-scanner install-osv-scanner: mkdir -p '$(bin_dir)' curl --fail --location --show-error --silent --output '$(osv-scanner)' \ 'https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_$(kernel_name)_$(machine_hardware)' chmod u+x '$(osv-scanner)' $(osv-scanner): $(MAKE) install-osv-scanner ================================================ FILE: README.md ================================================ # Hyperledger Fabric Chaincode Java [![Build Status](https://dev.azure.com/Hyperledger/Fabric-Chaincode-Java/_apis/build/status/Fabric-Chaincode-Java?branchName=main)](https://dev.azure.com/Hyperledger/Fabric-Chaincode-Java/_build/latest?definitionId=39&branchName=main) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.hyperledger.fabric-chaincode-java/fabric-chaincode-shim/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.hyperledger.fabric-chaincode-java/fabric-chaincode-shim) [![Discord](https://img.shields.io/discord/905194001349627914?label=discord)](https://discordapp.com/channels/905194001349627914/943090527920877598) This is a Java based implementation of Hyperledger Fabric chaincode shim APIs, which enables development of smart contracts using the Java language. This project creates `fabric-chaincode-shim` jar files for developers' consumption and the `hyperledger/fabric-javaenv` docker image to run Java chaincode. ## Getting Started Application developers interested in developing Java smart contracts for Hyperledger Fabric should read the [JavaDoc](https://hyperledger.github.io/fabric-chaincode-java/) which includes download information, and links to documentation and samples. ## Project structure ### fabric-chaincode-shim Contains the java shim classes that define Java chaincode API and way to communicate with Fabric peers. ### fabric-chaincode-docker Contains instructions to build the `hyperledger/fabric-javaenv` docker image. ### fabric-chaincode-integration-test Contains higher level tests for Java chaincode. > **Note:** in the future these should be replaced with a separate suite of [Cucumber](https://cucumber.io) tests which run against all chaincode implementations. ### examples The following technical examples are in this repository. Please see the tutorials in the [documentation](https://hyperledger-fabric.readthedocs.io/en/latest/tutorials.html) - **fabric-contract-example-gradle** - Contains an example Java contract built using gradle - **fabric-contract-example-maven** - Contains an example Java contract built using maven - **fabric-contract-example-gradle-kotlin** - Contains an example Kotlin contract build using gradle (Kotlin gradle files) - **fabric-chaincode-example-sacc** - Contains an example java chaincode gradle project that includes sample chaincode and basic gradle build instructions. - **fabric-chaincode-example-sbe** - Contains an example java chaincode gradle project that includes state based endorsement ## 'dev' main branch builds These 'dev' drivers are built from the main branch only, and have a version format including the date for example `2.3.1.dev.20210303`. They are published to Artifactory. These can be accessed via the repository at ``` maven { url "https://hyperledger.jfrog.io/hyperledger/fabric-maven" } ``` They can be accessed in a build file like this ``` dependencies { compile group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '2.3.1.dev.+' } ``` ## Building and testing Make sure you have the following prereqs installed: - [Docker](https://www.docker.com/get-docker) - [Docker Compose](https://docs.docker.com/compose/install/) - [JDK 11](https://adoptium.net/) > **Note:** Java can be installed using [sdkman](https://sdkman.io/). Clone the repository if you haven't already. ``` git clone https://github.com/hyperledger/fabric-chaincode-java.git ``` Build java shim jars (proto and shim jars) and install them to local maven repository. ``` cd fabric-chaincode-java ./gradlew clean build install ``` > **Note:** `./gradlew clean build classes` can be used instead to reduce the binaries that are built. This should be sufficient for using the local repository. Build javaenv docker image, to have it locally. ``` ./gradlew buildImage ``` ## Compatibility For details on what Java runtime and versions of Hyperledger Fabric can be used please see the [compatibility document](COMPATIBILITY.md). --- [![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/) This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/) ================================================ FILE: RELEASING.md ================================================ # Releasing The following artifacts are created as a result of releasing Fabric Chaincode Java: - `fabric-javaenv` Docker images: - [Docker Hub](https://hub.docker.com/r/hyperledger/fabric-javaenv) - [GitHub Packages](https://github.com/orgs/hyperledger/packages/container/package/fabric-javaenv) - `fabric-chaincode-shim` Java libraries: - [Maven Central](https://central.sonatype.com/artifact/org.hyperledger.fabric-chaincode-java/fabric-chaincode-shim) - [GitHub Packages](https://github.com/hyperledger/fabric-chaincode-java/packages/50049) ## Before releasing The following tasks are required before releasing: - Ensure the version number in `build.gradle` is the required release version. - Check the last branch build passed since exactly this repository state will be released. ## Create release Creating a GitHub release on the [releases page](https://github.com/hyperledger/fabric-chaincode-java/releases) will trigger the build to publish the new release. When drafting the release, create a new tag for the new version (with a `v` prefix). For example: `v2.1.4` See previous releases for examples of the title and description. ## After releasing - Update the version number in `build.gradle` to the next version. - Update image version numbers in `fabric-chaincode-docker/build.gradle` to match the next version. - Update the `fabric-chaincode-shim` dependency version in all `build.gradle` and `pom.xml` files within `fabric-chaincode-integration-test/src/contracts` to match the next version. - Update the `fabric-chaincode-shim` dependency version in all `build.gradle`, `build.gradle.kts` and `pom.xml` files within `examples` to match the last _released_ version. - Check that `COMPATIBILITY.md` is correct and update if required. ================================================ FILE: SECURITY.md ================================================ # Hyperledger Security Policy ## Reporting a Security Bug If you think you have discovered a security issue in any of the Hyperledger projects, we'd love to hear from you. We will take all security bugs seriously and if confirmed upon investigation we will patch it within a reasonable amount of time and release a public security bulletin discussing the impact and credit the discoverer. There are two ways to report a security bug. The easiest is to email a description of the flaw and any related information (e.g. reproduction steps, version) to [security at hyperledger dot org](mailto:security@hyperledger.org). The other way is to file a confidential security bug in our [JIRA bug tracking system](https://jira.hyperledger.org). Be sure to set the “Security Level” to “Security issue”. The process by which the Hyperledger Security Team handles security bugs is documented further in our [Defect Response page](https://wiki.hyperledger.org/display/SEC/Defect+Response) on our [wiki](https://wiki.hyperledger.org). ================================================ FILE: build.gradle ================================================ /* * Copyright IBM Corp. 2018 All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ plugins { id "com.github.ben-manes.versions" version "0.54.0" id "com.diffplug.spotless" version "8.4.0" id "com.gradleup.nmcp.aggregation" version "1.4.4" id "com.gradleup.nmcp" version "1.4.4" apply false } version = '2.5.9' // If the nightly property is set, then this is the scheduled main // build - and we should publish this to artifactory // // Use the .dev. format to match Maven convention if (properties.containsKey('NIGHTLY')) { version = version + '.dev.' + getDate() ext.nightly = true // set property for use in subprojects } else { ext.nightly = false } nmcpAggregation { centralPortal { username = findProperty('mavenCentralUsername') password = findProperty('mavenCentralPassword') publishingType = "AUTOMATIC" } } dependencies { nmcpAggregation(project(':fabric-chaincode-shim')) } allprojects { apply plugin: "com.diffplug.spotless" repositories { mavenCentral() } spotless { format 'misc', { target '*.gradle', '.gitattributes', '.gitignore' trimTrailingWhitespace() leadingTabsToSpaces() endWithNewline() } } } subprojects { apply plugin: 'java' apply plugin: "maven-publish" group = 'org.hyperledger.fabric-chaincode-java' version = rootProject.version compileJava { options.release = 11 options.compilerArgs += ['-Werror', '-Xlint:all'] } dependencies { implementation 'commons-cli:commons-cli:1.11.0' implementation 'commons-logging:commons-logging:1.3.6' testImplementation platform('org.junit:junit-bom:6.0.3') testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'org.assertj:assertj-core:3.27.7' testImplementation 'org.mockito:mockito-core:5.23.0' testImplementation 'uk.org.webcompere:system-stubs-jupiter:2.1.8' testImplementation 'org.hamcrest:hamcrest-library:3.0' } test { useJUnitPlatform() } spotless { java { removeUnusedImports() palantirJavaFormat().formatJavadoc(true) formatAnnotations() } } } // Get the date in the reverse format for sorting static def getDate() { def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate } ================================================ FILE: docs/404.md ================================================ --- title: "404 - Page Not Found" permalink: /404.html --- ## The page you wanted does not exist If you were looking for Javadoc, try one of the releases below: {% include javadocs.html %} ================================================ FILE: docs/_config.yml ================================================ theme: minima title: "fabric-chaincode-java" releases: - main - release-1.4 - release-2.2 ================================================ FILE: docs/_includes/footer.html ================================================ ================================================ FILE: docs/_includes/header.html ================================================ ================================================ FILE: docs/_includes/javadocs.html ================================================ ================================================ FILE: docs/index.md ================================================ --- layout: home --- Hyperledger Fabric offers a number of SDKs to support developing smart contracts (chaincode) in various programming languages. There are two other smart contract SDKs available for Go, and Node.js, in addition to this Java SDK: * [Go SDK documentation](https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim) * [Node.js SDK documentation](https://hyperledger.github.io/fabric-chaincode-node/) ## Documentation Detailed explanation on the concepts and programming model for smart contracts can be found in the [Chaincode Tutorials section of the Hyperledger Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/latest/developapps/smartcontract.html#). Javadoc is available for each release: {% include javadocs.html %} ## Download Gradle: ``` dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:VERSION' } ``` Maven: ``` org.hyperledger.fabric-chaincode-java fabric-chaincode-shim VERSION ``` More options can be found on the [central maven repository](https://search.maven.org/artifact/org.hyperledger.fabric-chaincode-java/fabric-chaincode-shim/). Check the [release notes](https://github.com/hyperledger/fabric-chaincode-java/releases) for the changes in each version. ## Compatibility For details on what versions of Java and Hyperledger Fabric can be used please see the [compatibility document](https://github.com/hyperledger/fabric-chaincode-java/blob/main/COMPATIBILITY.md). ## Samples Several Java chaincode samples can be found in the [fabric-samples repository](https://github.com/hyperledger/fabric-samples) ================================================ FILE: examples/fabric-contract-example-as-service/Dockerfile ================================================ # Copyright 2019 IBM All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 # Example multi-stage dockerfile for Java Chaincode # the first stage FROM gradle:9-jdk21 AS gradle_build # copy the build.gradle and src code to the container COPY src/ src/ COPY build.gradle ./ # Build and package our code RUN gradle build shadowJar # the second stage of our build just needs the compiled files FROM eclipse-temurin:25-jre # copy only the artifacts we need from the first stage and discard the rest COPY --from=gradle_build /home/gradle/build/libs/chaincode.jar /chaincode.jar ENV PORT=9999 EXPOSE 9999 # set the startup command to execute the jar CMD ["java", "-jar", "/chaincode.jar"] ================================================ FILE: examples/fabric-contract-example-as-service/README.md ================================================ # Java Chaincode -as-a-service This example shows how to start the chaincode in it's 'as-a-service' mode. Note that this has been improved over the original mechanism ## Build Changes There are no changes required to tbe build file. For example the `build.gradle` can be left like this ``` manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } ``` ## Contract Code No changes are required to the contract code. Note that the previous 'bootstrap' main method is not required. ## Environment Variables The setting of the `CHAINCODE_SERVER_ADDRESS` environment variable will trigger the code to work in the 'as-a-service' mode. This should be set to the hostname:port that the server will be exposed on. Typically this would be ``` CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 ``` *NOTE* if `CHAINCODE_SERVER_ADDRESS` is set, and the chaincode is deployed as a regular chaincode, this will result in a failure. The chaincode will still start in 'as-a-service' mode. The `CORE_CHAINCODE_ID_NAME` must also be set to match the ID used when deploying the chaincode. *For TLS* ensure that - `CORE_PEERT_TLS_ENABLED` is true - `CORE_PEER_TLS_ROOTCERT_FILE` is set to the certificate of the root CA - `CORE_TLS_CLIENT_KEY_FILE` and `CORE_TLS_CLIENT_CERT_FILE` if using mutual TLS (PEM encoded) ## Dockerfile There is an example dockerfile that shows how the chaincode can be built into a container image. ================================================ FILE: examples/fabric-contract-example-as-service/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } version = '0.0.1' repositories { mavenCentral() maven { url = "https://www.jitpack.io" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.8' implementation 'org.json:json:20251224' testImplementation platform('org.junit:junit-bom:6.0.2') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.assertj:assertj-core:3.27.7' testImplementation 'org.mockito:mockito-core:5.21.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.example.Application' } } compileJava { options.release.set(11) options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" } test { useJUnitPlatform() testLogging { events "PASSED", "SKIPPED", "FAILED" } } ================================================ FILE: examples/fabric-contract-example-as-service/src/main/java/org/example/contract/MyAsset.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example.contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONObject; @DataType() public class MyAsset { @Property() private String value; public MyAsset() { } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String toJSONString() { return new JSONObject(this).toString(); } public static MyAsset fromJSONString(String json) { String value = new JSONObject(json).getString("value"); MyAsset asset = new MyAsset(); asset.setValue(value); return asset; } } ================================================ FILE: examples/fabric-contract-example-as-service/src/main/java/org/example/contract/MyAssetContract.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example.contract; import static java.nio.charset.StandardCharsets.UTF_8; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Transaction; @Contract @Default public class MyAssetContract implements ContractInterface { public MyAssetContract() { } @Transaction() public boolean myAssetExists(Context ctx, String myAssetId) { byte[] buffer = ctx.getStub().getState(myAssetId); return (buffer != null && buffer.length > 0); } @Transaction() public void createMyAsset(Context ctx, String myAssetId, String value) { boolean exists = myAssetExists(ctx, myAssetId); if (exists) { throw new RuntimeException("The asset " + myAssetId + " already exists"); } MyAsset asset = new MyAsset(); asset.setValue(value); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public MyAsset readMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx, myAssetId); if (!exists) { throw new RuntimeException("The asset " + myAssetId + " does not exist"); } MyAsset newAsset = MyAsset.fromJSONString(new String(ctx.getStub().getState(myAssetId), UTF_8)); return newAsset; } @Transaction() public void updateMyAsset(Context ctx, String myAssetId, String newValue) { boolean exists = myAssetExists(ctx, myAssetId); if (!exists) { throw new RuntimeException("The asset " + myAssetId + " does not exist"); } MyAsset asset = new MyAsset(); asset.setValue(newValue); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public void deleteMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx, myAssetId); if (!exists) { throw new RuntimeException("The asset " + myAssetId + " does not exist"); } ctx.getStub().delState(myAssetId); } } ================================================ FILE: examples/fabric-contract-example-as-service/src/test/java/org/example/MyAssetContractTest.java ================================================ /* * SPDX-License-Identifier: Apache License 2.0 */ package org.example; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; import org.example.contract.MyAsset; import org.example.contract.MyAssetContract; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.shim.ChaincodeServerProperties; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public final class MyAssetContractTest { @Nested class AssetExists { @Test public void noProperAsset() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[]{}); boolean result = contract.myAssetExists(ctx, "10001"); assertFalse(result); } @Test public void assetExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[]{42}); boolean result = contract.myAssetExists(ctx, "10001"); assertTrue(result); } @Test public void noKey() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(null); boolean result = contract.myAssetExists(ctx, "10002"); assertFalse(result); } } @Nested class AssetCreates { @Test public void newAssetCreate() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); String json = "{\"value\":\"TheAsset\"}"; contract.createMyAsset(ctx, "10001", "TheAsset"); verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void alreadyExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(new byte[]{42}); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.createMyAsset(ctx, "10002", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10002 already exists"); } } @Test public void assetRead() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); MyAsset asset = new MyAsset(); asset.setValue("Valuable"); String json = asset.toJSONString(); when(stub.getState("10001")).thenReturn(json.getBytes(StandardCharsets.UTF_8)); MyAsset returnedAsset = contract.readMyAsset(ctx, "10001"); assertEquals(returnedAsset.getValue(), asset.getValue()); } @Nested class AssetUpdates { @Test public void updateExisting() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[]{42}); contract.updateMyAsset(ctx, "10001", "updates"); String json = "{\"value\":\"updates\"}"; verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void updateMissing() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.updateMyAsset(ctx, "10001", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } } @Test public void assetDelete() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.deleteMyAsset(ctx, "10001"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } @Test public void test() { ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setKeepAliveTimeMinutes(-10); assertEquals(true, true); } } ================================================ FILE: examples/fabric-contract-example-gradle/.gitignore ================================================ .gradle/ build/ bin/ ================================================ FILE: examples/fabric-contract-example-gradle/README.md ================================================ This example needs to use gradle4.6 please install this first eg using sdkman `sdk install gradle 4.6` and then add the wrapper code before building `gradle wrapper` ================================================ FILE: examples/fabric-contract-example-gradle/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } version = '0.0.1' repositories { mavenCentral() maven { url = "https://www.jitpack.io" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.8' implementation 'org.json:json:20251224' testImplementation platform('org.junit:junit-bom:6.0.2') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.assertj:assertj-core:3.27.7' testImplementation 'org.mockito:mockito-core:5.21.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } } compileJava { options.release.set(11) options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" } test { useJUnitPlatform() testLogging { events "PASSED", "SKIPPED", "FAILED" } } ================================================ FILE: examples/fabric-contract-example-gradle/src/main/java/org/example/MyAsset.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONObject; @DataType() public class MyAsset { @Property() private String value; public MyAsset(){ } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String toJSONString() { return new JSONObject(this).toString(); } public static MyAsset fromJSONString(String json) { String value = new JSONObject(json).getString("value"); MyAsset asset = new MyAsset(); asset.setValue(value); return asset; } } ================================================ FILE: examples/fabric-contract-example-gradle/src/main/java/org/example/MyAssetContract.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Transaction; import io.swagger.v3.oas.annotations.info.Contact; import io.swagger.v3.oas.annotations.info.Info; import io.swagger.v3.oas.annotations.info.License; import static java.nio.charset.StandardCharsets.UTF_8; @Contract(name = "MyAssetContract", info = @Info(title = "MyAsset contract", description = "Very basic Java Contract example", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "MyAssetContract@example.com", name = "MyAssetContract", url = "http://MyAssetContract.me"))) @Default public class MyAssetContract implements ContractInterface { public MyAssetContract() { } @Transaction() public boolean myAssetExists(Context ctx, String myAssetId) { byte[] buffer = ctx.getStub().getState(myAssetId); return (buffer != null && buffer.length > 0); } @Transaction() public void createMyAsset(Context ctx, String myAssetId, String value) { boolean exists = myAssetExists(ctx,myAssetId); if (exists) { throw new RuntimeException("The asset "+myAssetId+" already exists"); } MyAsset asset = new MyAsset(); asset.setValue(value); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public MyAsset readMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } MyAsset newAsset = MyAsset.fromJSONString(new String(ctx.getStub().getState(myAssetId),UTF_8)); return newAsset; } @Transaction() public void updateMyAsset(Context ctx, String myAssetId, String newValue) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } MyAsset asset = new MyAsset(); asset.setValue(newValue); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public void deleteMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } ctx.getStub().delState(myAssetId); } } ================================================ FILE: examples/fabric-contract-example-gradle/src/test/java/org/example/MyAssetContractTest.java ================================================ /* * SPDX-License-Identifier: Apache License 2.0 */ package org.example; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public final class MyAssetContractTest { @Nested class AssetExists { @Test public void noProperAsset() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] {}); boolean result = contract.myAssetExists(ctx,"10001"); assertFalse(result); } @Test public void assetExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] {42}); boolean result = contract.myAssetExists(ctx,"10001"); assertTrue(result); } @Test public void noKey() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(null); boolean result = contract.myAssetExists(ctx,"10002"); assertFalse(result); } } @Nested class AssetCreates { @Test public void newAssetCreate() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); String json = "{\"value\":\"TheAsset\"}"; contract.createMyAsset(ctx, "10001", "TheAsset"); verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void alreadyExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(new byte[] { 42 }); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.createMyAsset(ctx, "10002", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10002 already exists"); } } @Test public void assetRead() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); MyAsset asset = new MyAsset(); asset.setValue("Valuable"); String json = asset.toJSONString(); when(stub.getState("10001")).thenReturn(json.getBytes(StandardCharsets.UTF_8)); MyAsset returnedAsset = contract.readMyAsset(ctx, "10001"); assertEquals(returnedAsset.getValue(), asset.getValue()); } @Nested class AssetUpdates { @Test public void updateExisting() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] { 42 }); contract.updateMyAsset(ctx, "10001", "updates"); String json = "{\"value\":\"updates\"}"; verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void updateMissing() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.updateMyAsset(ctx, "10001", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } } @Test public void assetDelete() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.deleteMyAsset(ctx, "10001"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } } ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/.fabricignore ================================================ # # SPDX-License-Identifier: Apache-2.0 # /.classpath /.git/ /.gradle/ /.project /.settings/ /bin/ /build/ ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/.gitignore ================================================ # # SPDX-License-Identifier: Apache-2.0 # /.classpath /.gradle/ /.project /.settings/ /bin/ /build/ ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/build.gradle.kts ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { id("com.gradleup.shadow") version "9.3.1" id("org.jetbrains.kotlin.jvm") version "2.2.21" } version = "0.0.1" dependencies { implementation("org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.8") implementation("org.json:json:20250517") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testImplementation("org.junit.jupiter:junit-jupiter:6.0.1") testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") } repositories { mavenCentral() maven { setUrl("https://jitpack.io") } } tasks { "shadowJar"(ShadowJar::class) { archiveBaseName = "chaincode" archiveVersion = "" archiveClassifier = "" duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes(mapOf("Main-Class" to "org.hyperledger.fabric.contract.ContractRouter")) } } } tasks.withType { useJUnitPlatform() testLogging { events("passed", "skipped", "failed") } } ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/settings.gradle.kts ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ rootProject.name = "gradle-kotlin" ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/src/main/kotlin/org/example/MyAsset.kt ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example import org.hyperledger.fabric.contract.annotation.DataType import org.hyperledger.fabric.contract.annotation.Property import org.json.JSONObject @DataType class MyAsset(@Property() var value: String?) { fun toJSONString(): String { return JSONObject(this).toString() } companion object { fun fromJSONString(json: String): MyAsset { val value = JSONObject(json).getString("value") return MyAsset(value) } } } ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/src/main/kotlin/org/example/MyAssetContract.kt ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example import org.hyperledger.fabric.contract.Context import org.hyperledger.fabric.contract.ContractInterface import org.hyperledger.fabric.contract.annotation.Contact import org.hyperledger.fabric.contract.annotation.Contract import org.hyperledger.fabric.contract.annotation.Default import org.hyperledger.fabric.contract.annotation.Info import org.hyperledger.fabric.contract.annotation.License import org.hyperledger.fabric.contract.annotation.Transaction @Contract(name = "MyAssetContract", info = Info(title = "MyAsset contract", description = "Kotlin gradle dsl and Kotlin Contract", version = "0.0.1", license = License(name = "Apache-2.0", url = ""), contact = Contact(email = "gradle-kotlin@example.com", name = "gradle-kotlin", url = "http://gradle-kotlin.me"))) @Default class MyAssetContract : ContractInterface { @Transaction fun myAssetExists(ctx: Context, myAssetId: String): Boolean { val buffer = ctx.stub.getState(myAssetId) return (buffer != null && buffer.size > 0) } @Transaction fun createMyAsset(ctx: Context, myAssetId: String, value: String) { val exists = myAssetExists(ctx, myAssetId) if (exists) { throw RuntimeException("The my asset $myAssetId already exists") } val asset = MyAsset(value) ctx.stub.putState(myAssetId, asset.toJSONString().toByteArray(Charsets.UTF_8)) } @Transaction fun readMyAsset(ctx: Context, myAssetId: String): MyAsset { val exists = myAssetExists(ctx, myAssetId) if (!exists) { throw RuntimeException("The my asset $myAssetId does not exist") } return MyAsset.fromJSONString(ctx.stub.getState(myAssetId).toString(Charsets.UTF_8)) } @Transaction fun updateMyAsset(ctx: Context, myAssetId: String, newValue: String) { val asset = readMyAsset(ctx, myAssetId) asset.value = newValue ctx.stub.putState(myAssetId, asset.toJSONString().toByteArray(Charsets.UTF_8)) } @Transaction fun deleteMyAsset(ctx: Context, myAssetId: String) { val exists = myAssetExists(ctx, myAssetId) if (!exists) { throw RuntimeException("The my asset $myAssetId does not exist") } ctx.stub.delState(myAssetId) } } ================================================ FILE: examples/fabric-contract-example-gradle-kotlin/src/test/kotlin/org/example/MyAssetContractTest.kt ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* import com.nhaarman.mockitokotlin2.* import java.util.ArrayList import org.hyperledger.fabric.contract.Context import org.hyperledger.fabric.shim.ChaincodeStub import org.hyperledger.fabric.shim.Chaincode.Response import org.hyperledger.fabric.shim.Chaincode.Response.Status class MyAssetContractTest { lateinit var ctx: Context lateinit var stub: ChaincodeStub @BeforeEach fun beforeEach() { ctx = mock() stub = mock() whenever(ctx.stub).thenReturn(stub) whenever(stub.getState("1001")).thenReturn("{\"value\":\"my asset 1001 value\"}".toByteArray(Charsets.UTF_8)) whenever(stub.getState("1002")).thenReturn("{\"value\":\"my asset 1002 value\"}".toByteArray(Charsets.UTF_8)) } @Nested inner class myAssetExists { @Test fun `should return true for a my asset`() { val contract = MyAssetContract() val result = contract.myAssetExists(ctx, "1001") assertTrue(result) } @Test fun `should return false for a my asset that does not exist (no key)`() { val contract = MyAssetContract() val result = contract.myAssetExists(ctx, "1003") assertFalse(result) } @Test fun `should return false for a my asset that does not exist (no data)`() { val contract = MyAssetContract() whenever(stub.getState("1003")).thenReturn(ByteArray(0)) val result = contract.myAssetExists(ctx, "1003") assertFalse(result) } } @Nested inner class createMyAsset { @Test fun `should create a my asset`() { val contract = MyAssetContract() contract.createMyAsset(ctx, "1003", "my asset 1003 value") verify(stub, times(1)).putState("1003", "{\"value\":\"my asset 1003 value\"}".toByteArray(Charsets.UTF_8)) } @Test fun `should throw an error for a my asset that already exists`() { val contract = MyAssetContract() val e = assertThrows(RuntimeException::class.java) { contract.createMyAsset(ctx, "1001", "my asset 1001 value") } assertEquals(e.message, "The my asset 1001 already exists") } } @Nested inner class readMyAsset { @Test fun `should return a my asset`() { val contract = MyAssetContract() val asset = contract.readMyAsset(ctx, "1001") assertEquals("my asset 1001 value", asset.value) } @Test fun `should throw an error for a my asset that does not exist`() { val contract = MyAssetContract() val e = assertThrows(RuntimeException::class.java) { contract.readMyAsset(ctx, "1003") } assertEquals(e.message, "The my asset 1003 does not exist") } } @Nested inner class updateMyAsset { @Test fun `should update a my asset`() { val contract = MyAssetContract() contract.updateMyAsset(ctx, "1001", "my asset 1001 new value") verify(stub, times(1)).putState("1001", "{\"value\":\"my asset 1001 new value\"}".toByteArray(Charsets.UTF_8)) } @Test fun `should throw an error for a my asset that does not exist`() { val contract = MyAssetContract() val e = assertThrows(RuntimeException::class.java) { contract.updateMyAsset(ctx, "1003", "my asset 1003 new value") } assertEquals(e.message, "The my asset 1003 does not exist") } } @Nested inner class deleteMyAsset { @Test fun `should delete a my asset`() { val contract = MyAssetContract() contract.deleteMyAsset(ctx, "1001") verify(stub, times(1)).delState("1001") } @Test fun `should throw an error for a my asset that does not exist`() { val contract = MyAssetContract() val e = assertThrows(RuntimeException::class.java) { contract.deleteMyAsset(ctx, "1003") } assertEquals(e.message, "The my asset 1003 does not exist") } } } ================================================ FILE: examples/fabric-contract-example-maven/.gitignore ================================================ target ================================================ FILE: examples/fabric-contract-example-maven/pom.xml ================================================ 4.0.0 MyAssetContract MyAssetContract 1.0-SNAPSHOT 21 UTF-8 UTF-8 2.5.8 1.5.32 2.0.17 jitpack.io https://www.jitpack.io org.junit junit-bom 6.0.3 pom import org.hyperledger.fabric-chaincode-java fabric-chaincode-shim ${fabric-chaincode-java.version} compile org.hyperledger.fabric fabric-protos 0.3.7 compile org.slf4j slf4j-api ${slf4j.version} compile ch.qos.logback logback-classic ${logback.version} runtime org.junit.jupiter junit-jupiter-api compile org.junit.jupiter junit-jupiter-params test org.junit.jupiter junit-jupiter-engine test org.mockito mockito-core 5.23.0 org.json json 20251224 src maven-surefire-plugin 3.5.5 maven-compiler-plugin 3.15.0 ${java.version} org.apache.maven.plugins maven-shade-plugin 3.6.2 package shade chaincode org.hyperledger.fabric.contract.ContractRouter *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA ================================================ FILE: examples/fabric-contract-example-maven/src/main/java/org/example/MyAsset.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONObject; @DataType() public class MyAsset { @Property() private String value; public MyAsset(){ } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String toJSONString() { return new JSONObject(this).toString(); } public static MyAsset fromJSONString(String json) { String value = new JSONObject(json).getString("value"); MyAsset asset = new MyAsset(); asset.setValue(value); return asset; } } ================================================ FILE: examples/fabric-contract-example-maven/src/main/java/org/example/MyAssetContract.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import static java.nio.charset.StandardCharsets.UTF_8; @Contract(name = "MyAssetContract", info = @Info(title = "MyAsset contract", description = "Very basic Java Contract example", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "MyAssetContract@example.com", name = "MyAssetContract", url = "http://MyAssetContract.me"))) @Default public class MyAssetContract implements ContractInterface { public MyAssetContract() { } @Transaction() public boolean myAssetExists(Context ctx, String myAssetId) { byte[] buffer = ctx.getStub().getState(myAssetId); return (buffer != null && buffer.length > 0); } @Transaction() public void createMyAsset(Context ctx, String myAssetId, String value) { boolean exists = myAssetExists(ctx,myAssetId); if (exists) { throw new RuntimeException("The asset "+myAssetId+" already exists"); } MyAsset asset = new MyAsset(); asset.setValue(value); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public MyAsset readMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } MyAsset newAsset = MyAsset.fromJSONString(new String(ctx.getStub().getState(myAssetId),UTF_8)); return newAsset; } @Transaction() public void updateMyAsset(Context ctx, String myAssetId, String newValue) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } MyAsset asset = new MyAsset(); asset.setValue(newValue); ctx.getStub().putState(myAssetId, asset.toJSONString().getBytes(UTF_8)); } @Transaction() public void deleteMyAsset(Context ctx, String myAssetId) { boolean exists = myAssetExists(ctx,myAssetId); if (!exists) { throw new RuntimeException("The asset "+myAssetId+" does not exist"); } ctx.getStub().delState(myAssetId); } } ================================================ FILE: examples/fabric-contract-example-maven/src/test/java/org/example/MyAssetContractTest.java ================================================ /* * SPDX-License-Identifier: Apache License 2.0 */ package org.example; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public final class MyAssetContractTest { @Nested class AssetExists { @Test public void noProperAsset() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] {}); boolean result = contract.myAssetExists(ctx,"10001"); assertFalse(result); } @Test public void assetExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] {42}); boolean result = contract.myAssetExists(ctx,"10001"); assertTrue(result); } @Test public void noKey() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(null); boolean result = contract.myAssetExists(ctx,"10002"); assertFalse(result); } } @Nested class AssetCreates { @Test public void newAssetCreate() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); String json = "{\"value\":\"TheAsset\"}"; contract.createMyAsset(ctx, "10001", "TheAsset"); verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void alreadyExists() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10002")).thenReturn(new byte[] { 42 }); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.createMyAsset(ctx, "10002", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10002 already exists"); } } @Test public void assetRead() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); MyAsset asset = new MyAsset(); asset.setValue("Valuable"); String json = asset.toJSONString(); when(stub.getState("10001")).thenReturn(json.getBytes(StandardCharsets.UTF_8)); MyAsset returnedAsset = contract.readMyAsset(ctx, "10001"); assertEquals(returnedAsset.getValue(), asset.getValue()); } @Nested class AssetUpdates { @Test public void updateExisting() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(new byte[] { 42 }); contract.updateMyAsset(ctx, "10001", "updates"); String json = "{\"value\":\"updates\"}"; verify(stub).putState("10001", json.getBytes(UTF_8)); } @Test public void updateMissing() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.updateMyAsset(ctx, "10001", "TheAsset"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } } @Test public void assetDelete() { MyAssetContract contract = new MyAssetContract(); Context ctx = mock(Context.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(ctx.getStub()).thenReturn(stub); when(stub.getState("10001")).thenReturn(null); Exception thrown = assertThrows(RuntimeException.class, () -> { contract.deleteMyAsset(ctx, "10001"); }); assertEquals(thrown.getMessage(), "The asset 10001 does not exist"); } } ================================================ FILE: examples/ledger-api/.gitignore ================================================ .gradle/ build/ bin/ ================================================ FILE: examples/ledger-api/README.md ================================================ This example needs to use gradle4.6 please install this first eg using sdkman `sdk install gradle 4.6` and then add the wrapper code before building `gradle wrapper` ================================================ FILE: examples/ledger-api/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } version '0.0.1' repositories { mavenCentral() maven { url "https://www.jitpack.io" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.8' implementation 'org.json:json:20251224' testImplementation platform('org.junit:junit-bom:6.0.2') testImplementation 'org.junit.jupiter:junit-jupiter' testImplementation 'org.assertj:assertj-core:3.27.7' testImplementation 'org.mockito:mockito-core:5.21.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } } compileJava { options.release.set(11) options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-parameters" } test { useJUnitPlatform() testLogging { events "PASSED", "SKIPPED", "FAILED" } } ================================================ FILE: examples/ledger-api/src/main/java/org/example/LedgerAPIContract.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import static java.nio.charset.StandardCharsets.UTF_8; @Contract(name = "LedgerAPI", info = @Info(title = "Ledger API Examples", description = "Technical Details and use of the Ledger API", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "ledgerapi@example.com", name = "ledgerapi", url = "http://ledgerapi.me"))) @Default public class LedgerAPIContract implements ContractInterface { public LedgerAPIContract() { } /** * Example showing getting the world state ledger. */ @Transaction() public boolean assetExistsInWorldState(Context ctx, String myAssetId) { Collection collection = Ledger.getLedger(ctx).getCollection(Collection.WORLD); // check exists return false; } public final static String PRIVATE_RECORDS = "PrivateRecords"; /** * Example showing getting the a named private data collectionm. */ @Transaction() public boolean assetExistsInPrivateData(Context ctx, String myAssetId) { Collection collection = Ledger.getLedger(ctx).getCollection(LedgerAPIContract.PRIVATE_RECORDS); // check exists return false; } /** * Exanmple showing getting one of the organizational collections */ @Transaction() public boolean assetExistsInOrganizationPrivateData(Context ctx, String myAssetId) { Collection collection = Ledger.getLedger(ctx).getCollection(Collection.organizationCollection("myMspId")); // check exists return false; } } ================================================ FILE: examples/ledger-api/src/main/java/org/example/MyAsset.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.example; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONObject; @DataType() public class MyAsset { @Property() private String value; public MyAsset(){ } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String toJSONString() { return new JSONObject(this).toString(); } public static MyAsset fromJSONString(String json) { String value = new JSONObject(json).getString("value"); MyAsset asset = new MyAsset(); asset.setValue(value); return asset; } } ================================================ FILE: fabric-chaincode-docker/.gitignore ================================================ /bin/ ================================================ FILE: fabric-chaincode-docker/Dockerfile ================================================ ARG JAVA_IMAGE=eclipse-temurin:25-jdk FROM ${JAVA_IMAGE} AS builder RUN apt-get update \ && apt-get install -y curl zip unzip RUN curl -s "https://get.sdkman.io" | bash SHELL ["/bin/bash", "-c"] RUN . /root/.sdkman/bin/sdkman-init.sh \ && sdk install gradle 9.4.1 \ && sdk install maven 3.9.15 FROM ${JAVA_IMAGE} AS dependencies COPY --from=builder /root/.sdkman/candidates/gradle/current /opt/gradle COPY --from=builder /root/.sdkman/candidates/maven/current /opt/maven SHELL ["/bin/bash", "-c"] ENV PATH="/opt/maven/bin:/opt/gradle/bin:${PATH}" # Coping libs, scripts and sources COPY build/distributions/ /root/ #Creating folders structure RUN mkdir -p \ /root/chaincode-java/chaincode/src \ /root/chaincode-java/chaincode/build/out \ /root/chaincode-java/shim-src/fabric-chaincode-integration-test \ /root/chaincode-java/shim-src/fabric-chaincode-docker #Making scripts runnable RUN chmod +x /root/chaincode-java/start /root/chaincode-java/build.sh # Build protos and shim jar and installing them to maven local and gradle cache WORKDIR /root/chaincode-java/shim-src RUN gradle \ clean \ fabric-chaincode-shim:build \ fabric-chaincode-shim:publishToMavenLocal \ -x javadoc \ -x test \ -x pmdMain \ -x pmdTest \ -x spotlessCheck WORKDIR /root/chaincode-java # Run the Gradle and Maven commands to generate the wrapper variants # of each tool # Gradle doesn't run without settings.gradle file, so create one RUN touch settings.gradle \ && gradle wrapper \ && ./gradlew wrapper \ && mvn -N wrapper:wrapper # Creating final javaenv image which will include all required # dependencies to build and compile java chaincode FROM ${JAVA_IMAGE} RUN apt-get update \ && apt-get install -y zip unzip \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /chaincode/input \ && mkdir -p /chaincode/output SHELL ["/bin/bash", "-c"] # Copy setup scripts, and the cached dependencies COPY --from=dependencies /root/chaincode-java /root/chaincode-java COPY --from=dependencies /root/.gradle /root/.gradle COPY --from=dependencies /root/.m2 /root/.m2 WORKDIR /root/chaincode-java ================================================ FILE: fabric-chaincode-docker/README.md ================================================ # Quick reference - **Maintained by**: [The Fabric Java chaincode maintainers](https://github.com/hyperledger/fabric-chaincode-java) # Overview This image is used by the default [Hyperledger Fabric](https://hyperledger-fabric.readthedocs.io/) chaincode builder when deploying Java smart contracts. It is not intended for use independently of Hyperledger Fabric. # Image variants Detailed information on image tags, interoperability, and supported Java versions can be found in the [compatibility](https://github.com/hyperledger/fabric-chaincode-java/blob/main/COMPATIBILITY.md) documentation. # License This image is provided under the [Apache-2.0](https://github.com/hyperledger/fabric-chaincode-java/blob/main/LICENSE) license. ================================================ FILE: fabric-chaincode-docker/build.gradle ================================================ /* * Copyright IBM Corp. 2018 All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ plugins { id 'com.bmuschko.docker-remote-api' version '10.0.0' } repositories { mavenCentral() } import com.bmuschko.gradle.docker.tasks.image.* tasks.register('copyLib', Copy) { dependsOn ':fabric-chaincode-shim:build' from project(':fabric-chaincode-shim').configurations.runtimeClasspath into('build/distributions/chaincode-java/lib') } tasks.register('copyShimJar', Copy) { dependsOn copyLib from project(':fabric-chaincode-shim').jar into('build/distributions/chaincode-java/lib') } tasks.register('copyStartScript', Copy) { dependsOn copyShimJar from('start') into('build/distributions/chaincode-java') } tasks.register('copyBuildScript', Copy) { dependsOn copyStartScript from('build.sh') into('build/distributions/chaincode-java') } tasks.register('copyAllDeps', Copy) { dependsOn copyBuildScript copy { from project(':fabric-chaincode-shim').getProjectDir() into('build/distributions/chaincode-java/shim-src/fabric-chaincode-shim/') } copy { from project.getParent().file("build.gradle") into('build/distributions/chaincode-java/shim-src/') } copy { from project.getParent().file("settings.gradle") into('build/distributions/chaincode-java/shim-src/') } } tasks.register('buildImage', DockerBuildImage) { dependsOn copyAllDeps inputDir = project.file('Dockerfile').parentFile images = ['hyperledger/fabric-javaenv', 'hyperledger/fabric-javaenv:2.5', 'hyperledger/fabric-javaenv:2.5.9'] } ================================================ FILE: fabric-chaincode-docker/build.sh ================================================ #!/usr/bin/env bash set -ex INPUT_DIR=/chaincode/input OUTPUT_DIR=/chaincode/output TMP_DIR=$(mktemp -d) NUM_JARS=$(find ${INPUT_DIR} -name "*.jar" | wc -l) buildGradle() { echo "Copying from $1 to ${TMP_DIR}" cd $1 tar cf - . | (cd ${TMP_DIR}; tar xf -) cd ${TMP_DIR} echo "Gradle build" if [ -f ./gradlew ]; then chmod +x ./gradlew ./gradlew build shadowJar -x test else /root/chaincode-java/gradlew build shadowJar -x test fi retval=$? if [ $retval -ne 0 ]; then exit $retval fi cp build/libs/chaincode.jar $2 retval=$? if [ $retval -ne 0 ]; then exit $retval fi touch $2/.uberjar cd "$SAVED" >/dev/null } buildMaven() { echo "Copying from $1 to ${TMP_DIR}" cd $1 tar cf - . | (cd ${TMP_DIR}; tar xf -) cd ${TMP_DIR} echo "Maven build" if [ -f ./mvnw ]; then chmod +x ./mvnw else cp -r /root/chaincode-java/.mvn . cp /root/chaincode-java/mvnw . fi ./mvnw compile package -DskipTests -Dmaven.test.skip=true retval=$? if [ $retval -ne 0 ]; then exit $retval fi cp target/chaincode.jar $2 retval=$? if [ $retval -ne 0 ]; then exit $retval fi touch $2/.uberjar cd "$SAVED" >/dev/null } for DIR in ${INPUT_DIR} ${INPUT_DIR}/src; do if [ -f ${DIR}/build.gradle -o -f ${DIR}/build.gradle.kts ]; then buildGradle ${DIR} ${OUTPUT_DIR} exit 0 elif [ -f ${DIR}/pom.xml ]; then buildMaven ${DIR} ${OUTPUT_DIR} exit 0 fi done if [ ${NUM_JARS} -eq 0 ]; then >&2 echo "Not build.gradle nor pom.xml found in chaincode source, don't know how to build chaincode" >&2 echo "Project folder content:" >&2 find ${INPUT_DIR} -name "*" -exec ls -ld '{}' \; exit 255 else cd ${INPUT_DIR} && tar cf - $(find . -name "*.jar") | (cd ${OUTPUT_DIR} && tar xvf -) fi ================================================ FILE: fabric-chaincode-docker/start ================================================ #!/usr/bin/env bash set -ex ROOT_DIR=/root/chaincode-java LIB_DIR=${ROOT_DIR}/lib CHAINCODE_DIR=${ROOT_DIR}/chaincode LIB_JARS=$(find ${LIB_DIR} -name "*.jar" | paste -s -d ":" -) CHAINCODE_JARS=$(find ${CHAINCODE_DIR} -name "*.jar" | paste -s -d ":" -) NUM_CHAINCODE_JARS=$(find ${CHAINCODE_DIR} -name "*.jar" | wc -l) JAVA_OPTS="-XshowSettings" if [ -f ${CHAINCODE_DIR}/.uberjar ]; then if [ ${NUM_CHAINCODE_JARS} -ne 1 ]; then >&2 echo "Cannot start uber JAR as more than one JAR file was found in the chaincode directory" exit 255 fi exec java ${JAVA_OPTS} -jar ${CHAINCODE_JARS} "$@" else exec java ${JAVA_OPTS} -cp ${CHAINCODE_JARS}:${LIB_JARS} org.hyperledger.fabric.contract.ContractRouter "$@" fi ================================================ FILE: fabric-chaincode-integration-test/.gitignore ================================================ repository _cfg *.tar.gz log.txt ================================================ FILE: fabric-chaincode-integration-test/build.gradle ================================================ dependencies { implementation project(':fabric-chaincode-docker') implementation project(':fabric-chaincode-shim') implementation 'org.json:json:20251224' } test { // Always run tests, even when nothing changed. dependsOn 'cleanTest' // Show test results. testLogging { events "passed", "skipped", "failed" showExceptions = true showCauses = true showStandardStreams = true exceptionFormat = "full" } } tasks.register('getLatestDockerImages') { doLast { providers.exec { workingDir "." commandLine "sh", "-c", "./getDockerImages.sh" } } } ================================================ FILE: fabric-chaincode-integration-test/chaincodebootstrap.gradle ================================================ allprojects { apply plugin: 'maven-publish' publishing { repositories { maven { name = 'fabric' url = "file:$chaincodeRepoDir" } } } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-gradle/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } group 'org.hyperledger.fabric-chaincode-java' version '1.0-SNAPSHOT' compileJava { options.release = 11 } repositories { mavenCentral() maven { url "$projectDir/repository" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.9' implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-gradle/src/main/java/org/hyperledger/fabric/example/BareGradle.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; @Contract(name = "BareGradle", info = @Info(title = "BareGradle contract", description = "Contract but using all the APIs", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "fred@example.com", name = "fred", url = "http://fred.example.com"))) @Default public class BareGradle implements ContractInterface { public BareGradle() { } @Transaction() public String whoami(Context ctx){ return this.getClass().getSimpleName(); } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-gradle/src/main/resources/config.props ================================================ MAX_INBOUND_MESSAGE_SIZE=4000 CHAINCODE_METRICS_ENABLED=true TP_CORE_POOL_SIZE=4 TP_MAX_POOL_SIZE=4 TP_QUEUE_SIZE=4000 ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-maven/.gitignore ================================================ target ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-maven/pom.xml ================================================ 4.0.0 MyAssetContract MyAssetContract 1.0-SNAPSHOT 11 UTF-8 UTF-8 2.5.9 jitpack.io https://www.jitpack.io localfabirc file://${project.basedir}/repository org.hyperledger.fabric-chaincode-java fabric-chaincode-shim ${fabric-chaincode-java.version} compile src maven-compiler-plugin 3.15.0 ${java.version} org.apache.maven.plugins maven-shade-plugin 3.6.2 package shade chaincode org.hyperledger.fabric.contract.ContractRouter *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-maven/src/main/java/org/hyperledger/fabric/example/BareMaven.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; @Contract(name = "BareMaven", info = @Info(title = "BareGradle contract", description = "Contract but using all the APIs", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "fred@example.com", name = "fred", url = "http://fred.example.com"))) @Default public class BareMaven implements ContractInterface { public BareMaven() { } @Transaction() public String whoami(Context ctx){ return this.getClass().getSimpleName(); } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/bare-maven/src/main/resources/config.props ================================================ MAX_INBOUND_MESSAGE_SIZE=4000 CHAINCODE_METRICS_ENABLED=true TP_CORE_POOL_SIZE=4 TP_MAX_POOL_SIZE=4 TP_QUEUE_SIZE=4000 ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } group 'org.hyperledger.fabric-chaincode-java' version '' compileJava { options.release = 11 } repositories { mavenCentral() maven { url "$projectDir/repository" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.9' implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s ' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/settings.gradle ================================================ rootProject.name = 'fabric-chaincode-example-sacc' ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/src/main/java/org/hyperledger/fabric/example/AllLedgerAPI.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.ledger.Ledger; @Contract(name = "AllLedgerAPI", info = @Info(title = "AllLedgerAPI contract", description = "Contract but using all the Ledger APIs", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "fred@example.com", name = "fred", url = "http://fred.example.com"))) @Default public class AllLedgerAPI implements ContractInterface { public AllLedgerAPI() { } @Transaction() public void accessLedgers(Context ctx){ Ledger ledger = Ledger.getLedger(ctx); // not much else can be done for the moment } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/build.gradle ================================================ plugins { id 'com.gradleup.shadow' version '9.3.1' id 'java' } group 'org.hyperledger.fabric-chaincode-java' version '1.0-SNAPSHOT' compileJava { options.release = 11 } repositories { mavenCentral() maven { url "$projectDir/repository" } } dependencies { implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.5.9' implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' implementation 'commons-logging:commons-logging:1.3.5' implementation 'com.google.code.gson:gson:2.13.2' } shadowJar { archiveBaseName = 'chaincode' archiveVersion = '' archiveClassifier = '' duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() manifest { attributes 'Main-Class': 'org.hyperledger.fabric.contract.ContractRouter' } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH= @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/settings.gradle ================================================ rootProject.name = 'fabric-chaincode-example-sacc' ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/src/main/java/org/hyperledger/fabric/example/AllAPI.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.metrics.MetricsProvider; import org.hyperledger.fabric.shim.ledger.*; import org.hyperledger.fabric.shim.*; import static java.nio.charset.StandardCharsets.UTF_8; @Contract(name = "AllAPI", info = @Info(title = "AllAPI contract", description = "Contract but using all the APIs", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "fred@example.com", name = "fred", url = "http://fred.example.com"))) @Default public class AllAPI implements ContractInterface { public AllAPI() { } @Transaction() public void putBulkStates(Context ctx){ for (int x=100; x<200; x++){ String key = "key"+x; String value = "value:"+x; putState(ctx,key,value); } } @Transaction() public void putState(Context ctx, String key, String payload){ ChaincodeStub stub = ctx.getStub(); stub.putState(key,payload.getBytes(UTF_8)); } @Transaction() public void putStateComposite(Context ctx, String key[], String payload){ String composite = new CompositeKey("composite",key).toString(); this.putState(ctx,composite,payload); } @Transaction() public void getState(Context ctx, String key, String payload){ ChaincodeStub stub = ctx.getStub(); String result = stub.getStringState(key); if (!result.equals(payload)){ String msg = "GetState::["+key+"] Expected "+payload+" got "+result; System.out.println(msg); throw new RuntimeException(msg); } } @Transaction() public int getByRange(Context ctx, String start, String end){ ChaincodeStub stub = ctx.getStub(); System.out.println("getByRange>>"); QueryResultsIterator qri = stub.getStateByRange(start,end); int count=0; for (KeyValue kv : qri){ kv.getKey(); kv.getStringValue(); count++; System.out.println("["+kv.getKey()+"] "+kv.getStringValue()); } System.out.println("getByRange<<"); return count; } @Transaction() public String getByRangePaged(Context ctx, String start, String end, int pageSize, String bookmark){ ChaincodeStub stub = ctx.getStub(); System.out.println("getByRangePaged>>"); QueryResultsIteratorWithMetadata qri = stub.getStateByRangeWithPagination(start,end,pageSize,bookmark); for (KeyValue kv : qri){ kv.getKey(); kv.getStringValue(); System.out.println("["+kv.getKey()+"] "+kv.getStringValue()); } String newbookmark = qri.getMetadata().getBookmark(); int records = qri.getMetadata().getFetchedRecordsCount(); System.out.println(newbookmark+" @ "+records); System.out.println("getByRangePaged<<"); return newbookmark; } @Transaction() public String getMetricsProviderName(Context ctx){ final MetricsProvider provider = Metrics.getProvider(); final String name = provider.getClass().getName(); return name; } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/src/main/java/org/hyperledger/fabric/example/EndorsementCC.java ================================================ package org.hyperledger.fabric.example; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; import org.hyperledger.fabric.shim.ext.sbe.impl.StateBasedEndorsementFactory; import com.google.gson.JsonArray; @Contract() public class EndorsementCC implements ContractInterface { private static Log _logger = LogFactory.getLog(EndorsementCC.class); public void setup(Context ctx) { _logger.info("Init java EndorsementCC"); ctx.getStub().putStringState("pub", "foo"); _logger.info("Init done"); } @Transaction() public void addorgs(Context ctx, String type, String orgs) { _logger.info("Invoking addOrgs"); byte[] epBytes; if ("pub".equals(type)) { epBytes = ctx.getStub().getStateValidationParameter("pub"); } else if ("priv".equals(type)) { epBytes = ctx.getStub().getPrivateDataValidationParameter("col", "priv"); } else { throw new RuntimeException("Unknown key specified"); } StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(epBytes); ep.addOrgs(StateBasedEndorsement.RoleType.RoleTypePeer, orgs); epBytes = ep.policy(); if ("pub".equals(type)) { ctx.getStub().setStateValidationParameter("pub", epBytes); } else { ctx.getStub().setPrivateDataValidationParameter("col", "priv", epBytes); } } @Transaction() public void delorgs(Context ctx, String type, String orgs) { _logger.info("Invoking delOrgs"); byte[] epBytes; if ("pub".equals(type)) { epBytes = ctx.getStub().getStateValidationParameter("pub"); } else if ("priv".equals(type)) { epBytes = ctx.getStub().getPrivateDataValidationParameter("col", "priv"); } else { throw new RuntimeException("Unknown key specified"); } StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(epBytes); // delete organizations from the endorsement policy of that key ep.delOrgs(orgs); epBytes = ep.policy(); if ("pub".equals(type)) { ctx.getStub().setStateValidationParameter("pub", epBytes); } else { ctx.getStub().setPrivateDataValidationParameter("col", "priv", epBytes); } ctx.getStub().setStateValidationParameter("endorsed_state", epBytes); } @Transaction() public String listorgs(Context ctx, String type) { _logger.info("Invoking listOrgs"); byte[] epBytes; if ("pub".equals(type)) { epBytes = ctx.getStub().getStateValidationParameter("pub"); } else if ("priv".equals(type)) { epBytes = ctx.getStub().getPrivateDataValidationParameter("col", "priv"); } else { throw new RuntimeException("Unknown key specified"); } StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(epBytes); List orgs = ep.listOrgs(); JsonArray orgsList = new JsonArray(); orgs.forEach(org -> orgsList.add(org)); String s = orgsList.toString(); _logger.info("orgsList "+s); return orgsList.toString(); } @Transaction() public void delEP(Context ctx, String type) { _logger.info("Invoking delEP"); if ("pub".equals(type)) { ctx.getStub().setStateValidationParameter("pub", null); } else if ("priv".equals(type)) { ctx.getStub().setPrivateDataValidationParameter("col", "priv", null); } else { throw new RuntimeException("Unknown key specified"); } } @Transaction() public void setval(Context ctx, String type, String value) { _logger.info("Invoking setVal"); if ("pub".equals(type)) { ctx.getStub().putStringState("pub", value); } else if ("priv".equals(type)) { ctx.getStub().putPrivateData("col", "priv",value); } else { throw new RuntimeException("Unknown key specified"); } } @Transaction() public String getval(Context ctx, String type) { _logger.info("Invoking getVal"); byte[] buffer; if ("pub".equals(type)) { buffer = (ctx.getStub().getState("pub")); } else if ("priv".equals(type)) { buffer = (ctx.getStub().getPrivateData("col", "priv")); } else { throw new RuntimeException("Unknown key specified"); } String value = new String(buffer,UTF_8); return value; } @Transaction() public void deleteval(Context ctx, String type) { _logger.info("Invoking deleteval"); if ("pub".equals(type)) { ctx.getStub().delState("pub"); } else if ("priv".equals(type)) { ctx.getStub().delPrivateData("col", "priv"); } else { throw new RuntimeException("Unknown key specified"); } } @Transaction(intent = Transaction.TYPE.EVALUATE) public boolean recordExists(Context ctx, String type) { _logger.info("Invoking recordExists"); if ("pub".equals(type)) { byte[] buffer = ctx.getStub().getState("pub"); return (buffer != null && buffer.length > 0); } else if ("priv".equals(type)) { byte[] buffer = ctx.getStub().getPrivateDataHash("col", "priv"); return (buffer != null && buffer.length > 0); } else { throw new RuntimeException("Unknown key specified"); } } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/fabric-shim-api/src/main/resources/config.props ================================================ MAX_INBOUND_MESSAGE_SIZE=4000 CHAINCODE_METRICS_ENABLED=true TP_CORE_POOL_SIZE=4 TP_MAX_POOL_SIZE=4 TP_QUEUE_SIZE=4000 ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/.gitignore ================================================ target ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/.mvn/wrapper/maven-wrapper.properties ================================================ wrapperVersion=3.3.4 distributionType=bin distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Apache Maven Wrapper startup batch script, version 3.3.4 # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ]; then if [ -f /usr/local/etc/mavenrc ]; then . /usr/local/etc/mavenrc fi if [ -f /etc/mavenrc ]; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ]; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false darwin=false mingw=false case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true ;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then JAVA_HOME="$(/usr/libexec/java_home)" export JAVA_HOME else JAVA_HOME="/Library/Java/Home" export JAVA_HOME fi fi ;; esac if [ -z "$JAVA_HOME" ]; then if [ -r /etc/gentoo-release ]; then JAVA_HOME=$(java-config --jre-home) fi fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin; then [ -n "$JAVA_HOME" ] \ && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") [ -n "$CLASSPATH" ] \ && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw; then [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ && JAVA_HOME="$( cd "$JAVA_HOME" || ( echo "cannot cd into $JAVA_HOME." >&2 exit 1 ) pwd )" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="$(which javac)" if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=$(which readlink) if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then if $darwin; then javaHome="$(dirname "$javaExecutable")" javaExecutable="$(cd "$javaHome" && pwd -P)/javac" else javaExecutable="$(readlink -f "$javaExecutable")" fi javaHome="$(dirname "$javaExecutable")" javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ]; then if [ -n "$JAVA_HOME" ]; then if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="$( \unset -f command 2>/dev/null \command -v java )" fi fi if [ ! -x "$JAVACMD" ]; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ]; then echo "Warning: JAVA_HOME environment variable is not set." >&2 fi # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ]; then echo "Path not specified to find_maven_basedir" >&2 return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ]; do if [ -d "$wdir"/.mvn ]; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=$( cd "$wdir/.." || exit 1 pwd ) fi # end of workaround done printf '%s' "$( cd "$basedir" || exit 1 pwd )" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then # Remove \r in case we run on Windows within Git Bash # and check out the repository with auto CRLF management # enabled. Otherwise, we may read lines that are delimited with # \r\n and produce $'-Xarg\r' rather than -Xarg due to word # splitting rules. tr -s '\r\n' ' ' <"$1" fi } log() { if [ "$MVNW_VERBOSE" = true ]; then printf '%s\n' "$1" fi } BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then exit 1 fi MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} export MAVEN_PROJECTBASEDIR log "$MAVEN_PROJECTBASEDIR" trim() { # MWRAPPER-139: # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. # Needed for removing poorly interpreted newline sequences when running in more # exotic environments such as mingw bash on Windows. printf "%s" "${1}" | tr -d '[:space:]' } ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" if [ -r "$wrapperJarPath" ]; then log "Found $wrapperJarPath" else log "Couldn't find $wrapperJarPath, downloading it ..." if [ -n "$MVNW_REPOURL" ]; then wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" else wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" fi while IFS="=" read -r key value; do case "$key" in wrapperUrl) wrapperUrl=$(trim "${value-}") break ;; esac done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" log "Downloading from: $wrapperUrl" if $cygwin; then wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") fi if command -v wget >/dev/null; then log "Found wget ... using wget" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then wget ${QUIET:+"$QUIET"} "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else wget ${QUIET:+"$QUIET"} --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi elif command -v curl >/dev/null; then log "Found curl ... using curl" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then curl ${QUIET:+"$QUIET"} -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" else curl ${QUIET:+"$QUIET"} --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi else log "Falling back to using Java to download" javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then javaSource=$(cygpath --path --windows "$javaSource") javaClass=$(cygpath --path --windows "$javaClass") fi if [ -e "$javaSource" ]; then if [ ! -e "$javaClass" ]; then log " - Compiling MavenWrapperDownloader.java ..." ("$JAVA_HOME/bin/javac" "$javaSource") fi if [ -e "$javaClass" ]; then log " - Running MavenWrapperDownloader.java ..." ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" fi fi fi fi ########################################################################################## # End of extension ########################################################################################## # If specified, validate the SHA-256 sum of the Maven wrapper jar file wrapperSha256Sum="" while IFS="=" read -r key value; do case "$key" in wrapperSha256Sum) wrapperSha256Sum=$(trim "${value-}") break ;; esac done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" if [ -n "$wrapperSha256Sum" ]; then wrapperSha256Result=false if command -v sha256sum >/dev/null; then if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c - >/dev/null 2>&1; then wrapperSha256Result=true fi elif command -v shasum >/dev/null; then if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then wrapperSha256Result=true fi else echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 exit 1 fi if [ $wrapperSha256Result = false ]; then echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 exit 1 fi fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$JAVA_HOME" ] \ && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") [ -n "$CLASSPATH" ] \ && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") [ -n "$MAVEN_PROJECTBASEDIR" ] \ && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain # shellcheck disable=SC2086 # safe args exec "$JAVACMD" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. >&2 echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. >&2 goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. >&2 echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. >&2 goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( if "%MVNW_VERBOSE%" == "true" ( echo Found %WRAPPER_JAR% ) ) else ( if not "%MVNW_REPOURL%" == "" ( SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %WRAPPER_URL% ) powershell -Command "&{"^ "$webclient = new-object System.Net.WebClient;"^ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% ) ) @REM End of extension @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file SET WRAPPER_SHA_256_SUM="" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B ) IF NOT %WRAPPER_SHA_256_SUM%=="" ( powershell -Command "&{"^ "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ " exit 1;"^ "}"^ "}" if ERRORLEVEL 1 goto error ) @REM Provide a "standardized" way to retrieve the CLI args that will @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* %MAVEN_JAVA_EXE% ^ %JVM_CONFIG_MAVEN_PROPS% ^ %MAVEN_OPTS% ^ %MAVEN_DEBUG_OPTS% ^ -classpath %WRAPPER_JAR% ^ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%"=="on" pause if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% cmd /C exit /B %ERROR_CODE% ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/pom.xml ================================================ 4.0.0 MyAssetContract MyAssetContract 1.0-SNAPSHOT 11 UTF-8 UTF-8 2.5.9 jitpack.io https://www.jitpack.io artifactory https://hyperledger.jfrog.io/hyperledger/fabric-maven localfabric file://${project.basedir}/repository org.hyperledger.fabric-chaincode-java fabric-chaincode-shim ${fabric-chaincode-java.version} compile src maven-compiler-plugin 3.15.0 ${java.version} org.apache.maven.plugins maven-shade-plugin 3.6.2 package shade chaincode org.hyperledger.fabric.contract.ContractRouter *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/src/main/java/org/hyperledger/fabric/example/WrapperMaven.java ================================================ /* * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.example; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; @Contract(name = "WrapperMaven", info = @Info(title = "BareGradle contract", description = "Contract but using all the APIs", version = "0.0.1", license = @License(name = "SPDX-License-Identifier: Apache-2.0", url = ""), contact = @Contact(email = "fred@example.com", name = "fred", url = "http://fred.example.com"))) @Default public class WrapperMaven implements ContractInterface { public WrapperMaven() { } @Transaction() public String whoami(Context ctx){ return this.getClass().getSimpleName(); } } ================================================ FILE: fabric-chaincode-integration-test/src/contracts/wrapper-maven/src/main/resources/config.props ================================================ MAX_INBOUND_MESSAGE_SIZE=4000 CHAINCODE_METRICS_ENABLED=true TP_CORE_POOL_SIZE=4 TP_MAX_POOL_SIZE=4 TP_QUEUE_SIZE=4000 ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/contractinstall/ContractInstallTest.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.contractinstall; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; import org.hyperleder.fabric.shim.integration.util.FabricState; import org.hyperleder.fabric.shim.integration.util.InvokeHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** Basic Java Chaincode Test */ public class ContractInstallTest { @BeforeAll public static void setUp() throws Exception { FabricState.getState().start(); } @Test public void testInstall() { InvokeHelper helper = InvokeHelper.newHelper("baregradlecc", "sachannel"); String text = helper.invoke("org1", "whoami"); assertThat(text, containsString("BareGradle")); helper = InvokeHelper.newHelper("baremaven", "sachannel"); text = helper.invoke("org1", "whoami"); assertThat(text, containsString("BareMaven")); helper = InvokeHelper.newHelper("wrappermaven", "sachannel"); text = helper.invoke("org1", "whoami"); assertThat(text, containsString("WrapperMaven")); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/ledgertests/LedgerIntegrationTest.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.ledgertests; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; import org.hyperleder.fabric.shim.integration.util.FabricState; import org.hyperleder.fabric.shim.integration.util.InvokeHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** Basic Java Chaincode Test */ public class LedgerIntegrationTest { @BeforeAll public static void setUp() throws Exception { FabricState.getState().start(); } @Test public void testLedgers() { InvokeHelper helper = InvokeHelper.newHelper("ledgercc", "sachannel"); String text = helper.invoke("org1", "accessLedgers"); assertThat(text, containsString("success")); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/shimtests/SACCIntegrationTest.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.shimtests; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; import org.hyperleder.fabric.shim.integration.util.FabricState; import org.hyperleder.fabric.shim.integration.util.InvokeHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; /** Basic Java Chaincode Test */ public class SACCIntegrationTest { @BeforeAll public static void setUp() throws Exception { FabricState.getState().start(); } @Test public void testLedger() { InvokeHelper helper = InvokeHelper.newHelper("shimcc", "sachannel"); String text = helper.invoke("org1", "putBulkStates"); assertThat(text, containsString("success")); text = helper.invoke("org1", "getByRange", "key120", "key170"); assertThat(text, containsString("50")); text = helper.invoke("org1", "getByRangePaged", "key120", "key170", "10", ""); System.out.println(text); assertThat(text, containsString("key130")); text = helper.invoke("org1", "getMetricsProviderName"); System.out.println(text); assertThat(text, containsString("org.hyperledger.fabric.metrics.impl.DefaultProvider")); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/shimtests/SBECCIntegrationTest.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.shimtests; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.hamcrest.core.StringContains.containsString; import org.hyperleder.fabric.shim.integration.util.FabricState; import org.hyperleder.fabric.shim.integration.util.InvokeHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SBECCIntegrationTest { @BeforeAll public static void setUp() throws Exception { FabricState.getState().start(); } @Test public void runSBE_pub_setget() { final String mode = "pub"; final InvokeHelper helper = InvokeHelper.newHelper("shimcc", "sachannel"); String text; text = helper.invoke("org1", "EndorsementCC:setval", mode, "foo"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("foo")); text = helper.invoke("org1", "EndorsementCC:addorgs", mode, "org1MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org1MSP")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val1"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val1")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val2"); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val2")); text = helper.invoke("org1", "EndorsementCC:addorgs", mode, "org2MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org2MSP")); assertThat(text, containsString("org1MSP")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val3"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val3")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val4"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val4")); text = helper.invoke("org1", "EndorsementCC:delorgs", mode, "org1MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org2MSP")); assertThat(text, not(containsString("org1MSP"))); text = helper.invoke("org1", "EndorsementCC:deleteval", mode); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:recordExists", mode); assertThat(text, containsString("false")); } @Test public void runSBE_priv() { final String mode = "priv"; final InvokeHelper helper = InvokeHelper.newHelper("shimcc", "sachannel"); String text; text = helper.invoke("org1", "EndorsementCC:setval", mode, "foo"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("foo")); text = helper.invoke("org1", "EndorsementCC:addorgs", mode, "org1MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org1MSP")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val1"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val1")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val2"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val2")); text = helper.invoke("org1", "EndorsementCC:addorgs", mode, "org2MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org2MSP")); assertThat(text, containsString("org1MSP")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val3"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val3")); text = helper.invoke("org1", "EndorsementCC:setval", mode, "val4"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:getval", mode); assertThat(text, containsString("val4")); text = helper.invoke("org1", "EndorsementCC:delorgs", mode, "org1MSP"); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:listorgs", mode); assertThat(text, containsString("org2MSP")); assertThat(text, not(containsString("org1MSP"))); text = helper.invoke("org1", "EndorsementCC:deleteval", mode); assertThat(text, containsString("success")); text = helper.invoke("org1", "EndorsementCC:recordExists", mode); assertThat(text, containsString("false")); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/Bash.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; /** Represents the 'peer' cli command */ public final class Bash extends Command { public static BashBuilder newBuilder() { return new BashBuilder(); } public static class BashBuilder extends Command.Builder { String cmd; String orderer; String channel; String ccname; boolean evaluate = false; int waitForEventTimeout; List args = new ArrayList(); Map transientData; public BashBuilder duplicate() { try { return (BashBuilder) this.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public BashBuilder cmd(String cmd) { this.cmd = cmd; return this; } public BashBuilder cmdargs(String argsArray[]) { this.args = Arrays.asList(argsArray); return this; } public Bash build(Map additionalEnv) { ArrayList list = new ArrayList<>(); list.add(cmd); return new Bash(list, additionalEnv); } public Bash build() { ArrayList list = new ArrayList<>(); list.add(cmd); list.addAll(args); return new Bash(list); } } Bash(List cmd) { super(cmd); } Bash(List cmd, Map env) { super(cmd, env); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/Command.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.lang.ProcessBuilder.Redirect; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class Command { protected final List cmd; protected final Map env = new HashMap<>(); Command(List cmd, Map additionalEnv) { this.cmd = cmd; this.env.putAll(additionalEnv); } Command(List cmd) { this(cmd, Collections.emptyMap()); } public static final class Result { public List stdout; public List stderr; public int exitcode; } /** Run but don't suppress the output being printed directly */ public Result run() { return this.run(false); } /** * Run the command, and process the output to arrays for later parsing and checking * * @param quiet true if the output should NOT be printed directly to System.out/System.err */ public Result run(boolean quiet) { ProcessBuilder processBuilder = new ProcessBuilder(cmd); processBuilder.environment().putAll(env); final Result result = new Result(); System.out.println("Running:" + this); try { processBuilder.redirectInput(Redirect.INHERIT); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); System.out.println("Started..... "); CompletableFuture> soutFut = readOutStream(process.getInputStream(), quiet ? null : System.out); CompletableFuture> serrFut = readOutStream(process.getErrorStream(), quiet ? null : System.err); CompletableFuture resultFut = soutFut.thenCombine(serrFut, (stdout, stderr) -> { // print to current stderr the stderr of process and return the stdout result.stderr = stderr; result.stdout = stdout; return result; }); result.exitcode = process.waitFor(); // get stdout once ready, blocking resultFut.get(); } catch (IOException | InterruptedException | ExecutionException e) { e.printStackTrace(); result.exitcode = -1; } return result; } /** * Collect the information from the executed process and add them to a result object * * @param is * @param stream * @return Completable Future with the array list of the stdout/stderr */ CompletableFuture> readOutStream(InputStream is, PrintStream stream) { return CompletableFuture.supplyAsync(() -> { try (InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); ) { ArrayList res = new ArrayList<>(); String inputLine; while ((inputLine = br.readLine()) != null) { if (stream != null) stream.println(inputLine); res.add(inputLine); } return res; } catch (Throwable e) { throw new RuntimeException("problem with executing program", e); } }); } public String toString() { return "[" + String.join(" ", cmd) + "]"; } public static class Builder implements Cloneable { @SuppressWarnings("unchecked") public Builder duplicate() { try { return (Builder) this.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/Docker.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.util.ArrayList; import java.util.List; /** Represents the 'docker' cli command */ public final class Docker extends Command { public static DockerBuilder newBuilder() { return new DockerBuilder(); } public static final class DockerBuilder implements Cloneable { boolean exec; String container; String script; String channel; public DockerBuilder duplicate() { try { return (DockerBuilder) this.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public DockerBuilder script(String script) { this.script = script; return this; } public DockerBuilder channel(String channel) { this.channel = channel; return this; } public DockerBuilder container(String container) { this.container = container; return this; } public DockerBuilder exec() { this.exec = true; return this; } public Docker build() { ArrayList list = new ArrayList<>(); list.add("docker"); if (exec) { list.add("exec"); } if (container == null || container.isEmpty()) { throw new RuntimeException("container should be set"); } list.add(container); if (script == null || script.isEmpty()) { throw new RuntimeException("script should be set"); } list.add(script); if (channel == null || channel.isEmpty()) { throw new RuntimeException("channel should be set"); } list.add(channel); return new Docker(list); } } Docker(List cmd) { super(cmd); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/DockerCompose.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.util.ArrayList; import java.util.List; /** Represents the 'peer' cli command */ public final class DockerCompose extends Command { public static DockerComposeBuilder newBuilder() { return new DockerComposeBuilder(); } public static final class DockerComposeBuilder extends Command.Builder { String composeFile; boolean up = true; boolean detach = false; public DockerComposeBuilder file(String composeFile) { this.composeFile = composeFile; return this; } public DockerComposeBuilder duplicate() { return (DockerComposeBuilder) super.duplicate(); } public DockerComposeBuilder up() { this.up = true; return this; } public DockerComposeBuilder detach() { this.detach = true; return this; } public DockerComposeBuilder down() { this.up = false; return this; } public DockerCompose build() { ArrayList list = new ArrayList<>(); list.add("docker-compose"); if (composeFile != null && !composeFile.isEmpty()) { list.add("-f"); list.add(composeFile); } list.add(up ? "up" : "down"); if (detach) { list.add("-d"); } return new DockerCompose(list); } } DockerCompose(List cmd) { super(cmd); super.env.put("COMPOSE_PROJECT_NAME", "first-network"); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/FabricState.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import org.hyperleder.fabric.shim.integration.util.Bash.BashBuilder; public final class FabricState { private static final FabricState state = new FabricState(); private boolean started = false; public static FabricState getState() { return state; } public synchronized void start() { if (!this.started) { BashBuilder bashBuilder = new Bash.BashBuilder().cmd("src/test/resources/scripts/mfsetup.sh"); bashBuilder.build().run(); this.started = true; } else { System.out.println("Fabric already started...."); } } public Map orgEnv(String org) { Path currentRelativePath = Paths.get(""); String s = currentRelativePath.toAbsolutePath().toString(); Map env = new HashMap<>(); env.put( "CORE_PEER_MSPCONFIGPATH", Paths.get(s, "src/test/resources/_cfg/_msp/" + org, org + "admin/msp") .toString()); env.put("CORE_PEER_LOCALMSPID", org + "MSP"); env.put("CORE_PEER_ADDRESS", org + "peer-api.127-0-0-1.nip.io:8080"); System.out.println(env); return env; } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/InvokeHelper.java ================================================ package org.hyperleder.fabric.shim.integration.util; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; import org.hyperleder.fabric.shim.integration.util.Command.Result; import org.hyperleder.fabric.shim.integration.util.Peer.PeerBuilder; public class InvokeHelper { private String ccname; private String channel; public static InvokeHelper newHelper(String ccname, String channel) { InvokeHelper ih = new InvokeHelper(); ih.ccname = ccname; ih.channel = channel; return ih; } public String invoke(String org, String... args) { Map orgEnv = FabricState.getState().orgEnv(org); PeerBuilder coreBuilder = Peer.newBuilder().ccname(ccname).channel(channel); Result r = coreBuilder.argsTx(args).build(orgEnv).run(); System.out.println(r.stdout); String text = r.stdout.stream() .filter(line -> line.matches(".*chaincodeInvokeOrQuery.*")) .collect(Collectors.joining(System.lineSeparator())) .trim(); if (!text.contains("result: status:200")) { Command logsCommand = new Command(Arrays.asList("docker", "logs", "microfab"), orgEnv); logsCommand.run(); throw new RuntimeException(text); } int payloadIndex = text.indexOf("payload:"); if (payloadIndex > 1) { return text.substring(payloadIndex + 9, text.length() - 1); } return "success"; } } ================================================ FILE: fabric-chaincode-integration-test/src/test/java/org/hyperleder/fabric/shim/integration/util/Peer.java ================================================ /* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package org.hyperleder.fabric.shim.integration.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; /** Represents the 'peer' cli command */ public final class Peer extends Command { public static PeerBuilder newBuilder() { return new PeerBuilder(); } public static final class PeerBuilder extends Command.Builder { String tlsArgs; String orderer; String channel; String ccname; boolean evaluate = false; int waitForEventTimeout; List args = new ArrayList(); Map transientData; public PeerBuilder duplicate() { try { return (PeerBuilder) this.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public PeerBuilder tlsArgs(String tlsArgs) { this.tlsArgs = tlsArgs; return this; } public PeerBuilder orderer(String orderer) { this.orderer = orderer; return this; } public PeerBuilder channel(String channel) { this.channel = channel; return this; } public PeerBuilder ccname(String ccname) { this.ccname = ccname; return this; } public PeerBuilder evaluate() { this.evaluate = true; return this; } public PeerBuilder invoke() { this.evaluate = false; return this; } public PeerBuilder argsTx(List args) { this.args = args; return this; } public PeerBuilder argsTx(String[] argsArray) { this.args = Arrays.asList(argsArray); return this; } public PeerBuilder transientData(Map transientData) { this.transientData = transientData; return this; } public PeerBuilder waitForEvent(int seconds) { this.waitForEventTimeout = seconds; return this; } public PeerBuilder waitForEvent() { this.waitForEvent(0); return this; } private String transientToString() { JSONObject json = new JSONObject(this.transientData); return "'" + json.toString() + "'"; } private String argsToString() { JSONArray array = new JSONArray(this.args); JSONObject json = new JSONObject(); json.put("Args", array); return json.toString(); } public Peer build(Map additionalEnv) { ArrayList list = new ArrayList<>(); list.add("peer"); list.add("chaincode"); list.add(evaluate ? "query" : "invoke"); if (tlsArgs != null && !tlsArgs.isEmpty()) { list.add(tlsArgs); } if (channel == null || channel.isEmpty()) { throw new RuntimeException("Channel should be set"); } list.add("-C"); list.add(channel); if (ccname == null || ccname.isEmpty()) { throw new RuntimeException("Chaincode name should be set"); } list.add("-n"); list.add(ccname); if (args == null || args.isEmpty()) { throw new RuntimeException("Args should be set"); } list.add("-c"); list.add(argsToString()); if (transientData != null && !transientData.isEmpty()) { list.add("--transient"); list.add(transientToString()); } if (waitForEventTimeout > 0) { list.add("--waitForEvent --waitForEventTimeout"); list.add(waitForEventTimeout + "s"); } else if (waitForEventTimeout == 0) { list.add("--waitForEvent"); } list.add("--orderer"); list.add("orderer-api.127-0-0-1.nip.io:8080"); list.add("--peerAddresses"); list.add("org1peer-api.127-0-0-1.nip.io:8080"); list.add("--peerAddresses"); list.add("org2peer-api.127-0-0-1.nip.io:8080"); return new Peer(list, additionalEnv); } } Peer(List cmd, Map additionalEnv) { super(cmd, additionalEnv); } } ================================================ FILE: fabric-chaincode-integration-test/src/test/resources/docker-compose-microfab.yaml ================================================ # Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # version: '2' services: microfab: container_name: microfab image: ghcr.io/hyperledger-labs/microfab tty: true environment: - MICROFAB_CONFIG={"couchdb":false,"endorsing_organizations":[{"name":"org1"},{"name":"org2"}],"channels":[{"name":"sachannel","endorsing_organizations":["org1","org2"]}],"capability_level":"V2_5"} - FABRIC_LOGGING_SPEC=info ports: - 8080:8080 ================================================ FILE: fabric-chaincode-integration-test/src/test/resources/scripts/ccutils.sh ================================================ #!/bin/bash C_RESET='\033[0m' C_RED='\033[0;31m' C_GREEN='\033[0;32m' C_BLUE='\033[0;34m' C_YELLOW='\033[1;33m' export VERBOSE=true export MAX_RETRY=3 export DELAY=5 function setGlobals() { local USING_ORG=$1 infoln "Using organization ${USING_ORG}" if [ $USING_ORG -eq 1 ]; then export CORE_PEER_LOCALMSPID="org1MSP" export CORE_PEER_MSPCONFIGPATH=${CFG}/_msp/org1/org1admin/msp export CORE_PEER_ADDRESS=org1peer-api.127-0-0-1.nip.io:8080 elif [ $USING_ORG -eq 2 ]; then export CORE_PEER_LOCALMSPID="org2MSP" export CORE_PEER_MSPCONFIGPATH=${CFG}/_msp/org2/org2admin/msp export CORE_PEER_ADDRESS=org2peer-api.127-0-0-1.nip.io:8080 else errorln "ORG Unknown" fi if [ "$VERBOSE" == "true" ]; then env | grep CORE fi } # installChaincode PEER ORG function installChaincode() { ORG=$1 setGlobals $ORG set -x peer lifecycle chaincode queryinstalled --output json | jq -r 'try (.installed_chaincodes[].package_id)' | grep ^${PACKAGE_ID}$ >&log.txt if test $? -ne 0; then peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt res=$? fi { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode installation on peer0.org${ORG} has failed" successln "Chaincode is installed on peer0.org${ORG}" } # queryInstalled PEER ORG function queryInstalled() { ORG=$1 setGlobals $ORG set -x peer lifecycle chaincode queryinstalled --output json | jq -r 'try (.installed_chaincodes[].package_id)' | grep ^${PACKAGE_ID}$ >&log.txt res=$? { set +x; } 2>/dev/null cat log.txt verifyResult $res "Query installed on peer0.org${ORG} has failed" successln "Query installed successful on peer0.org${ORG} on channel" } # approveForMyOrg VERSION PEER ORG function approveForMyOrg() { ORG=$1 setGlobals $ORG set -x peer lifecycle chaincode approveformyorg -o orderer-api.127-0-0-1.nip.io:8080 --channelID $CHANNEL_NAME --name ${CC_NAME} ${COLLECTIONS_CFG} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} >&log.txt res=$? { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" successln "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME'" } # checkCommitReadiness VERSION PEER ORG function checkCommitReadiness() { ORG=$1 shift 1 setGlobals $ORG infoln "Checking the commit readiness of the chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'..." local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY infoln "Attempting to check the commit readiness of the chaincode definition on peer0.org${ORG}, Retry after $DELAY seconds." set -x peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} ${COLLECTIONS_CFG} --sequence ${CC_SEQUENCE} --output json >&log.txt res=$? { set +x; } 2>/dev/null let rc=0 for var in "$@"; do grep "$var" log.txt &>/dev/null || let rc=1 done COUNTER=$(expr $COUNTER + 1) done cat log.txt if test $rc -eq 0; then infoln "Checking the commit readiness of the chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME'" else fatalln "After $MAX_RETRY attempts, Check commit readiness result on peer0.org${ORG} is INVALID!" fi } # commitChaincodeDefinition VERSION PEER ORG (PEER ORG)... function commitChaincodeDefinition() { parsePeerConnectionParameters $@ res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option set -x peer lifecycle chaincode commit -o orderer-api.127-0-0-1.nip.io:8080 --channelID $CHANNEL_NAME --name ${CC_NAME} "${PEER_CONN_PARMS[@]}" --version ${CC_VERSION} ${COLLECTIONS_CFG} --sequence ${CC_SEQUENCE} >&log.txt res=$? { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode definition commit failed on peer0.org${ORG} on channel '$CHANNEL_NAME' failed" successln "Chaincode definition committed on channel '$CHANNEL_NAME'" } # queryCommitted ORG function queryCommitted() { ORG=$1 setGlobals $ORG EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc" infoln "Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'..." local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY infoln "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds." set -x peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt res=$? { set +x; } 2>/dev/null test $res -eq 0 && VALUE=$(cat log.txt | grep -o '^Version: '$CC_VERSION', Sequence: [0-9]*, Endorsement Plugin: escc, Validation Plugin: vscc') test "$VALUE" = "$EXPECTED_RESULT" && let rc=0 COUNTER=$(expr $COUNTER + 1) done cat log.txt if test $rc -eq 0; then successln "Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME'" else fatalln "After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID!" fi } function chaincodeInvokeInit() { parsePeerConnectionParameters $@ res=$? verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters " # while 'peer chaincode' command can get the orderer endpoint from the # peer (if join was successful), let's supply it directly as we know # it using the "-o" option set -x fcn_call='{"function":"'${CC_INIT_FCN}'","Args":[]}' infoln "invoke fcn call:${fcn_call}" peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C $CHANNEL_NAME -n ${CC_NAME} "${PEER_CONN_PARMS[@]}" --isInit -c ${fcn_call} >&log.txt res=$? { set +x; } 2>/dev/null cat log.txt verifyResult $res "Invoke execution on $PEERS failed " successln "Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME'" } function chaincodeQuery() { ORG=$1 setGlobals $ORG infoln "Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'..." local rc=1 local COUNTER=1 # continue to poll # we either get a successful response, or reach MAX RETRY while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do sleep $DELAY infoln "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds." set -x peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' >&log.txt res=$? { set +x; } 2>/dev/null let rc=$res COUNTER=$(expr $COUNTER + 1) done cat log.txt if test $rc -eq 0; then successln "Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME'" else fatalln "After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID!" fi } function packageChaincode() { set -x peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME} >&log.txt res=$? PACKAGE_ID=$(peer lifecycle chaincode calculatepackageid ${CC_NAME}.tar.gz) { set +x; } 2>/dev/null cat log.txt verifyResult $res "Chaincode packaging has failed" successln "Chaincode is packaged" } # println echos string function println() { echo -e "$1" } # errorln echos i red color function errorln() { println "${C_RED}${1}${C_RESET}" } # successln echos in green color function successln() { println "${C_GREEN}${1}${C_RESET}" } # infoln echos in blue color function infoln() { println "${C_BLUE}${1}${C_RESET}" } # warnln echos in yellow color function warnln() { println "${C_YELLOW}${1}${C_RESET}" } # fatalln echos in red color and exits with fail status function fatalln() { errorln "$1" exit 1 } function verifyResult() { if [ $1 -ne 0 ]; then fatalln "$2" fi } # parsePeerConnectionParameters $@ # Helper function that sets the peer connection parameters for a chaincode # operation function parsePeerConnectionParameters() { PEER_CONN_PARMS=() PEERS="" while [ "$#" -gt 0 ]; do setGlobals $1 PEER="peer0.org$1" ## Set peer addresses if [ -z "$PEERS" ] then PEERS="$PEER" else PEERS="$PEERS $PEER" fi PEER_CONN_PARMS=("${PEER_CONN_PARMS[@]}" --peerAddresses $CORE_PEER_ADDRESS) ## Set path to TLS certificate CA=PEER0_ORG$1_CA TLSINFO=(--tlsRootCertFiles "${!CA}") PEER_CONN_PARMS=("${PEER_CONN_PARMS[@]}" "${TLSINFO[@]}") # shift by one to get to the next organization shift done } ================================================ FILE: fabric-chaincode-integration-test/src/test/resources/scripts/collection_config.json ================================================ [ { "name": "col", "policy": "OR( OR('org1MSP.member','org1MSP.admin') , OR('org2MSP.member','org2MSP.admin') )", "blockToLive": 100000, "maxPeerCount": 1, "requiredPeerCount": 1 } ] ================================================ FILE: fabric-chaincode-integration-test/src/test/resources/scripts/mfsetup.sh ================================================ #!/bin/bash set -x DIR="$(cd "$(dirname "${BASH_SOURCE[0]}" )"/.. && pwd )" export CFG="${DIR}/_cfg" mkdir -p "${CFG}" # --- # Setup up Microfab image # using the IBM tagged version until labs workflow is updated docker rm -f microfab || true export MICROFAB_CONFIG='{"couchdb":false,"endorsing_organizations":[{"name":"org1"},{"name":"org2"}],"channels":[{"name":"sachannel","endorsing_organizations":["org1","org2"]}],"capability_level":"V2_5"}' docker run --name microfab \ -d \ -p 8080:8080 \ --add-host host.docker.internal:host-gateway \ --rm \ -e MICROFAB_CONFIG="${MICROFAB_CONFIG}" \ -e FABRIC_LOGGING_SPEC=info \ ghcr.io/hyperledger-labs/microfab timeout 60s bash -c 'docker logs -f microfab | grep --max-count=1 "Microfab started"' curl -sSL http://console.localho.st:8080/ak/api/v1/components > $CFG/cfg.json npx @hyperledger-labs/weft microfab -w $CFG/_wallets -p $CFG/_gateways -m $CFG/_msp -f --config $CFG/cfg.json # bring in the helper bash scripts . $DIR/scripts/ccutils.sh function deployCC() { ## package the chaincode packageChaincode ## Install chaincode on peer0.org1 and peer0.org2 infoln "Installing chaincode on peer0.org1..." installChaincode 1 infoln "Install chaincode on peer0.org2..." installChaincode 2 ## query whether the chaincode is installed queryInstalled 1 ## approve the definition for org1 approveForMyOrg 1 ## check whether the chaincode definition is ready to be committed ## expect org1 to have approved and org2 not to checkCommitReadiness 1 "\"org1MSP\": true" "\"org2MSP\": false" checkCommitReadiness 2 "\"org1MSP\": true" "\"org2MSP\": false" ## now approve also for org2 approveForMyOrg 2 ## check whether the chaincode definition is ready to be committed ## expect them both to have approved checkCommitReadiness 1 "\"org1MSP\": true" "\"org2MSP\": true" checkCommitReadiness 2 "\"org1MSP\": true" "\"org2MSP\": true" ## now that we know for sure both orgs have approved, commit the definition commitChaincodeDefinition 1 2 ## query on both orgs to see that the definition committed successfully queryCommitted 1 queryCommitted 2 } #./gradlew -I ./chaincode-init.gradle -PchaincodeRepoDir=$(realpath ./fabric-chaincode-integration-test/src/contracts/fabric-ledger-api/repository) publishShimPublicationToFabricRepository export CC_SRC_PATH="${DIR}/../../contracts/fabric-ledger-api" export CC_NAME="ledgercc" export CC_RUNTIME_LANGUAGE=java export CC_VERSION=1 export CC_SEQUENCE=1 export CHANNEL_NAME=sachannel export COLLECTIONS_CFG="" deployCC export CC_SRC_PATH="${DIR}/../../contracts/bare-gradle" export CC_NAME="baregradlecc" deployCC export CC_SRC_PATH="${DIR}/../../contracts/bare-maven" export CC_NAME="baremaven" deployCC export CC_SRC_PATH="${DIR}/../../contracts/wrapper-maven" export CC_NAME="wrappermaven" deployCC export COLLECTIONS_CFG="--collections-config ${CFG}/../scripts/collection_config.json" export CC_SRC_PATH="${DIR}/../../contracts/fabric-shim-api" export CC_NAME="shimcc" deployCC ================================================ FILE: fabric-chaincode-shim/build.gradle ================================================ /* * Copyright IBM Corp. 2018 All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ plugins { id 'maven-publish' id 'signing' id 'jacoco' id 'pmd' id "com.gradleup.nmcp" } pmd { toolVersion = '7.20.0' ruleSetFiles = files('../pmd-ruleset.xml') ruleSets = [] // explicitly set to empty to avoid using the default configuration ignoreFailures = false } pmdTest.enabled = false dependencyLocking { lockAllConfigurations() } tasks.withType(Test).configureEach { systemProperty 'CORE_CHAINCODE_LOGGING_LEVEL', 'DEBUG' } dependencies { constraints { pmd('org.apache.commons:commons-lang3:3.19.0') { because('CVE-2025-48924') } } implementation platform('com.google.protobuf:protobuf-bom:4.34.1') implementation platform('io.grpc:grpc-bom:1.80.0') implementation platform('io.opentelemetry:opentelemetry-bom:1.61.0') implementation platform("org.bouncycastle:bc-jdk18on-bom:1.84") implementation 'org.hyperledger.fabric:fabric-protos:0.3.7' implementation 'org.bouncycastle:bcpkix-jdk18on' implementation 'org.bouncycastle:bcprov-jdk18on' implementation 'io.github.classgraph:classgraph:4.8.184' implementation 'com.github.erosb:everit-json-schema:1.14.6' implementation 'org.json:json:20251224' implementation 'com.google.protobuf:protobuf-java-util' implementation 'io.grpc:grpc-netty-shaded' implementation 'io.grpc:grpc-protobuf' implementation 'io.grpc:grpc-stub' testImplementation 'io.grpc:grpc-inprocess' implementation 'io.opentelemetry:opentelemetry-api' implementation 'io.opentelemetry.proto:opentelemetry-proto:1.10.0-alpha' implementation 'io.opentelemetry:opentelemetry-sdk' implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure' implementation 'io.opentelemetry:opentelemetry-sdk-trace' implementation 'io.opentelemetry:opentelemetry-exporter-otlp' implementation 'io.opentelemetry:opentelemetry-extension-trace-propagators' implementation 'io.opentelemetry.semconv:opentelemetry-semconv:1.40.0' implementation 'io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.26.1-alpha' } sourceSets { main { java { srcDirs 'src/main/java' } } test { java { srcDir 'src/test/java' } } } jacocoTestReport { afterEvaluate { classDirectories.from = files(classDirectories.files.collect { fileTree(dir: it, exclude: 'org/hyperledger/fabric/protos/**') }) } } jacocoTestCoverageVerification { violationRules { rule { element = 'CLASS' excludes = ['org.hyperledger.fabric.shim.helper.Channel', 'org.hyperledger.fabric.shim.impl.Handler', 'org.hyperledger.fabric.shim.impl.ChaincodeSupportStream.1', 'org.hyperledger.fabric.contract.ContractRouter', 'org.hyperledger.fabric.contract.routing.impl.ContractDefinitionImpl', 'org.hyperledger.fabric.contract.routing.RoutingRegistry', 'org.hyperledger.fabric.contract.execution.impl.ContractInvocationRequest', 'org.hyperledger.fabric.contract.routing.TransactionType', 'org.hyperledger.fabric.contract.metadata.MetadataBuilder', 'org.hyperledger.fabric.shim.ChaincodeBase*', 'org.hyperledger.fabric.shim.impl.InvocationTaskManager', 'org.hyperledger.fabric.shim.impl.InvocationStubImpl*', 'org.hyperledger.fabric.shim.impl.ChaincodeSupportClient*', 'org.hyperledger.fabric.shim.impl.InvocationTaskExecutor', 'org.hyperledger.fabric.shim.impl.ChaincodeInvocationTask', 'org.hyperledger.fabric.shim.impl.QueryResultsIteratorImpl*', 'org.hyperledger.fabric.shim.impl.ChaincodeMessageFactory', 'org.hyperledger.fabric.shim.ChaincodeServerProperties'] limit { minimum = 0.86 } } rule { element = 'CLASS' includes = ['org.hyperledger.fabric.shim.helper.Channel', 'org.hyperledger.fabric.contract.ContractRouter', 'org.hyperledger.fabric.contract.execution.impl.ContractInvocationRequest', 'org.hyperledger.fabric.contract.routing.impl.ContractDefinitionImpl', 'org.hyperledger.fabric.contract.routing.RoutingRegistry', 'org.hyperledger.fabric.shim.impl.Handler', 'org.hyperledger.fabric.shim.ChaincodeBase', 'org.hyperledger.fabric.contract.metadata.MetadataBuilder', 'org.hyperledger.fabric.shim.impl.InvocationTaskManager', 'org.hyperledger.fabric.shim.impl.InvocationTaskExecutor', 'org.hyperledger.fabric.shim.impl.ChaincodeSupportClient', 'org.hyperledger.fabric.shim.impl.ChaincodeMessageFactory'] limit { minimum = 0.71 } } } } test.finalizedBy(jacocoTestReport) test.finalizedBy(jacocoTestCoverageVerification) tasks.register('licenseCheck') { group = "license" description = "Checks the License part of each source file" println "Checking Licences..." def noSPDX = new LinkedList() def missing = new LinkedList() sourceSets.forEach { sourceSet -> sourceSet.allSource.findAll { !it.path.contains("build") && !(it.path.contains("test") && it.path.contains("resources")) }.each { file -> if (!file.name.contains("json")) { BufferedReader r = new BufferedReader(new FileReader(file)) def line, hasSPDX = false, hasTraditional = false while ((line = r.readLine()) != null) { if (line.contains("SPDX-License-Identifier")) { hasSPDX = true break } if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) { hasTraditional = true break } } if (!hasSPDX) { if (hasTraditional) { noSPDX.add(file) } else { missing.add(file) } } } } } if (noSPDX.isEmpty()) { println "All remaining files have Apache 2.0 headers." } else { println "We are standardizing with the SPDX style license headers." println "The following files contain the traditional license headers which are still valid:" noSPDX.each { f -> println "\t" + f.getPath() } println "If you need to make a content update, please replace the Apache license header comment text with:" println "\tSPDX-License-Identifier: Apache-2.0\n" } if (!missing.isEmpty()) { def error = "The following files are missing Apache 2.0 headers:\n" missing.each { f -> error += f.getPath() + "\n" } error += "Fatal Error - All files must have a license header" throw new IllegalArgumentException(error) } } javadoc { failOnError = true excludes = ['org/hyperledger/fabric/contract/ContextFactory.java', 'org/hyperledger/fabric/contract/ContractRouter.java', 'org/hyperledger/fabric/contract/ContractRuntimeException.java', 'org/hyperledger/fabric/contract/execution/**', 'org/hyperledger/fabric/contract/metadata/**', 'org/hyperledger/fabric/contract/routing/**', 'org/hyperledger/fabric/contract/systemcontract/**', 'org/hyperledger/fabric/ledger/**', 'org/hyperledger/fabric/shim/helper/**', 'org/hyperledger/fabric/**/impl/**'] source = sourceSets.main.allJava classpath = sourceSets.main.runtimeClasspath options.addStringOption('Xdoclint:none', '-quiet') options.addStringOption('Xwerror', '-quiet') options.overview = "src/main/java/org/hyperledger/fabric/overview.html" } def final stagingDeployUrl = layout.buildDirectory.dir('staging-deploy') publishing { publications { shim(MavenPublication) { groupId = project.group artifactId = project.name version = project.version from components.java pom { name = 'JavaChaincodeShim' packaging = 'jar' description = 'Hyperledger Fabric Java Chaincode Shim' url = 'https://hyperledger.github.io/fabric-chaincode-java/' scm { connection = 'scm:git:https://github.com/hyperledger/fabric-chaincode-java.git' developerConnection = 'scm:git:ssh://github.com:hyperledger/fabric-chaincode-java.git' url = 'https://github.com/hyperledger/fabric-chaincode-java' } licenses { license { name = 'The Apache License, Version 2.0' url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { id = 'denyeart' name = 'David Enyeart' email = 'enyeart@us.ibm.com' } developer { id = 'bestbeforetoday' name = 'Mark S. Lewis' email = 'Mark.S.Lewis@outlook.com' } } } } } repositories { maven { name = "GitHub" url = "https://maven.pkg.github.com/hyperledger/fabric-chaincode-java" credentials { username = System.getenv("GITHUB_ACTOR") password = System.getenv("GITHUB_TOKEN") } } } } signing { def signingKey = findProperty('signingKey') def signingPassword = findProperty('signingPassword') useInMemoryPgpKeys(signingKey, signingPassword) required = { gradle.taskGraph.hasTask(":${project.name}:publishShimPublicationToNmcpRepository") || gradle.taskGraph.hasTask(":${project.name}:publishShimPublicationToGitHubRepository") } sign publishing.publications.shim } // Need to specify the sourcesJar task BEFORE the java{withSourcesJar()} so that it picks up the duplicatesStratergy // otherwise this fails with a duplicates error. // (see https://github.com/gradle/gradle/issues/17236) tasks.register('sourcesJar', Jar) { duplicatesStrategy = DuplicatesStrategy.INCLUDE archiveClassifier = 'sources' from sourceSets.main.allSource } java { withJavadocJar() withSourcesJar() } build.dependsOn licenseCheck // setup more detailed test output formats import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent tasks.withType(Test).configureEach { environment "CORE_PEER_LOCALMSPID", "mymsp" testLogging { // set options for log level LIFECYCLE events TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_OUT exceptionFormat = TestExceptionFormat.FULL showExceptions = true showCauses = true showStackTraces = true // set options for log level DEBUG and INFO debug { events TestLogEvent.STARTED, TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.STANDARD_ERROR, TestLogEvent.STANDARD_OUT exceptionFormat = TestExceptionFormat.FULL } info.events = debug.events info.exceptionFormat = debug.exceptionFormat afterSuite {desc, result -> if (!desc.parent) { // will match the outermost suite def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" def startItem = '| ', endItem = ' |' def repeatLength = startItem.length() + output.length() + endItem.length() println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) } } } } ================================================ FILE: fabric-chaincode-shim/javabuild.sh ================================================ #!/bin/bash # #Copyright DTCC 2016 All Rights Reserved. # #Licensed under the Apache License, Version 2.0 (the "License"); #you may not use this file except in compliance with the License. #You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # #Unless required by applicable law or agreed to in writing, software #distributed under the License is distributed on an "AS IS" BASIS, #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #See the License for the specific language governing permissions and #limitations under the License. # # set -e PARENTDIR=$(pwd) ARCH=`uname -m` pattern='(https?://)?((([^:\/]+)(:([^\/]*))?@)?([^:\/?]+)(:([0-9]+))?)' [ -n "$http_proxy" ] && HTTPPROXY=$http_proxy [ -n "$HTTP_PROXY" ] && HTTPPROXY=$HTTP_PROXY [ -n "$https_proxy" ] && HTTPSPROXY=$https_proxy [ -n "$HTTPS_PROXY" ] && HTTPSPROXY=$HTTPS_PROXY if [ -n "$HTTPPROXY" ]; then if [[ "$HTTPPROXY" =~ $pattern ]]; then [ -n "${BASH_REMATCH[4]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyUser=${BASH_REMATCH[4]}" [ -n "${BASH_REMATCH[6]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyPassword=${BASH_REMATCH[6]}" [ -n "${BASH_REMATCH[7]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyHost=${BASH_REMATCH[7]}" [ -n "${BASH_REMATCH[9]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyPort=${BASH_REMATCH[9]}" fi fi if [ -n "$HTTPSPROXY" ]; then if [[ "$HTTPSPROXY" =~ $pattern ]]; then [ -n "${BASH_REMATCH[4]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttps.proxyUser=${BASH_REMATCH[4]}" [ -n "${BASH_REMATCH[6]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttps.proxyPassword=${BASH_REMATCH[6]}" [ -n "${BASH_REMATCH[7]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttps.proxyHost=${BASH_REMATCH[7]}" [ -n "${BASH_REMATCH[9]}" ] && JAVA_OPTS="$JAVA_OPTS -Dhttps.proxyPort=${BASH_REMATCH[9]}" fi fi export JAVA_OPTS if [ x$ARCH == xx86_64 ] then gradle -q -b ${PARENTDIR}/core/chaincode/shim/java/build.gradle clean gradle -q -b ${PARENTDIR}/core/chaincode/shim/java/build.gradle build cp -r ${PARENTDIR}/core/chaincode/shim/java/build/libs /root/ else echo "FIXME: Java Shim code needs work on ppc64le and s390x." echo "Commenting it for now." fi ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/Logger.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric; import java.io.PrintWriter; import java.io.StringWriter; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.LogManager; /** Logger class to use throughout the Contract Implementation. */ public class Logger extends java.util.logging.Logger { /** * Subclasses must ensure that a parent logger is set appropriately, for example: * *

{@code logger.setParent(java.util.logging.Logger.getLogger("org.hyperledger.fabric"))} * * @param name A name for the logger. */ protected Logger(final String name) { super(name, null); } /** * @param name * @return Logger */ public static Logger getLogger(final String name) { Logger result = new Logger(name); result.setParent(java.util.logging.Logger.getLogger("org.hyperledger.fabric")); return result; } /** @param msgSupplier */ public void debug(final Supplier msgSupplier) { log(Level.FINEST, msgSupplier); } /** @param msg */ public void debug(final String msg) { log(Level.FINEST, msg); } /** * @param class1 * @return Logger */ public static Logger getLogger(final Class class1) { // important to add the logger to the log manager final Logger result = Logger.getLogger(class1.getName()); LogManager.getLogManager().addLogger(result); return result; } /** @param message */ public void error(final String message) { log(Level.SEVERE, message); } /** @param msgSupplier */ public void error(final Supplier msgSupplier) { log(Level.SEVERE, msgSupplier); } /** * @param throwable * @return Throwable */ public String formatError(final Throwable throwable) { if (throwable == null) { return null; } final StringWriter buffer = new StringWriter(); buffer.append(throwable.getMessage()); throwable.printStackTrace(new PrintWriter(buffer)); final Throwable cause = throwable.getCause(); if (cause != null) { buffer.append(".. caused by .."); buffer.append(this.formatError(cause)); } return buffer.toString(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/Logging.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.logging.Level; import java.util.logging.LogManager; /** * Assistance class to use when logging. * *

For chaincode/contract implementations please use java.util.logging or your own framework. All the Hyperledger * Fabric code here is logged in loggers with names starting org.hyperledger * *

Control of this is via the environment variables 'CORE_CHAINCODE_LOGGING_LEVEL' this takes a string that matches * the following Java.util.logging levels (case insensitive) * *

CRITICAL, ERROR == Level.SEVERE, WARNING == Level.WARNING, INFO == Level.INFO NOTICE == Level.CONFIG, DEBUG == * Level.FINEST */ public final class Logging { /** Name of the Performance logger. */ public static final String PERFLOGGER = "org.hyperledger.Performance"; /** Private Constructor. */ private Logging() {} /** * Formats a Throwable to a string with details of all the causes. * * @param throwable Exception * @return String formatted with all the details */ public static String formatError(final Throwable throwable) { if (throwable == null) { return null; } final StringWriter buffer = new StringWriter(); buffer.append(throwable.getMessage()).append(System.lineSeparator()); throwable.printStackTrace(new PrintWriter(buffer)); final Throwable cause = throwable.getCause(); if (cause != null) { buffer.append(".. caused by ..").append(System.lineSeparator()); buffer.append(formatError(cause)); } return buffer.toString(); } /** * Sets the log level to the the. * * @param newLevel the new logging level */ public static void setLogLevel(final String newLevel) { final Level l = mapLevel(newLevel); final LogManager logManager = LogManager.getLogManager(); // slightly cumbersome approach - but the loggers don't have a 'get children' // so find those that have the correct stem. final List allLoggers = Collections.list(logManager.getLoggerNames()); allLoggers.add("org.hyperledger"); allLoggers.stream() .filter(name -> name.startsWith("org.hyperledger")) .map(logManager::getLogger) .forEach(logger -> { if (logger != null) { logger.setLevel(l); } }); } private static Level mapLevel(final String level) { if (level != null) { switch (level.toUpperCase(Locale.getDefault()).trim()) { case "ERROR": case "CRITICAL": return Level.SEVERE; case "WARNING": case "WARN": return Level.WARNING; case "INFO": return Level.INFO; case "NOTICE": return Level.CONFIG; case "DEBUG": return Level.FINEST; default: return Level.INFO; } } return Level.INFO; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ClientIdentity.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.DEROctetString; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.protos.msp.SerializedIdentity; import org.hyperledger.fabric.shim.ChaincodeStub; import org.json.JSONException; import org.json.JSONObject; /** * ClientIdentity represents information about the identity that submitted a transaction. Chaincodes can use this class * to obtain information about the submitting identity including a unique ID, the MSP (Membership Service Provider) ID, * and attributes. Such information is useful in enforcing access control by the chaincode. */ public final class ClientIdentity { private static final Logger LOGGER = Logger.getLogger(ContractRouter.class.getName()); private final String mspId; private final X509Certificate cert; private final Map attrs; private final String id; // special OID used by Fabric to save attributes in x.509 certificates private static final String FABRIC_CERT_ATTR_OID = "1.2.3.4.5.6.7.8.1"; /** * Creates new ClientIdentity helper. * * @param stub * @throws CertificateException * @throws JSONException * @throws IOException */ public ClientIdentity(final ChaincodeStub stub) throws CertificateException, IOException { final byte[] signingId = stub.getCreator(); // Create a Serialized Identity protobuf final SerializedIdentity si = SerializedIdentity.parseFrom(signingId); this.mspId = si.getMspid(); final byte[] idBytes = si.getIdBytes().toByteArray(); final X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(idBytes)); this.cert = cert; // Get the extension where the identity attributes are stored final byte[] extensionValue = cert.getExtensionValue(FABRIC_CERT_ATTR_OID); if (extensionValue != null) { this.attrs = parseAttributes(extensionValue); } else { this.attrs = new HashMap<>(); } // Populate identity this.id = "x509::" + cert.getSubjectDN().getName() + "::" + cert.getIssuerDN().getName(); } /** * getId returns the ID associated with the invoking identity. This ID is guaranteed to be unique within the MSP. * * @return {String} A string in the format: "x509::{subject DN}::{issuer DN}" */ public String getId() { return this.id; } /** * getMSPID returns the MSP ID of the invoking identity. * * @return {String} */ public String getMSPID() { return this.mspId; } /** * parseAttributes returns a map of the attributes associated with an identity. * * @param extensionValue DER-encoded Octet string stored in the attributes extension of the certificate, as a byte * array * @return attrMap {Map} a map of identity attributes as key value pair strings * @throws IOException */ private Map parseAttributes(final byte[] extensionValue) throws IOException { final Map attrMap = new HashMap<>(); // Create ASN1InputStream from extensionValue try (ByteArrayInputStream inStream = new ByteArrayInputStream(extensionValue); ASN1InputStream asn1InputStream = new ASN1InputStream(inStream)) { // Read the DER object final ASN1Primitive derObject = asn1InputStream.readObject(); if (derObject instanceof DEROctetString) { final DEROctetString derOctetString = (DEROctetString) derObject; // Create attributeString from octets and create JSON object final String attributeString = new String(derOctetString.getOctets(), UTF_8); final JSONObject extJSON = new JSONObject(attributeString); final JSONObject attrs = extJSON.getJSONObject("attrs"); final Iterator keys = attrs.keys(); while (keys.hasNext()) { final String key = keys.next(); // Populate map with attributes and values attrMap.put(key, attrs.getString(key)); } } } catch (final JSONException error) { // creating a JSON object failed // decoded extensionValue is not a string containing JSON LOGGER.error(() -> LOGGER.formatError(error)); // return empty map } return attrMap; } /** * getAttributeValue returns the value of the client's attribute named `attrName`. If the invoking identity * possesses the attribute, returns the value of the attribute. If the invoking identity does not possess the * attribute, returns null. * * @param attrName Name of the attribute to retrieve the value from the identity's credentials (such as x.509 * certificate for PKI-based MSPs). * @return {String | null} Value of the attribute or null if the invoking identity does not possess the attribute. */ public String getAttributeValue(final String attrName) { return this.attrs.getOrDefault(attrName, null); } /** * assertAttributeValue verifies that the invoking identity has the attribute named `attrName` with a value of * `attrValue`. * * @param attrName Name of the attribute to retrieve the value from the identity's credentials (such as x.509 * certificate for PKI-based MSPs) * @param attrValue Expected value of the attribute * @return {boolean} True if the invoking identity possesses the attribute and the attribute value matches the * expected value. Otherwise, returns false. */ public boolean assertAttributeValue(final String attrName, final String attrValue) { return this.attrs.containsKey(attrName) && attrValue.equals(this.attrs.get(attrName)); } /** * getX509Certificate returns the X509 certificate associated with the invoking identity, or null if it was not * identified by an X509 certificate, for instance if the MSP is implemented with an alternative to PKI such as * [Identity Mixer](https://jira.hyperledger.org/browse/FAB-5673). * * @return {X509Certificate | null} */ public X509Certificate getX509Certificate() { return this.cert; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/Context.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import java.io.IOException; import java.security.cert.CertificateException; import org.hyperledger.fabric.shim.ChaincodeStub; import org.json.JSONException; /** * This context is available to all 'transaction functions' and provides the transaction context. It also provides * access to the APIs for the world state using {@link #getStub()} * *

Applications can implement their own versions if they wish to add functionality. All subclasses MUST implement a * constructor, for example * *

{@code
 * public MyContext extends Context {
 *
 *     public MyContext(ChaincodeStub stub) {
 *        super(stub);
 *     }
 * }
 *
 * }
*/ public class Context { /** */ protected ChaincodeStub stub; /** */ protected ClientIdentity clientIdentity; /** * Creates new client identity and sets it as a property of the stub. * * @param stub Instance of the {@link ChaincodeStub} to use */ public Context(final ChaincodeStub stub) { this.stub = stub; try { this.clientIdentity = new ClientIdentity(stub); } catch (CertificateException | JSONException | IOException e) { throw new ContractRuntimeException("Could not create new client identity", e); } } /** @return ChaincodeStub instance to use */ public ChaincodeStub getStub() { return this.stub; } /** @return ClientIdentity object to use */ public ClientIdentity getClientIdentity() { return this.clientIdentity; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContextFactory.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.shim.ChaincodeStub; /** Factory to create {@link Context} from {@link ChaincodeStub} by wrapping stub with dynamic proxy. */ public final class ContextFactory { private static final ContextFactory INSTANCE = new ContextFactory(); /** @return ContextFactory */ public static ContextFactory getInstance() { return INSTANCE; } /** * @param stub * @return Context */ public Context createContext(final ChaincodeStub stub) { return new Context(stub); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractInterface.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.shim.ChaincodeException; import org.hyperledger.fabric.shim.ChaincodeStub; /** * All Contracts should implement this interface, in addition to the * {@linkplain org.hyperledger.fabric.contract.annotation.Contract} annotation. * *

All methods on this interface have default implementations; for many contracts it may not be needed to sub-class * these. * *

Each method on the Contract that is marked with the {@link org.hyperledger.fabric.contract.annotation.Transaction} * annotation is considered a Transaction Function. This is eligible for calling. Each transaction function is supplied * with its first parameter being a {@link org.hyperledger.fabric.contract.Context}. The other parameters are supplied * at the developer's discretion. * *

The sequence of calls is * *

 * createContext()  -> beforeTransaction() -> the transaction function -> afterTransaction()
 * 
* *

If any of these functions throws an exception it is considered an error case and the whole transaction is failed. * The {@link org.hyperledger.fabric.contract.Context} is a very important object as it provides transactional context * for access to current transaction id, ledger state, etc. * *

Note on Threading * *

All code should be 'Thread Friendly'. Each method must not rely on instance fields or class side variables for * storage. Nor should they use any ThreadLocal Storage. Ledger data is stored via the ledger api available via the * {@link Context}. * *

If information needs to be passed from {@link #beforeTransaction(Context)} to {@link #afterTransaction(Context, * Object)} or between separate transaction functions when called directly then a subclass of the {@link Context} should * be provided. */ public interface ContractInterface { /** * Create context from {@link ChaincodeStub}. * *

Default impl provided, but can be overwritten by contract * * @param stub Instance of the ChaincodeStub to use for this transaction * @return instance of the context to use for the current transaction being executed */ default Context createContext(final ChaincodeStub stub) { return ContextFactory.getInstance().createContext(stub); } /** * Invoked for any transaction that does not exist. * *

This will throw an exception. If you wish to alter the exception thrown or if you wish to consider requests * for transactions that don't exist as not an error, subclass this method. * * @param ctx the context as created by {@link #createContext(ChaincodeStub)}. */ default void unknownTransaction(final Context ctx) { throw new ChaincodeException("Undefined contract method called"); } /** * Invoked once before each transaction. * *

Any exceptions thrown will fail the transaction, and neither the required transaction or the * {@link #afterTransaction(Context, Object)} will be called * * @param ctx the context as created by {@link #createContext(ChaincodeStub)}. */ default void beforeTransaction(final Context ctx) { // Nothing by default } /** * Invoked once after each transaction. * *

Any exceptions thrown will fail the transaction. * * @param ctx the context as created by {@link #createContext(ChaincodeStub)}. * @param result The object returned from the transaction function if any. As this is a Java object and therefore * pass-by-reference it is possible to modify this object. */ default void afterTransaction(final Context ctx, final Object result) { // Nothing by default } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractRouter.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import java.io.IOException; import java.util.Properties; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.contract.execution.ExecutionFactory; import org.hyperledger.fabric.contract.execution.ExecutionService; import org.hyperledger.fabric.contract.execution.InvocationRequest; import org.hyperledger.fabric.contract.metadata.MetadataBuilder; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.RoutingRegistry; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.hyperledger.fabric.contract.routing.impl.RoutingRegistryImpl; import org.hyperledger.fabric.contract.routing.impl.SerializerRegistryImpl; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeServer; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.NettyChaincodeServer; import org.hyperledger.fabric.shim.ResponseUtils; import org.hyperledger.fabric.traces.Traces; /** * Router class routes Init/Invoke requests to contracts. Implements {@link org.hyperledger.fabric.shim.Chaincode} * interface. * * @see ContractInterface */ public final class ContractRouter extends ChaincodeBase { private static final Logger LOGGER = Logger.getLogger(ContractRouter.class.getName()); private final RoutingRegistry registry; private final TypeRegistry typeRegistry; // Store instances of SerializerInterfaces - identified by the contract // annotation (default is JSON) private final SerializerRegistryImpl serializers; private final ExecutionService executor; /** * Take the arguments from the cli, and initiate processing of cli options and environment variables. * *

Create the Contract scanner, and the Execution service * * @param args */ public ContractRouter(final String[] args) { super(); super.initializeLogging(); super.processEnvironmentOptions(); super.processCommandLineOptions(args); super.validateOptions(); final Properties props = super.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); LOGGER.fine("ContractRouter"); registry = new RoutingRegistryImpl(); typeRegistry = TypeRegistry.getRegistry(); serializers = new SerializerRegistryImpl(); try { serializers.findAndSetContents(); } catch (InstantiationException | IllegalAccessException e) { final ContractRuntimeException cre = new ContractRuntimeException("Unable to locate Serializers", e); LOGGER.severe(() -> Logging.formatError(cre)); throw cre; } executor = ExecutionFactory.getInstance().createExecutionService(serializers); } /** Locate all the contracts that are available on the classpath. */ void findAllContracts() { registry.findAndSetContracts(this.typeRegistry); } /** * Start the chaincode container off and running. * *

This will send the initial flow back to the peer * * @throws Exception */ @SuppressWarnings("PMD.AvoidCatchingGenericException") void startRouting() { try { super.connectToPeer(); } catch (final Exception e) { LOGGER.severe(() -> Logging.formatError(e)); throw new ContractRuntimeException("Unable to start routing", e); } } @SuppressWarnings("PMD.AvoidCatchingGenericException") private Response processRequest(final ChaincodeStub stub) { LOGGER.info(() -> "Got invoke routing request"); try { if (stub.getStringArgs().isEmpty()) { return ResponseUtils.newSuccessResponse(); } LOGGER.info(() -> "Got the invoke request for:" + stub.getFunction() + " " + stub.getParameters()); final InvocationRequest request = ExecutionFactory.getInstance().createRequest(stub); final TxFunction txFn = getRouting(request); LOGGER.info(() -> "Got routing:" + txFn.getRouting()); return executor.executeRequest(txFn, request, stub); } catch (final Throwable throwable) { return ResponseUtils.newErrorResponse(throwable); } } @Override public Response invoke(final ChaincodeStub stub) { return processRequest(stub); } @Override public Response init(final ChaincodeStub stub) { return processRequest(stub); } /** * Given the Invocation Request, return the routing object for this call. * * @param request * @return TxFunction for the request */ TxFunction getRouting(final InvocationRequest request) { // request name is the fully qualified 'name:txname' if (registry.containsRoute(request)) { return registry.getTxFn(request); } else { LOGGER.fine(() -> "Namespace is " + request); final ContractDefinition contract = registry.getContract(request.getNamespace()); return contract.getUnknownRoute(); } } /** * Main method to start the contract based chaincode. * * @param args */ @SuppressWarnings("PMD.SignatureDeclareThrowsException") public static void main(final String[] args) throws Exception { final ContractRouter cfc = new ContractRouter(args); cfc.findAllContracts(); LOGGER.fine(() -> cfc.getRoutingRegistry().toString()); // Create the Metadata ahead of time rather than have to produce every // time MetadataBuilder.initialize(cfc.getRoutingRegistry(), cfc.getTypeRegistry()); LOGGER.info(() -> "Metadata follows:" + MetadataBuilder.debugString()); // check if this should be running in client or server mode if (cfc.isServer()) { LOGGER.info("Starting chaincode as server"); ChaincodeServer chaincodeServer = new NettyChaincodeServer(cfc, cfc.getChaincodeServerConfig()); chaincodeServer.start(); } else { LOGGER.info("Starting chaincode as client"); cfc.startRouting(); } } TypeRegistry getTypeRegistry() { return this.typeRegistry; } RoutingRegistry getRoutingRegistry() { return this.registry; } /** * Start router and Chaincode server. * * @param chaincodeServer */ public void startRouterWithChaincodeServer(final ChaincodeServer chaincodeServer) throws IOException, InterruptedException { findAllContracts(); LOGGER.fine(() -> getRoutingRegistry().toString()); MetadataBuilder.initialize(getRoutingRegistry(), getTypeRegistry()); LOGGER.info(() -> "Metadata follows:" + MetadataBuilder.debugString()); chaincodeServer.start(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/ContractRuntimeException.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.shim.ChaincodeException; /** * Specific RuntimeException for events that occur in the calling and handling of the Contracts, NOT within the contract * logic itself. * *

FUTURE At some future point we wish to add more diagnostic information into this, for example current tx id */ public class ContractRuntimeException extends ChaincodeException { /** Generated serial version id. */ private static final long serialVersionUID = -884373036398750450L; /** @param string */ public ContractRuntimeException(final String string) { super(string); } /** * @param string * @param cause */ public ContractRuntimeException(final String string, final Throwable cause) { super(string, cause); } /** @param cause */ public ContractRuntimeException(final Throwable cause) { super(cause); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Contact.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Class level annotation that identifies this class as being a contact. Can be populated with email, name and url * fields. */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Contact { /** @return String */ String email() default ""; /** @return String */ String name() default ""; /** @return String */ String url() default ""; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Contract.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Class level annotation that identifies this class as being a contract. Can supply information and an alternative name * for the contract rather than the classname */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Contract { /** * The Info object can be supplied to provide additional information about the contract. * *

Including title, description, version and license * * @return Info object */ Info info() default @Info(); /** * Contract name. * *

Normally the name of the class is used to refer to the contract (name without package). This can be altered if * wished. * * @return Name of the contract to be used instead of the Classname */ String name() default ""; /** * Transaction Serializer Classname. * *

Fully Qualified Classname of the TRANSACTION serializer that should be used with this contract. * *

This is the serializer that is used to parse incoming transaction request parameters and convert the return * type * * @return Default serializer classname */ String transactionSerializer() default "org.hyperledger.fabric.contract.execution.JSONTransactionSerializer"; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/DataType.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Class level annotation indicating this class represents one of the complex types that can be returned or passed to * the transaction functions. * *

These datatypes are used (within the current implementation) for determining the data flow protocol from the * Contracts to the SDK and for permitting a fully formed Interface Definition to be created for the contract. * *

Complex types can appear within this definition, and these are identified using this annotation. * *

FUTURE To take these annotations are also utilize them for leverage storage */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface DataType { /** * Namespace of the type. * * @return String */ String namespace() default ""; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Default.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Default Contract. * *

Class level annotation that defines the contract that is the default contract, and as such invoke of the * transaction functions does not need to be qualified by the contract name */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Default {} ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Info Details * *

Class level annotation that identifies this class as being an info object. Can supply additional information about * the contract, including title, description, version, license and contact information. */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface Info { /** @return String */ String title() default ""; /** @return String */ String description() default ""; /** @return String */ String version() default ""; /** @return String */ String termsOfService() default ""; /** * License object that can be populated to include name and url. * * @return License object */ License license() default @License(); /** * Contact object that can be populated with email, name and url fields. * * @return Contact object */ Contact contact() default @Contact(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/License.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Class level annotation that identifies this class as being a license object. Can be populated to include name and * url. */ @Retention(RUNTIME) @Target(ElementType.TYPE) public @interface License { /** @return String */ String name() default ""; /** @return String */ String url() default ""; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Property.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Field and parameter level annotation defining a property of the class. * *

(identified by {@link DataType}) Can also be used on the parameters of transaction functions * *

Example of using this annotation * *

 *
 * // max 15 character string, a-z with spaces
 * @Property(schema = {"pattern", "^[a-zA-Z\\s]{0,15}$"})
 * private String text;
 *
 * // How friendly is this on a scale of 1-5, 1 being formal, 5 being familiar
 * @Property(schema = {"minimum", "1", "maximum", "5"})
 * private int friendliness = 1;
 *
 * 
*/ @Retention(RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) public @interface Property { /** * Allows each property to be defined a detail set of rules to determine the valid types of this data. The format * follows the syntax of the OpenAPI Schema object. * * @return String array of the key-value pairs of the schema */ String[] schema() default {}; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Serializer.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Class level annotation that defines the serializer that should be used to convert objects to and from the wire * format. * *

This should annotate a class that implements the Serializer interface */ @Retention(RUNTIME) @Target({ElementType.TYPE, ElementType.TYPE_USE}) public @interface Serializer { /** What is this serializer able to target? */ enum TARGET { /** Target transaction functions. */ TRANSACTION, /** Target all elements. */ ALL } /** @return Target of the serializer */ TARGET target() default Serializer.TARGET.ALL; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Transaction.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.annotation; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Method level annotation indicating the method to be a callable transaction function. * *

These functions are called in client SDKs by the combination of * *

 *  [contractname]:[transactioname]
 * 
* * Unless specified otherwise, the contract name is the class name (without package) and the transaction name is the * method name. */ @Retention(RUNTIME) @Target(METHOD) public @interface Transaction { /** The intended invocation style for a transaction function. */ enum TYPE { /** Transaction is used to submit updates to the ledger. */ SUBMIT, /** Transaction is evaluated to query information from the ledger. */ EVALUATE } /** * Submit semantics. * *

TRUE indicates that this function is intended to be called with the 'submit' semantics * *

FALSE indicates that this is intended to be called with the evaluate semantics * * @return boolean, default is true * @deprecated Please use intent */ @Deprecated boolean submit() default true; /** * What are submit semantics for this transaction. * *

*
SUBMIT *
indicates that this function is intended to be called with the 'submit' semantics *
EVALUATE *
indicates that this is intended to be called with the 'evaluate' semantics *
* * @return submit semantics */ TYPE intent() default Transaction.TYPE.SUBMIT; /** * The name of the callable transaction if it should be different to the method name. * * @return the transaction name */ String name() default ""; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Provides annotations required for Contract implementations. * * @see org.hyperledger.fabric.contract.ContractInterface */ package org.hyperledger.fabric.contract.annotation; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionFactory.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import org.hyperledger.fabric.contract.execution.impl.ContractExecutionService; import org.hyperledger.fabric.contract.execution.impl.ContractInvocationRequest; import org.hyperledger.fabric.contract.routing.impl.SerializerRegistryImpl; import org.hyperledger.fabric.shim.ChaincodeStub; public class ExecutionFactory { private static final ExecutionFactory INSTANCE = new ExecutionFactory(); /** @return ExecutionFactory */ public static ExecutionFactory getInstance() { return INSTANCE; } /** * @param context Chaincode Context * @return Invocation request */ public InvocationRequest createRequest(final ChaincodeStub context) { return new ContractInvocationRequest(context); } /** * @param serializers Instance of the serializer * @return Execution Service */ public ExecutionService createExecutionService(final SerializerRegistryImpl serializers) { return new ContractExecutionService(serializers); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionService.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeStub; /** * ExecutionService. * *

Service that executes {@link InvocationRequest} (wrapped Init/Invoke + extra data) using routing information */ @FunctionalInterface public interface ExecutionService { /** * @param txFn * @param req * @param stub * @return Chaincode response */ Chaincode.Response executeRequest(TxFunction txFn, InvocationRequest req, ChaincodeStub stub); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/InvocationRequest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import java.util.List; /** * Invocation Request. * *

All information needed to find {@link org.hyperledger.fabric.contract.annotation.Contract} and invoke the request. */ public interface InvocationRequest { /** */ String DEFAULT_NAMESPACE = "default"; /** @return Namespace */ String getNamespace(); /** @return Method */ String getMethod(); /** @return Args as byte array */ List getArgs(); /** @return Request */ String getRequestName(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/JSONTransactionSerializer.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import static java.nio.charset.StandardCharsets.UTF_8; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Map; import java.util.Set; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Serializer; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.DataTypeDefinition; import org.hyperledger.fabric.contract.routing.PropertyDefinition; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** Used as the default serialisation for transmission from SDK to Contract. */ @Serializer() @SuppressWarnings({ "PMD.GodClass", "PMD.AvoidLiteralsInIfCondition", "PMD.AvoidDuplicateLiterals", "PMD.AvoidDeeplyNestedIfStmts" }) public class JSONTransactionSerializer implements SerializerInterface { private static final Logger LOGGER = Logger.getLogger(JSONTransactionSerializer.class.getName()); private final TypeRegistry typeRegistry = TypeRegistry.getRegistry(); /** * Convert the value supplied to a byte array, according to the TypeSchema. * * @param value * @param ts * @return Byte buffer */ @Override public byte[] toBuffer(final Object value, final TypeSchema ts) { LOGGER.debug(() -> "Schema to convert is " + ts); byte[] buffer = null; if (value != null) { final String type = ts.getType(); if (type != null) { switch (type) { case "array": final JSONArray array = normalizeArray(new JSONArray(value), ts); buffer = array.toString().getBytes(UTF_8); break; case "string": final String format = ts.getFormat(); if ("utin16".equals(format)) { buffer = Character.valueOf((char) value).toString().getBytes(UTF_8); } else { buffer = ((String) value).getBytes(UTF_8); } break; case "number": case "integer": case "boolean": default: buffer = value.toString().getBytes(UTF_8); } } else { // at this point we can assert that the value is // representing a complex data type // so we can get this from // the type registry, and get the list of propertyNames // it should have final DataTypeDefinition dtd = this.typeRegistry.getDataType(ts); final Set keySet = dtd.getProperties().keySet(); final String[] propNames = keySet.toArray(new String[0]); // Note: whilst the current JSON library does pretty much // everything is required, this part is hard. // we want to create a JSON Object based on the value, // with certain property names. // Based on the constructors available we need to have a two // step process, create a JSON Object, then create the object // we really want based on the propNames final JSONObject obj = new JSONObject(new JSONObject(value), propNames); buffer = obj.toString().getBytes(UTF_8); } } return buffer; } /** * Normalize the Array. * *

We need to take the JSON array, and if there are complex datatypes within it ensure that they don't get * spurious JSON properties appearing * *

This method needs to be general so has to copy with nested arrays and with primitive and Object types * * @param jsonArray incoming array * @param ts Schema to normalise to * @return JSONArray */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") private JSONArray normalizeArray(final JSONArray jsonArray, final TypeSchema ts) { JSONArray normalizedArray; // Need to work with what type of array this is final TypeSchema items = ts.getItems(); final String type = items.getType(); if (null == type) { // get the permitted propeties in the type, // then loop over the array and ensure they are correct final DataTypeDefinition dtd = this.typeRegistry.getDataType(items); final Set keySet = dtd.getProperties().keySet(); final String[] propNames = keySet.toArray(new String[0]); normalizedArray = new JSONArray(); // array of objects // iterate over said array for (int i = 0; i < jsonArray.length(); i++) { final JSONObject obj = new JSONObject(jsonArray.getJSONObject(i), propNames); normalizedArray.put(i, obj); } } else if ("array".equals(type)) { // nested arrays, get the type of what it makes up // Need to loop over all elements and normalize each one normalizedArray = new JSONArray(); for (int i = 0; i < jsonArray.length(); i++) { normalizedArray.put(i, normalizeArray(jsonArray.getJSONArray(i), items)); } } else { // primitive - can return this directly normalizedArray = jsonArray; } return normalizedArray; } /** * Take the byte buffer and return the object as required. * * @param buffer Byte buffer from the wire * @param ts TypeSchema representing the type * @return Object created; relies on Java auto-boxing for primitives */ @Override public Object fromBuffer(final byte[] buffer, final TypeSchema ts) { try { final String stringData = new String(buffer, UTF_8); return convert(stringData, ts); } catch (InstantiationException | IllegalAccessException e) { throw new ContractRuntimeException(e); } } /** * We need to be able to map between the primative class types and the object variants. In the case where this is * needed Java auto-boxing doesn't actually help. * *

For other types the parameter is passed directly back * * @param primitive class for the primitive * @return Class for the Object variant */ private Class mapPrimitive(final Class primitive) { if (primitive.isArray()) { return mapArrayPrimitive(primitive); } return mapBasicPrimitive(primitive); } private Class mapArrayPrimitive(final Class primitive) { switch (primitive.getComponentType().getName()) { case "int": return Integer[].class; case "long": return Long[].class; case "float": return Float[].class; case "double": return Double[].class; case "short": return Short[].class; case "byte": return Byte[].class; case "char": return Character[].class; case "boolean": return Boolean[].class; default: return primitive; } } private Class mapBasicPrimitive(final Class primitive) { switch (primitive.getName()) { case "int": return Integer.class; case "long": return Long.class; case "float": return Float.class; case "double": return Double.class; case "short": return Short.class; case "byte": return Byte.class; case "char": return Character.class; case "boolean": return Boolean.class; default: return primitive; } } /** Internal method to do the conversion */ private Object convert(final String stringData, final TypeSchema ts) throws IllegalAccessException, InstantiationException { LOGGER.debug(() -> "Schema to convert is " + ts); String type = ts.getType(); String format = null; if (type == null) { type = "object"; final String ref = ts.getRef(); format = ref.substring(ref.lastIndexOf('/') + 1); } switch (type) { case "string": return convertString(stringData, ts); case "integer": return convertInteger(stringData, ts); case "number": return convertNumber(stringData, ts); case "boolean": return Boolean.parseBoolean(stringData); case "object": return createComponentInstance(format, stringData, ts); case "array": return convertArray(stringData, ts); default: return null; } } private Object convertArray(final String stringData, final TypeSchema ts) throws IllegalAccessException, InstantiationException { final JSONArray jsonArray = new JSONArray(stringData); final TypeSchema itemSchema = ts.getItems(); // note here that the type has to be converted in the case of primitives final Object[] data = (Object[]) Array.newInstance(mapPrimitive(itemSchema.getTypeClass(this.typeRegistry)), jsonArray.length()); for (int i = 0; i < jsonArray.length(); i++) { final Object convertedData = convert(jsonArray.get(i).toString(), itemSchema); data[i] = convertedData; } return data; } private Object convertNumber(final String stringData, final TypeSchema ts) { if ("float".equals(ts.getFormat())) { return Float.parseFloat(stringData); } return Double.parseDouble(stringData); } private Object convertInteger(final String stringData, final TypeSchema ts) { switch (ts.getFormat()) { case "int32": return Integer.parseInt(stringData); case "int8": return Byte.parseByte(stringData); case "int16": return Short.parseShort(stringData); case "int64": return Long.parseLong(stringData); default: throw new IllegalArgumentException("Unknown format for integer " + ts.getFormat()); } } private Object convertString(final String stringData, final TypeSchema ts) { if ("uint16".equals(ts.getFormat())) { return stringData.charAt(0); } return stringData; } /** * Create new instance of the specificied object from the supplied JSON String. * * @param format Details of the format needed * @param jsonString JSON string * @param ts TypeSchema * @return new object */ @SuppressWarnings("PMD.AvoidAccessibilityAlteration") Object createComponentInstance(final String format, final String jsonString, final TypeSchema ts) { final DataTypeDefinition dtd = this.typeRegistry.getDataType(format); Object obj; try { obj = dtd.getTypeClass().getDeclaredConstructor().newInstance(); } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e1) { throw new ContractRuntimeException("Unable to to create new instance of type", e1); } final JSONObject json = new JSONObject(jsonString); // request validation of the type may throw an exception if validation fails ts.validate(json); try { final Map fields = dtd.getProperties(); for (final PropertyDefinition prop : fields.values()) { final Field f = prop.getField(); f.setAccessible(true); final Object newValue = convert(json.get(prop.getName()).toString(), prop.getSchema()); f.set(obj, newValue); } return obj; } catch (SecurityException | IllegalArgumentException | IllegalAccessException | InstantiationException | JSONException e) { throw new ContractRuntimeException("Unable to convert JSON to object", e); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/SerializerInterface.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import org.hyperledger.fabric.contract.metadata.TypeSchema; /** * This interface allows contract developers to change the serialization mechanism. There are two scenarios where * instances of DataTypes are serialized. * *

When the objects are (logically) transferred from the Client application to the Contract resulting in a * transaction function being invoked. Typically this is JSON, hence a default JSON parser is provided. * *

The JSONTransactionSerializer can be extended if needed */ public interface SerializerInterface { /** * Convert the value supplied to a byte array, according to the TypeSchema. * * @param value * @param ts * @return buffer */ byte[] toBuffer(Object value, TypeSchema ts); /** * Take the byte buffer and return the object as required. * * @param buffer Byte buffer from the wire * @param ts TypeSchema representing the type * @return Object created; relies on Java auto-boxing for primitives */ Object fromBuffer(byte[] buffer, TypeSchema ts); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractExecutionService.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution.impl; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Serializer; import org.hyperledger.fabric.contract.execution.ExecutionService; import org.hyperledger.fabric.contract.execution.InvocationRequest; import org.hyperledger.fabric.contract.execution.SerializerInterface; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.ParameterDefinition; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.contract.routing.impl.SerializerRegistryImpl; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeException; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ResponseUtils; public class ContractExecutionService implements ExecutionService { private static final Logger LOGGER = Logger.getLogger(ContractExecutionService.class.getName()); private final SerializerRegistryImpl serializers; /** @param serializers */ public ContractExecutionService(final SerializerRegistryImpl serializers) { this.serializers = serializers; } /** */ @Override public Chaincode.Response executeRequest( final TxFunction txFn, final InvocationRequest req, final ChaincodeStub stub) { LOGGER.fine(() -> "Routing Request" + txFn); final TxFunction.Routing rd = txFn.getRouting(); Chaincode.Response response; try { final ContractInterface contractObject = rd.getContractInstance(); final Context context = contractObject.createContext(stub); final List args = convertArgs(req.getArgs(), txFn); args.add(0, context); // force context into 1st position, other elements move up contractObject.beforeTransaction(context); final Object value = rd.getMethod().invoke(contractObject, args.toArray()); contractObject.afterTransaction(context, value); if (value == null) { response = ResponseUtils.newSuccessResponse(); } else { response = ResponseUtils.newSuccessResponse(convertReturn(value, txFn)); } } catch (IllegalAccessException | InstantiationException | NoSuchMethodException e) { final String message = String.format("Could not execute contract method: %s", rd.toString()); throw new ContractRuntimeException(message, e); } catch (final InvocationTargetException e) { final Throwable cause = e.getCause(); if (cause instanceof ChaincodeException) { response = ResponseUtils.newErrorResponse(cause); } else { throw new ContractRuntimeException("Error during contract method execution", cause); } } return response; } private byte[] convertReturn(final Object obj, final TxFunction txFn) { final SerializerInterface serializer = serializers.getSerializer(txFn.getRouting().getSerializerName(), Serializer.TARGET.TRANSACTION); final TypeSchema ts = txFn.getReturnSchema(); return serializer.toBuffer(obj, ts); } private List convertArgs(final List stubArgs, final TxFunction txFn) { final SerializerInterface serializer = serializers.getSerializer(txFn.getRouting().getSerializerName(), Serializer.TARGET.TRANSACTION); final List schemaParams = txFn.getParamsList(); final List args = new ArrayList<>(stubArgs.size() + 1); // allow for context as the first argument for (int i = 0; i < schemaParams.size(); i++) { args.add( i, serializer.fromBuffer(stubArgs.get(i), schemaParams.get(i).getSchema())); } return args; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractInvocationRequest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution.impl; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.contract.execution.InvocationRequest; import org.hyperledger.fabric.shim.ChaincodeStub; public final class ContractInvocationRequest implements InvocationRequest { @SuppressWarnings("PMD.ProperLogger") // PMD 7.12.0 gives a false positive here private static final Log LOGGER = LogFactory.getLog(ContractInvocationRequest.class); private static final Pattern NS_REGEX = Pattern.compile(":"); private final String namespace; private final String method; private final List args; /** @param context */ @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") public ContractInvocationRequest(final ChaincodeStub context) { List funcAndArgs = context.getArgs(); if (funcAndArgs.isEmpty()) { throw new IllegalArgumentException("Missing function name"); } final String func = new String(funcAndArgs.get(0), StandardCharsets.UTF_8); LOGGER.debug(func); final String[] funcParts = NS_REGEX.split(func); if (funcParts.length == 2) { namespace = funcParts[0]; method = funcParts[1]; } else { namespace = DEFAULT_NAMESPACE; method = funcParts[0]; } args = funcAndArgs.subList(1, funcAndArgs.size()); if (LOGGER.isDebugEnabled()) { LOGGER.debug(namespace + " " + method + " " + args); } } /** */ @Override public String getNamespace() { return namespace; } /** */ @Override public String getMethod() { return method; } /** */ @Override public List getArgs() { return args; } /** */ @Override public String getRequestName() { return namespace + ":" + method; } /** */ @Override public String toString() { return namespace + ":" + method + " @" + Integer.toHexString(System.identityHashCode(this)); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.execution.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.execution; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/MetadataBuilder.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.metadata; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.io.UncheckedIOException; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import org.everit.json.schema.Schema; import org.everit.json.schema.ValidationException; import org.everit.json.schema.loader.SchemaClient; import org.everit.json.schema.loader.SchemaLoader; import org.everit.json.schema.loader.internal.DefaultSchemaClient; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.DataTypeDefinition; import org.hyperledger.fabric.contract.routing.RoutingRegistry; import org.hyperledger.fabric.contract.routing.TransactionType; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.json.JSONObject; import org.json.JSONTokener; /** * Builder to assist in production of the metadata. * *

This class is used to build up the JSON structure to be returned as the metadata It is not a store of information, * rather a set of functional data to process to and from metadata json to the internal data structure */ @SuppressWarnings("PMD.AvoidDuplicateLiterals") public final class MetadataBuilder { private static final Logger LOGGER = Logger.getLogger(MetadataBuilder.class); private static final int PADDING = 3; // Metadata is composed of three primary sections // each of which is stored in a map private static Map> contractMap = new HashMap<>(); private static Map overallInfoMap = new HashMap<>(); private static Map componentMap = new HashMap<>(); // The schema client used to load any other referenced schemas private static SchemaClient schemaClient = new DefaultSchemaClient(); static final class MetadataMap extends HashMap { private static final long serialVersionUID = 1L; V putIfNotNull(final K key, final V value) { LOGGER.info(() -> key + " " + value); if (value != null && !value.toString().isEmpty()) { return put(key, value); } else { return null; } } } private MetadataBuilder() {} /** * Validation method. * * @throws ValidationException if the metadata is not valid */ public static void validate() { LOGGER.info("Running schema test validation"); final ClassLoader cl = MetadataBuilder.class.getClassLoader(); try (InputStream contractSchemaInputStream = cl.getResourceAsStream("contract-schema.json"); InputStream jsonSchemaInputStream = cl.getResourceAsStream("json-schema-draft-04-schema.json")) { final JSONObject rawContractSchema = new JSONObject(new JSONTokener(contractSchemaInputStream)); final JSONObject rawJsonSchema = new JSONObject(new JSONTokener(jsonSchemaInputStream)); final SchemaLoader schemaLoader = SchemaLoader.builder() .schemaClient(schemaClient) .schemaJson(rawContractSchema) .registerSchemaByURI(URI.create("http://json-schema.org/draft-04/schema"), rawJsonSchema) .build(); final Schema schema = schemaLoader.load().build(); schema.validate(metadata()); } catch (final IOException e) { throw new UncheckedIOException(e); } catch (final ValidationException e) { LOGGER.error(e::getMessage); e.getCausingExceptions().stream() .map(ValidationException::getMessage) .forEach(LOGGER::info); LOGGER.error(MetadataBuilder::debugString); throw e; } } /** * Setup the metadata from the found contracts. * * @param registry RoutingRegistry * @param typeRegistry TypeRegistry */ public static void initialize(final RoutingRegistry registry, final TypeRegistry typeRegistry) { final Collection contractDefinitions = registry.getAllDefinitions(); contractDefinitions.forEach(MetadataBuilder::addContract); final Collection dataTypes = typeRegistry.getAllDataTypes(); dataTypes.forEach(MetadataBuilder::addComponent); // need to validate that the metadata that has been created is really valid // it should be as it's been created by code, but this is a valuable double // check LOGGER.info("Validating schema created"); validate(); } /** * Adds a component/ complex data-type. * * @param datatype DataTypeDefinition */ public static void addComponent(final DataTypeDefinition datatype) { final Map component = new HashMap<>(); component.put("$id", datatype.getName()); component.put("type", "object"); component.put("additionalProperties", false); final Map propertiesMap = datatype.getProperties().entrySet().stream() .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().getSchema())); component.put("properties", propertiesMap); componentMap.put(datatype.getSimpleName(), component); } /** * Adds a new contract to the metadata as represented by the class object. * * @param contractDefinition Class of the object to use as a contract * @return the key that the contract class is referred to in the metadata */ @SuppressWarnings("PMD.LooseCoupling") public static String addContract(final ContractDefinition contractDefinition) { final String key = contractDefinition.getName(); final Contract annotation = contractDefinition.getAnnotation(); final Info info = annotation.info(); final HashMap infoMap = new HashMap<>(); infoMap.put("title", info.title()); infoMap.put("description", info.description()); infoMap.put("termsOfService", info.termsOfService()); MetadataMap contact = new MetadataMap<>(); contact.putIfNotNull("email", info.contact().email()); contact.putIfNotNull("name", info.contact().name()); contact.putIfNotNull("url", info.contact().url()); infoMap.put("contact", contact); MetadataMap license = new MetadataMap<>(); license.put("name", info.license().name()); license.putIfNotNull("url", info.license().url()); infoMap.put("license", license); infoMap.put("version", info.version()); final HashMap contract = new HashMap<>(); contract.put("name", key); contract.put("transactions", new ArrayList<>()); contract.put("info", infoMap); contractMap.put(key, contract); overallInfoMap.putAll(infoMap); final Collection fns = contractDefinition.getTxFunctions(); fns.forEach(txFn -> addTransaction(txFn, key)); return key; } /** * Adds a new transaction function to the metadata for the given contract. * * @param txFunction Object representing the transaction function * @param contractName Name of the contract that this function belongs to */ public static void addTransaction(final TxFunction txFunction, final String contractName) { final TypeSchema transaction = new TypeSchema(); final TypeSchema returnSchema = txFunction.getReturnSchema(); if (returnSchema != null) { transaction.put("returns", returnSchema); } final List tags = new ArrayList<>(); tags.add(txFunction.getType()); if (txFunction.getType() == TransactionType.SUBMIT) { // add deprecated tags tags.add(TransactionType.INVOKE); } else { tags.add(TransactionType.QUERY); } final Map contract = contractMap.get(contractName); @SuppressWarnings("unchecked") final List txs = (ArrayList) contract.get("transactions"); final List paramsList = new ArrayList<>(); txFunction.getParamsList().forEach(pd -> { final TypeSchema paramMap = pd.getSchema(); paramMap.put("name", pd.getName()); paramsList.add(paramMap); }); transaction.put("parameters", paramsList); if (!tags.isEmpty()) { transaction.put("tags", tags.toArray()); transaction.put("name", txFunction.getName()); txs.add(transaction); } } /** * Returns the metadata as a JSON string (compact). * * @return metadata as String */ public static String getMetadata() { return metadata().toString(); } /** * Returns the metadata as a JSON string (spaced out for humans). * * @return metadata as a spaced out string for humans */ public static String debugString() { return metadata().toString(PADDING); } /** * Create a JSONObject representing the schema. * * @return JSONObject of the metadata */ private static JSONObject metadata() { final Map metadata = new HashMap<>(); metadata.put("$schema", "https://fabric-shim.github.io/release-1.4/contract-schema.json"); metadata.put("info", overallInfoMap); metadata.put("contracts", contractMap); metadata.put("components", Collections.singletonMap("schemas", componentMap)); return new JSONObject(metadata); } /** @return All the components indexed by name */ public static Map getComponents() { return componentMap; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.metadata; import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.everit.json.schema.Schema; import org.everit.json.schema.ValidationException; import org.everit.json.schema.loader.SchemaLoader; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.json.JSONObject; /** * TypeSchema. * *

Custom sub-type of Map that helps with the case where if there's no value then do not insert the property at all * *

Does not include the "schema" top level map */ @SuppressWarnings("PMD.GodClass") public final class TypeSchema extends HashMap { private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(TypeSchema.class.getName()); private static final String SCHEMA_PROP = "schema"; private static final String TYPE_PROP = "type"; private static final String ITEMS_PROP = "items"; private static final String FORMAT_PROP = "format"; private static final String INTEGER_TYPE = "integer"; private Object putInternal(final String key, final Object value) { if (value != null && !value.toString().isEmpty()) { return put(key, value); } else { return null; } } String putIfNotNull(final String key, final String value) { return (String) this.putInternal(key, value); } String[] putIfNotNull(final String key, final String[] value) { return (String[]) this.putInternal(key, value); } TypeSchema putIfNotNull(final String key, final TypeSchema value) { return (TypeSchema) this.putInternal(key, value); } TypeSchema[] putIfNotNull(final String key, final TypeSchema[] value) { return (TypeSchema[]) this.putInternal(key, value); } /** @return Return Type String */ public String getType() { if (this.containsKey(SCHEMA_PROP)) { final Map intermediateMap = (Map) this.get(SCHEMA_PROP); return (String) intermediateMap.get(TYPE_PROP); } return (String) this.get(TYPE_PROP); } /** @return TypeSchema items */ public TypeSchema getItems() { if (this.containsKey(SCHEMA_PROP)) { final Map intermediateMap = (Map) this.get(SCHEMA_PROP); return (TypeSchema) intermediateMap.get(ITEMS_PROP); } return (TypeSchema) this.get(ITEMS_PROP); } /** @return Reference */ public String getRef() { if (this.containsKey(SCHEMA_PROP)) { final Map intermediateMap = (Map) this.get(SCHEMA_PROP); return (String) intermediateMap.get("$ref"); } return (String) this.get("$ref"); } /** @return Format */ public String getFormat() { if (this.containsKey(SCHEMA_PROP)) { final Map intermediateMap = (Map) this.get(SCHEMA_PROP); return (String) intermediateMap.get(FORMAT_PROP); } return (String) this.get(FORMAT_PROP); } /** * @param typeRegistry * @return Class object */ public Class getTypeClass(final TypeRegistry typeRegistry) { String type = Optional.ofNullable(getType()).orElse("object"); switch (type) { case "object": return getObjectClass(typeRegistry); case "string": return getStringClass(); case INTEGER_TYPE: return getIntegerClass(); case "number": return getNumberClass(); case "boolean": return boolean.class; case "array": return getArrayClass(typeRegistry); default: return null; } } private Class getArrayClass(final TypeRegistry typeRegistry) { final TypeSchema typdef = this.getItems(); final Class arrayType = typdef.getTypeClass(typeRegistry); return Array.newInstance(arrayType, 0).getClass(); } private Class getNumberClass() { switch (getFormat()) { case "double": return double.class; case "float": return float.class; default: throw new IllegalArgumentException("Unknown format for number of " + getFormat()); } } private Class getIntegerClass() { // need to check the format switch (getFormat()) { case "int8": return byte.class; case "int16": return short.class; case "int32": return int.class; case "int64": return long.class; default: throw new IllegalArgumentException("Unknown format for integer of " + getFormat()); } } @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") private Class getStringClass() { if ("uint16".equals(getFormat())) { return char.class; } return String.class; } private Class getObjectClass(final TypeRegistry typeRegistry) { final String ref = this.getRef(); final String format = ref.substring(ref.lastIndexOf('/') + 1); return typeRegistry.getDataType(format).getTypeClass(); } /** * Provide a mapping between the Java Language types and the OpenAPI based types. * * @param clz * @return TypeSchema */ @SuppressWarnings({"PMD.ReturnEmptyCollectionRatherThanNull", "PMD.AvoidLiteralsInIfCondition"}) public static TypeSchema typeConvert(final Class clz) { String className = clz.getTypeName(); if ("void".equals(className)) { return null; } final TypeSchema result = new TypeSchema(); TypeSchema schema = result; if (clz.isArray()) { result.put(TYPE_PROP, "array"); schema = new TypeSchema(); final Class componentClass = clz.getComponentType(); className = componentClass.getTypeName(); // double check the componentType if (componentClass.isArray()) { // nested arrays result.put(ITEMS_PROP, typeConvert(componentClass)); } else { result.put(ITEMS_PROP, schema); } } updateSchemaForClass(schema, className); return result; } @SuppressWarnings("PMD.CyclomaticComplexity") private static void updateSchemaForClass(final TypeSchema schema, final String className) { switch (className) { case "java.lang.String": schema.put(TYPE_PROP, "string"); return; case "char": case "java.lang.Character": schema.put(TYPE_PROP, "string"); schema.put(FORMAT_PROP, "uint16"); return; case "byte": case "java.lang.Byte": schema.put(TYPE_PROP, INTEGER_TYPE); schema.put(FORMAT_PROP, "int8"); return; case "short": case "java.lang.Short": schema.put(TYPE_PROP, INTEGER_TYPE); schema.put(FORMAT_PROP, "int16"); return; case "int": case "java.lang.Integer": schema.put(TYPE_PROP, INTEGER_TYPE); schema.put(FORMAT_PROP, "int32"); return; case "long": case "java.lang.Long": schema.put(TYPE_PROP, INTEGER_TYPE); schema.put(FORMAT_PROP, "int64"); return; case "double": case "java.lang.Double": schema.put(TYPE_PROP, "number"); schema.put(FORMAT_PROP, "double"); return; case "float": case "java.lang.Float": schema.put(TYPE_PROP, "number"); schema.put(FORMAT_PROP, "float"); return; case "boolean": case "java.lang.Boolean": schema.put(TYPE_PROP, "boolean"); return; default: schema.put("$ref", "#/components/schemas/" + className.substring(className.lastIndexOf('.') + 1)); } } /** * Validates the object against this schema. * * @param obj */ public void validate(final JSONObject obj) { // get the components bit of the main metadata final JSONObject toValidate = new JSONObject(); toValidate.put("prop", obj); JSONObject schemaJSON; if (this.containsKey(SCHEMA_PROP)) { schemaJSON = new JSONObject((Map) this.get(SCHEMA_PROP)); } else { schemaJSON = new JSONObject(this); } final JSONObject rawSchema = new JSONObject(); rawSchema.put("properties", new JSONObject().put("prop", schemaJSON)); rawSchema.put("components", new JSONObject().put("schemas", MetadataBuilder.getComponents())); final Schema schema = SchemaLoader.load(rawSchema); try { schema.validate(toValidate); } catch (final ValidationException e) { final StringBuilder sb = new StringBuilder("Validation Errors::"); e.getCausingExceptions().stream() .map(ValidationException::getMessage) .forEach(sb::append); String message = sb.toString(); LOGGER.info(message); throw new ContractRuntimeException(message, e); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.metadata; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Provides interfaces and classes to support the contract programming model. * *

The {@link org.hyperledger.fabric.contract} package implements the Fabric programming model as described in the Developing * Applications chapter of the Fabric documentation. * *

The main interface to implement is {@link org.hyperledger.fabric.contract.ContractInterface} */ package org.hyperledger.fabric.contract; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/ContractDefinition.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.lang.reflect.Method; import java.util.Collection; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; /** * Definition of the Contract * *

A data structure that represents the contract that will be executed in the chaincode. Primarily has * *

Name - either defined by the Contract annotation or the Class name (can be referred to as Namespace) Default - is * the default contract (defined by the Default annotation) TxFunctions in this contract do not need the name prefix * when invoked TxFunctions - the transaction functions defined in this contract * *

Will embedded the ContractInterface instance, as well as the annotation itself, and the routing for any tx * function that is unknown */ public interface ContractDefinition { /** @return the fully qualified name of the Contract */ String getName(); /** @return Complete collection of all the transaction functions in this contract */ Collection getTxFunctions(); /** @return Object reference to the instantiated object that is 'the contract' */ Class getContractImpl(); /** * @param m The java.lang.reflect object that is the method that is a tx function * @return TxFunction object representing this method */ TxFunction addTxFunction(Method m); /** @return if this is contract is the default one or not */ boolean isDefault(); /** * @param method name to be returned * @return TxFunction that represents this requested method */ TxFunction getTxFunction(String method); /** * @param method name to be checked * @return true if this txFunction exists or not */ boolean hasTxFunction(String method); /** @return The TxFunction to be used for this contract in case of unknown request */ TxFunction getUnknownRoute(); /** @return Underlying raw annotation */ Contract getAnnotation(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/DataTypeDefinition.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.util.Map; import org.hyperledger.fabric.contract.metadata.TypeSchema; public interface DataTypeDefinition { /** @return String */ String getName(); /** @return Map of String to PropertyDefinitions */ Map getProperties(); /** @return String */ String getSimpleName(); /** @return Class object of the type */ Class getTypeClass(); /** @return TypeSchema */ TypeSchema getSchema(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/ParameterDefinition.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.lang.reflect.Parameter; import org.hyperledger.fabric.contract.metadata.TypeSchema; public interface ParameterDefinition { /** @return Class type of the parameter */ Class getTypeClass(); /** @return TypeSchema of the parameter */ TypeSchema getSchema(); /** @return Parameter */ Parameter getParameter(); /** @return name of the parameter */ String getName(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/PropertyDefinition.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.lang.reflect.Field; import org.hyperledger.fabric.contract.metadata.TypeSchema; public interface PropertyDefinition { /** @return Class of the Property */ Class getTypeClass(); /** @return TypeSchema */ TypeSchema getSchema(); /** @return Field */ Field getField(); /** @return Name of the property */ String getName(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/RoutingRegistry.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.util.Collection; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.execution.InvocationRequest; public interface RoutingRegistry { /** * Add a new contract definition based on the class object located. * * @param clz Class Object to process into a ContractDefinition * @return ContractDefinition Instance */ ContractDefinition addNewContract(Class clz); /** * Based on the Invocation Request, can we create a route for this? * * @param request * @return ture/false */ boolean containsRoute(InvocationRequest request); /** * Get the route for invocation request. * * @param request * @return Routing obect */ TxFunction.Routing getRoute(InvocationRequest request); /** * Get the txFunction that matches the routing request. * * @param request * @return Transaction Function */ TxFunction getTxFn(InvocationRequest request); /** * Get the contract that matches the supplied name. * * @param name * @return Contract Definition */ ContractDefinition getContract(String name); /** * Returns all the ContractDefinitions for this registry. * * @return Collection of all definitions */ Collection getAllDefinitions(); /** * Locate all the contracts in this chaincode. * * @param typeRegistry */ void findAndSetContracts(TypeRegistry typeRegistry); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/TransactionType.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; public enum TransactionType { /** */ INVOKE, // deprecated /** */ QUERY, // deprecated /** */ DEFAULT, // deprecated /** */ SUBMIT, /** */ EVALUATE } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/TxFunction.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.metadata.TypeSchema; public interface TxFunction { interface Routing { /** * Method to route calls to the transaction function. * * @return a method. */ Method getMethod(); /** * The associated contract class. * * @return a contract class. */ Class getContractClass(); /** * The associated contract instance. * * @return a contract. * @throws IllegalAccessException * @throws InstantiationException * @throws InvocationTargetException * @throws NoSuchMethodException */ ContractInterface getContractInstance() throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; /** * Name of the serializer used for the transaction function. * * @return a serializer name. */ String getSerializerName(); } /** @return is this tx to be called when request fn is unknown */ boolean isUnknownTx(); /** @param unknown true if the transaction is to be called when the request fn is unknown; otherwise false. */ void setUnknownTx(boolean unknown); /** @return Name */ String getName(); /** @return Routing object */ Routing getRouting(); /** @return Class of the return type */ Class getReturnType(); /** @return Parameter array */ java.lang.reflect.Parameter[] getParameters(); /** @return Submit or Evaluate */ TransactionType getType(); /** @param returnSchema */ void setReturnSchema(TypeSchema returnSchema); /** @return TypeSchema of the return type */ TypeSchema getReturnSchema(); /** @param list */ void setParameterDefinitions(List list); /** @return List of parameters */ List getParamsList(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/TypeRegistry.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import java.util.Collection; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl; public interface TypeRegistry { /** @return TypeRegistry */ static TypeRegistry getRegistry() { return TypeRegistryImpl.getInstance(); } /** @param dtd */ void addDataType(DataTypeDefinition dtd); /** @param cl */ void addDataType(Class cl); /** * @param name * @return DataTypeDefinition */ DataTypeDefinition getDataType(String name); /** * @param schema * @return DataTypeDefinition */ DataTypeDefinition getDataType(TypeSchema schema); /** @return All datatypes */ Collection getAllDataTypes(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/ContractDefinitionImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.TxFunction; /** * Implementation of the ContractDefinition. * *

Contains information about the contract, including transaction functions and unknown transaction routing */ public final class ContractDefinitionImpl implements ContractDefinition { private static final Logger LOGGER = Logger.getLogger(ContractDefinitionImpl.class); private final Map txFunctions = new HashMap<>(); private final String name; private final boolean isDefault; private final Class contractClz; private final Contract contractAnnotation; private final TxFunction unknownTx; /** @param cl */ public ContractDefinitionImpl(final Class cl) { final Contract annotation = cl.getAnnotation(Contract.class); LOGGER.debug(() -> "Class Contract Annotation: " + annotation); final String annotationName = annotation.name(); if (annotationName == null || annotationName.isEmpty()) { this.name = cl.getSimpleName(); } else { this.name = annotationName; } isDefault = (cl.getAnnotation(Default.class) != null); contractAnnotation = cl.getAnnotation(Contract.class); contractClz = cl; try { final Method m = cl.getMethod("unknownTransaction", Context.class); unknownTx = new TxFunctionImpl(m, this); unknownTx.setUnknownTx(true); } catch (NoSuchMethodException | SecurityException e) { final ContractRuntimeException cre = new ContractRuntimeException("Failure to find unknownTransaction method", e); LOGGER.severe(() -> LOGGER.formatError(cre)); throw cre; } LOGGER.info(() -> "Found class: " + cl.getCanonicalName()); LOGGER.debug(() -> "Namespace: " + this.name); } @Override public String getName() { return name; } @Override public Collection getTxFunctions() { return txFunctions.values(); } @Override public Class getContractImpl() { return contractClz; } @Override public TxFunction addTxFunction(final Method m) { LOGGER.debug(() -> "Adding method " + m.getName()); final TxFunction txFn = new TxFunctionImpl(m, this); final TxFunction previousTxnFn = txFunctions.put(txFn.getName(), txFn); if (previousTxnFn != null) { final String message = String.format("Duplicate transaction method %s", previousTxnFn.getName()); final ContractRuntimeException cre = new ContractRuntimeException(message); LOGGER.severe(() -> LOGGER.formatError(cre)); throw cre; } return txFn; } @Override public boolean isDefault() { return isDefault; } @Override public TxFunction getTxFunction(final String method) { return txFunctions.get(method); } @Override public boolean hasTxFunction(final String method) { return txFunctions.containsKey(method); } @Override public TxFunction getUnknownRoute() { return unknownTx; } @Override public Contract getAnnotation() { return this.contractAnnotation; } @Override public String toString() { return name + ":" + txFunctions + " @" + Integer.toHexString(System.identityHashCode(this)); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/DataTypeDefinitionImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.stream.Stream; import org.hyperledger.fabric.contract.annotation.Property; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.DataTypeDefinition; import org.hyperledger.fabric.contract.routing.PropertyDefinition; public final class DataTypeDefinitionImpl implements DataTypeDefinition { private final Map properties = new HashMap<>(); private final String name; private final String simpleName; private final Class clazz; /** @param componentClass */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public DataTypeDefinitionImpl(final Class componentClass) { this.clazz = componentClass; this.name = componentClass.getName(); this.simpleName = componentClass.getSimpleName(); // given this class extract the property elements final Field[] fields = componentClass.getDeclaredFields(); for (final Field f : fields) { final Property propAnnotation = f.getAnnotation(Property.class); if (propAnnotation != null) { final TypeSchema ts = TypeSchema.typeConvert(f.getType()); // array of strings, "a","b","c","d" to become map of {a:b}, {c:d} final String[] userSupplied = propAnnotation.schema(); for (int i = 0; i < userSupplied.length; i += 2) { final String userKey = userSupplied[i]; Object userValue; switch (userKey.toLowerCase(Locale.getDefault())) { case "title": case "pattern": userValue = userSupplied[i + 1]; break; case "uniqueitems": userValue = Boolean.parseBoolean(userSupplied[i + 1]); break; case "required": case "enum": userValue = Stream.of(userSupplied[i + 1].split(",")) .map(String::trim) .toArray(String[]::new); break; default: userValue = Integer.parseInt(userSupplied[i + 1]); break; } ts.put(userKey, userValue); } final PropertyDefinition propDef = new PropertyDefinitionImpl(f.getName(), f.getClass(), ts, f); this.properties.put(f.getName(), propDef); } } } @Override public Class getTypeClass() { return this.clazz; } @Override public TypeSchema getSchema() { return TypeSchema.typeConvert(this.clazz); } /* * (non-Javadoc) * * @see org.hyperledger.fabric.contract.routing.DataTypeDefinition#getName() */ @Override public String getName() { return this.name; } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.DataTypeDefinition#getProperties() */ @Override public Map getProperties() { return properties; } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.DataTypeDefinition#getSimpleName() */ @Override public String getSimpleName() { return simpleName; } @Override public String toString() { return this.simpleName + " " + properties; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/ParameterDefinitionImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.lang.reflect.Parameter; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.ParameterDefinition; public final class ParameterDefinitionImpl implements ParameterDefinition { private final Class typeClass; private final TypeSchema schema; private final Parameter parameter; private final String name; /** * @param name * @param typeClass * @param schema * @param p */ public ParameterDefinitionImpl( final String name, final Class typeClass, final TypeSchema schema, final Parameter p) { this.typeClass = typeClass; this.schema = schema; this.parameter = p; this.name = name; } @Override public Class getTypeClass() { return this.typeClass; } @Override public TypeSchema getSchema() { return this.schema; } @Override public Parameter getParameter() { return this.parameter; } @Override public String getName() { return this.name; } @Override public String toString() { return this.name + "-" + this.typeClass + "-" + this.schema + "-" + this.parameter; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/PropertyDefinitionImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.lang.reflect.Field; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.PropertyDefinition; public final class PropertyDefinitionImpl implements PropertyDefinition { private final Class typeClass; private final TypeSchema schema; private final Field field; private final String name; /** * @param name * @param typeClass * @param schema * @param f */ public PropertyDefinitionImpl(final String name, final Class typeClass, final TypeSchema schema, final Field f) { this.typeClass = typeClass; this.schema = schema; this.field = f; this.name = name; } @Override public Class getTypeClass() { return this.typeClass; } @Override public TypeSchema getSchema() { return this.schema; } @Override public Field getField() { return this.field; } @Override public String getName() { return this.name; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/RoutingRegistryImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ScanResult; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.execution.InvocationRequest; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.RoutingRegistry; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.contract.routing.TypeRegistry; /** * Registry to hold permit access to the routing definitions. This is the primary internal data structure to permit * access to information about the contracts, and their transaction functions. * *

Contracts are added, and processed. At runtime, this can then be accessed to locate a specific 'Route' that can be * handed off to the ExecutionService */ public final class RoutingRegistryImpl implements RoutingRegistry { private static Logger logger = Logger.getLogger(RoutingRegistryImpl.class); private final Map contracts = new HashMap<>(); /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.RoutingRegistry#addNewContract(java. * lang.Class) */ @Override public ContractDefinition addNewContract(final Class clz) { logger.debug(() -> "Adding new Contract Class " + clz.getCanonicalName()); ContractDefinition contract; contract = new ContractDefinitionImpl(clz); // index this by the full qualified name contracts.put(contract.getName(), contract); if (contract.isDefault()) { contracts.put(InvocationRequest.DEFAULT_NAMESPACE, contract); } logger.debug(() -> "Put new contract in under name " + contract.getName()); return contract; } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.RoutingRegistry#containsRoute(org. * hyperledger.fabric.contract.execution.InvocationRequest) */ @Override public boolean containsRoute(final InvocationRequest request) { if (contracts.containsKey(request.getNamespace())) { final ContractDefinition cd = contracts.get(request.getNamespace()); if (cd.hasTxFunction(request.getMethod())) { return true; } } return false; } /* * (non-Javadoc) * * @see org.hyperledger.fabric.contract.routing.RoutingRegistry#getRoute(org. * hyperledger.fabric.contract.execution.InvocationRequest) */ @Override public TxFunction.Routing getRoute(final InvocationRequest request) { final TxFunction txFunction = contracts.get(request.getNamespace()).getTxFunction(request.getMethod()); return txFunction.getRouting(); } @Override public TxFunction getTxFn(final InvocationRequest request) { return contracts.get(request.getNamespace()).getTxFunction(request.getMethod()); } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.RoutingRegistry#getContract(java.lang * .String) */ @Override public ContractDefinition getContract(final String namespace) { final ContractDefinition contract = contracts.get(namespace); if (contract == null) { throw new ContractRuntimeException("Undefined contract called"); } return contract; } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.RoutingRegistry#getAllDefinitions() */ @Override public Collection getAllDefinitions() { return contracts.values(); } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.RoutingRegistry#findAndSetContracts() */ @Override @SuppressWarnings("unchecked") public void findAndSetContracts(final TypeRegistry typeRegistry) { // Find all classes that are valid contract or data type instances. final ClassGraph classGraph = new ClassGraph().enableClassInfo().enableAnnotationInfo(); final List> contractClasses = new ArrayList<>(); final List> dataTypeClasses = new ArrayList<>(); try (ScanResult scanResult = classGraph.scan()) { for (final ClassInfo classInfo : scanResult.getClassesWithAnnotation(Contract.class.getCanonicalName())) { logger.debug(() -> "Found class with contract annotation: " + classInfo.getName()); try { final Class contractClass = classInfo.loadClass(); logger.debug("Loaded class"); final Contract annotation = contractClass.getAnnotation(Contract.class); if (annotation == null) { // Since we check by name above, it makes sense to check it's actually // compatible, // and not some random class with the same name. logger.debug("Class does not have compatible contract annotation"); } else if (ContractInterface.class.isAssignableFrom(contractClass)) { logger.debug("Class is assignable from ContractInterface"); contractClasses.add((Class) contractClass); } else { logger.debug("Class is not assignable from ContractInterface"); } } catch (final IllegalArgumentException e) { logger.debug(() -> "Failed to load class: " + e); } } for (final ClassInfo classInfo : scanResult.getClassesWithAnnotation(DataType.class.getCanonicalName())) { logger.debug(() -> "Found class with data type annotation: " + classInfo.getName()); try { final Class dataTypeClass = classInfo.loadClass(); logger.debug("Loaded class"); final DataType annotation = dataTypeClass.getAnnotation(DataType.class); if (annotation == null) { // Since we check by name above, it makes sense to check it's actually // compatible, // and not some random class with the same name. logger.debug("Class does not have compatible data type annotation"); } else { logger.debug("Class has compatible data type annotation"); dataTypeClasses.add(dataTypeClass); } } catch (final IllegalArgumentException e) { logger.debug(() -> "Failed to load class: " + e); } } } // store the contracts in the internal data structures addContracts(contractClasses); // now need to look for the data types have been set with the dataTypeClasses.forEach(typeRegistry::addDataType); } private void addContracts(final List> contractClasses) { // set to ensure that we don't scan the same class twice final Set seenClass = new HashSet<>(); // loop over all the classes that have the Contract annotation for (final Class contractClass : contractClasses) { final String className = contractClass.getCanonicalName(); if (!seenClass.contains(className)) { final ContractDefinition contract = addNewContract(contractClass); logger.debug("Searching annotated methods"); for (final Method m : contractClass.getMethods()) { if (m.getAnnotation(Transaction.class) != null) { logger.debug(() -> "Found annotated method " + m.getName()); contract.addTxFunction(m); } } seenClass.add(className); } } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/SerializerRegistryImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ScanResult; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.annotation.Serializer; import org.hyperledger.fabric.contract.execution.SerializerInterface; /** * Registry to hold permit access to the serializer implementations. * *

It holds the serializers that have been defined. JSONTransactionSerializer is the default. */ public class SerializerRegistryImpl { private static final Logger LOGGER = Logger.getLogger(SerializerRegistryImpl.class); private static final Class ANNOTATION_CLASS = Serializer.class; // Could index these by name and or type. private final Map contents = new HashMap<>(); /** * Get a Serializer for the matching fully qualified classname, and the Target. * * @param name fully qualified classname * @param target the intended target of the serializer * @return Serializer instance */ public SerializerInterface getSerializer(final String name, final Serializer.TARGET target) { final String key = name + ":" + target; return contents.get(key); } private void add(final String name, final Serializer.TARGET target, final Class clazz) throws InstantiationException, IllegalAccessException { LOGGER.debug(() -> "Adding new Class " + clazz.getCanonicalName() + " for " + target); final String key = name + ":" + target; try { final SerializerInterface newObj = (SerializerInterface) clazz.getDeclaredConstructor().newInstance(); this.contents.put(key, newObj); } catch (InvocationTargetException | NoSuchMethodException e) { InstantiationException wrapper = new InstantiationException( "Exception constructing " + clazz.getCanonicalName() + ": " + e.getMessage()); wrapper.addSuppressed(e); throw wrapper; } } /** * Find all the serializers that have been defined. * * @see org.hyperledger.fabric.contract.routing.RoutingRegistry#findAndSetContracts(TypeRegistry) * @throws IllegalAccessException * @throws InstantiationException */ public void findAndSetContents() throws InstantiationException, IllegalAccessException { final ClassGraph classGraph = new ClassGraph().enableClassInfo().enableAnnotationInfo(); // set to ensure that we don't scan the same class twice final Set seenClass = new HashSet<>(); try (ScanResult scanResult = classGraph.scan()) { for (final ClassInfo classInfo : scanResult.getClassesWithAnnotation(ANNOTATION_CLASS.getCanonicalName())) { LOGGER.debug(() -> "Found class with contract annotation: " + classInfo.getName()); final Class cls = classInfo.loadClass(); LOGGER.debug("Loaded class"); final String className = cls.getCanonicalName(); if (!seenClass.contains(className)) { seenClass.add(className); this.add(className, Serializer.TARGET.TRANSACTION, cls); } } } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/TxFunctionImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.hyperledger.fabric.Logger; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Property; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.ParameterDefinition; import org.hyperledger.fabric.contract.routing.TransactionType; import org.hyperledger.fabric.contract.routing.TxFunction; public final class TxFunctionImpl implements TxFunction { private static final Logger LOGGER = Logger.getLogger(TxFunctionImpl.class); private final Method method; private String name; private TransactionType type; private final Routing routing; private TypeSchema returnSchema; private List paramsList; private boolean isUnknownTx; public static final class RoutingImpl implements Routing { private final Method method; private final Class clazz; private final String serializerName; /** * @param method * @param contract */ public RoutingImpl(final Method method, final ContractDefinition contract) { this.method = method; this.clazz = contract.getContractImpl(); this.serializerName = contract.getAnnotation().transactionSerializer(); } @Override public Method getMethod() { return method; } @Override public Class getContractClass() { return clazz; } @Override public ContractInterface getContractInstance() throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { return clazz.getDeclaredConstructor().newInstance(); } @Override public String toString() { return method.getName() + ":" + clazz.getCanonicalName(); } @Override public String getSerializerName() { return serializerName; } } /** * New TxFunction Definition Impl. * * @param m Reflect method object * @param contract ContractDefinition this is part of */ public TxFunctionImpl(final Method m, final ContractDefinition contract) { this.method = m; if (m.getAnnotation(Transaction.class) != null) { LOGGER.debug(() -> "Found Transaction method: " + m.getName()); if (m.getAnnotation(Transaction.class).intent() == Transaction.TYPE.SUBMIT) { this.type = TransactionType.SUBMIT; } else { this.type = TransactionType.EVALUATE; } final String txnName = m.getAnnotation(Transaction.class).name(); if (!txnName.isEmpty()) { this.name = txnName; } } if (name == null) { this.name = m.getName(); } // create the routing object that defines how to get the data to the transaction // function. this.routing = new RoutingImpl(m, contract); // set the return schema this.returnSchema = TypeSchema.typeConvert(m.getReturnType()); // parameter processing this.paramsList = buildParameters(m); } private List buildParameters(final Method m) { Parameter[] params = m.getParameters(); // validate the first one is a context object if (params.length == 0) { throw new ContractRuntimeException("First argument should be of type Context"); } if (!Context.class.isAssignableFrom(params[0].getType())) { throw new ContractRuntimeException( "First argument should be of type Context " + m.getName() + " " + params[0].getType()); } // FUTURE: if ever the method of creating the instance where to change, // the routing could be changed here, a different implementation could be made // here encapsulating the change. eg use an annotation to define where the // context goes return Arrays.stream(params) .skip(1) .map(TxFunctionImpl::newParameterDefinition) .collect(Collectors.toList()); } private static ParameterDefinitionImpl newParameterDefinition(final Parameter parameter) { final TypeSchema paramMap = new TypeSchema(); final TypeSchema schema = TypeSchema.typeConvert(parameter.getType()); final Property annotation = parameter.getAnnotation(Property.class); if (annotation != null) { final String[] userSupplied = annotation.schema(); for (int i = 0; i < userSupplied.length; i += 2) { schema.put(userSupplied[i], userSupplied[i + 1]); } } paramMap.put("name", parameter.getName()); paramMap.put("schema", schema); return new ParameterDefinitionImpl(parameter.getName(), parameter.getClass(), paramMap, parameter); } @Override public String getName() { return name; } @Override public Routing getRouting() { return this.routing; } @Override public Class getReturnType() { return method.getReturnType(); } @Override public Parameter[] getParameters() { return method.getParameters(); } @Override public TransactionType getType() { return this.type; } @Override public String toString() { return name + " @" + Integer.toHexString(System.identityHashCode(this)); } @Override public void setReturnSchema(final TypeSchema returnSchema) { this.returnSchema = returnSchema; } @Override public List getParamsList() { return paramsList; } /** @param paramsList */ public void setParamsList(final List paramsList) { this.paramsList = paramsList; } @Override public TypeSchema getReturnSchema() { return returnSchema; } @Override public void setParameterDefinitions(final List list) { this.paramsList = list; } @Override public boolean isUnknownTx() { return isUnknownTx; } @Override public void setUnknownTx(final boolean unknown) { this.isUnknownTx = unknown; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/TypeRegistryImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing.impl; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.DataTypeDefinition; import org.hyperledger.fabric.contract.routing.TypeRegistry; /** Registry to hold the complex data types as defined in the contract. */ public final class TypeRegistryImpl implements TypeRegistry { private static final TypeRegistryImpl INSTANCE = new TypeRegistryImpl(); private final Map components = new HashMap<>(); /** * Get the TypeRegistry singleton instance. * * @return TypeRegistry */ public static TypeRegistry getInstance() { return INSTANCE; } /* * (non-Javadoc) * * @see * org.hyperledger.fabric.contract.routing.TypeRegistry#addDataType(java.lang. * Class) */ @Override public void addDataType(final Class cl) { final DataTypeDefinitionImpl type = new DataTypeDefinitionImpl(cl); components.put(type.getSimpleName(), type); } /* * (non-Javadoc) * * @see org.hyperledger.fabric.contract.routing.TypeRegistry#getAllDataTypes() */ @Override public Collection getAllDataTypes() { return components.values(); } @Override public void addDataType(final DataTypeDefinition type) { components.put(type.getName(), type); } @Override public DataTypeDefinition getDataType(final String name) { return this.components.get(name); } @Override public DataTypeDefinition getDataType(final TypeSchema schema) { final String ref = schema.getRef(); final String format = ref.substring(ref.lastIndexOf('/') + 1); return getDataType(format); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/impl/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.routing.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.routing; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/systemcontract/SystemContract.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.systemcontract; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.metadata.MetadataBuilder; /** */ @Contract( name = "org.hyperledger.fabric", info = @Info( title = "Fabric System Contract", description = "Provides information about the contracts within this container")) public final class SystemContract implements ContractInterface { /** * @param ctx * @return Metadata */ @Transaction(intent = Transaction.TYPE.EVALUATE, name = "GetMetadata") public String getMetadata(final Context ctx) { return MetadataBuilder.getMetadata(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/systemcontract/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.contract.systemcontract; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Collection.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.ledger; /** Place holder. */ @SuppressWarnings("PMD.ImplicitFunctionalInterface") public interface Collection { /** Constant that can be used to refer to the 'Worldstate' collection explicitly. */ String WORLD = "worldstate"; /** * Placeholder. Purely in place to prevent Checkstyle inferring this class is pointless. will be removed in the next * story */ void placeholder(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/Ledger.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.ledger; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.ledger.impl.LedgerImpl; /** * Ledger representing the overall shared Transaction Data of the Network. * *

It is composed of a number of collections, one being the public or world state, and other private data * collections, including the implicit organizational collections. * *

Ledger objects can be passed between methods if required. All operations on the Ledger directly or via any child * object such as a Collection will be controlled by the supplied transactional context. */ public interface Ledger { /** * Get the Ledger instance that represents the current ledger state. * *

Any interactions with the ledger will be done under the control of the transactional context supplied. The * ledger object may be passed to other methods if required. * *

A new instance is returned on each call. * * @param ctx Context The Transactional context to use for interactions with this ledger * @return Ledger instance */ static Ledger getLedger(final Context ctx) { return new LedgerImpl(ctx); } /** * Return the a collection based on the supplied name. * *

Private Data collections can be accessed by name. * *

A new instance of a Collection object is returned on each call. * * @param name * @return Collection instance */ Collection getCollection(String name); /** * Return the World State collection. * *

A new instance of a Collection object is returned on each call. * * @return Collection instance */ Collection getDefaultCollection(); /** * Return a implicit organization collection. * *

Given the mspid of the ogranization return the private data collection that is implicitly created * *

A new instance of a Collection object is returned on each call. * * @param mspid String Organization's mspid * @return Collection instance */ Collection getOrganizationCollection(String mspid); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/impl/LedgerImpl.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.ledger.impl; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.ledger.Collection; import org.hyperledger.fabric.ledger.Ledger; public final class LedgerImpl implements Ledger { /** * New Ledger Implementation. * * @param ctx Context transactional context to use */ public LedgerImpl(final Context ctx) { // Empty stub } @Override public Collection getCollection(final String name) { return new Collection() { @Override public void placeholder() { // Empty stub } }; } @Override public Collection getDefaultCollection() { return this.getCollection(Collection.WORLD); } @Override public Collection getOrganizationCollection(final String mspid) { return this.getCollection("_implicit_org_" + mspid); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/impl/package-info.java ================================================ /* * Copyright 2023 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.ledger.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/ledger/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** Provides the API for contracts to access the shared ledger. */ package org.hyperledger.fabric.ledger; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/Metrics.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics; import java.util.Properties; import java.util.logging.Logger; import org.hyperledger.fabric.metrics.impl.DefaultProvider; import org.hyperledger.fabric.metrics.impl.NullProvider; /** * Metrics Interface. * *

Metrics setups up the provider in use from the configuration supplied If not enabled, nothing happens, but if * enabled but no specific logger default is used that uses the org.hyperledger.Performance logger */ public final class Metrics { private static final String CHAINCODE_METRICS_ENABLED = "CHAINCODE_METRICS_ENABLED"; private static final String CHAINCODE_METRICS_PROVIDER = "CHAINCODE_METRICS_PROVIDER"; private static Logger logger = Logger.getLogger(Metrics.class.getName()); private static MetricsProvider provider; private Metrics() {} /** * @param props * @return The metrics provide */ @SuppressWarnings("PMD.AvoidCatchingGenericException") public static MetricsProvider initialize(final Properties props) { if (Boolean.parseBoolean((String) props.get(CHAINCODE_METRICS_ENABLED))) { try { logger.info("Metrics enabled"); if (props.containsKey(CHAINCODE_METRICS_PROVIDER)) { final String providerClass = (String) props.get(CHAINCODE_METRICS_PROVIDER); @SuppressWarnings("unchecked") // it must be this type otherwise an error final Class clazz = (Class) Class.forName(providerClass); provider = clazz.getConstructor().newInstance(); } else { logger.info("Using default metrics provider (logs to org.hyperledger.Performance)"); provider = new DefaultProvider(); } } catch (Exception e) { throw new IllegalStateException("Unable to start metrics", e); } } else { // return a 'null' provider logger.info("Metrics disabled"); provider = new NullProvider(); } provider.initialize(props); return provider; } /** @return MetricsProvider */ public static MetricsProvider getProvider() { if (provider == null) { throw new IllegalStateException("No provider set, this should have been set"); } return provider; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/MetricsProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics; import java.util.Properties; /** * Interface to be implemented to send metrics on the chaincode to the 'backend-of-choice'. * *

An instance of this will be created, and provided with the resources from which chaincode specific metrics can be * collected. (via the no-argument constructor). * *

The choice of when, where and what to collect etc are within the remit of the provider. * *

This is the effective call sequence. * *

MyMetricsProvider mmp = new MyMetricsProvider() mmp.initialize(props_from_environment); // short while later.... * mmp.setTaskMetricsCollector(taskService); */ public interface MetricsProvider { /** * Initialize method that is called immediately after creation. * * @param props */ default void initialize(final Properties props) { // Do nothing by default } /** * Pass a reference to this task service for information gathering. This is related specifically to the handling of * tasks within the chaincode. i.e. how individual transactions are dispatched for execution. * * @param taskService */ default void setTaskMetricsCollector(final TaskMetricsCollector taskService) { // Do nothing by default } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/TaskMetricsCollector.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics; /** * Collect metrics relating to the task execution. * *

The task execution (of which each fabric transaction is one) is backed by a thread pool that implements this * interface. As that is an implementation class this interface abstracts the information available from it (as far as * metrics go). */ public interface TaskMetricsCollector { /** * Currently executing tasks. * * @return int > 0 */ int getCurrentTaskCount(); /** * Currently waiting tasks; should not be a higher number. * * @return int > 0 */ int getCurrentQueueCount(); /** * Currently executing threads. * * @return int > 0 */ int getActiveCount(); /** * Gets the current size of the pool. * * @return int > 0 */ int getPoolSize(); /** * Gets the core (minimum) pool size. * * @return int > 0 */ int getCorePoolSize(); /** * Gets the largest pool size so far. * * @return int > 0 */ int getLargestPoolSize(); /** * Gets the upper limit pool size. * * @return int > 0 */ int getMaximumPoolSize(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/impl/DefaultProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics.impl; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.metrics.MetricsProvider; import org.hyperledger.fabric.metrics.TaskMetricsCollector; /** Simple default provider that logs to the org.hyperledger.Performance logger the basic metrics. */ public final class DefaultProvider implements MetricsProvider { private static final Logger PERFLOGGER = Logger.getLogger(Logging.PERFLOGGER); private static final int TIME_INTERVAL = 5000; private TaskMetricsCollector taskService; /** */ public DefaultProvider() { PERFLOGGER.info("Default Metrics Provider started"); } @Override public void setTaskMetricsCollector(final TaskMetricsCollector taskService) { this.taskService = taskService; } @Override public void initialize(final Properties props) { final Timer metricTimer = new Timer(true); metricTimer.scheduleAtFixedRate( new TimerTask() { @Override public void run() { DefaultProvider.this.logMetrics(); } }, 0, TIME_INTERVAL); } void logMetrics() { PERFLOGGER.info(() -> { if (taskService == null) { return "No Metrics Provider service yet"; } return '{' + String.format(" \"active_count\":%d ", taskService.getActiveCount()) + ',' + String.format(" \"pool_size\":%d ", taskService.getPoolSize()) + ',' + String.format(" \"core_pool_size\":%d ", taskService.getCorePoolSize()) + ',' + String.format(" \"current_task_count\":%d ", taskService.getCurrentTaskCount()) + ',' + String.format(" \"current_queue_depth\":%d ", taskService.getCurrentQueueCount()) + '}'; }); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/impl/NullProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics.impl; import org.hyperledger.fabric.metrics.MetricsProvider; /** Very simple provider that does absolutely nothing. Used when metrics are disabled. */ public class NullProvider implements MetricsProvider {} ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/impl/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.metrics.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/metrics/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Provides interfaces and classes to support collection of metrics. * *

The main metrics that are available are the statistics around the number of tasks that are running, and how the * thread pool is handling these. * *

Note a 'task' is a message from the Peer to the Chaincode - this message is either a new transaction, or a * response from a stub API, eg getState(). Query apis may return more than one response. * *

To enable metrics, add a CHAINCODE_METRICS_ENABLED=true setting to the config.props * chaincode configuration file. See the Overview for details of how to configure * chaincode. * *

Open Telemetry To use Open Telemetry, set the following properties: * *

 * CHAINCODE_METRICS_ENABLED=true
 * CHAINCODE_METRICS_PROVIDER=org.hyperledger.fabric.metrics.impl.OpenTelemetryMetricsProvider
 * 
* * Additionally, you can set properties after the specification: * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md * *

Example: * *

 * OTEL_EXPORTER_OTLP_ENDPOINT=otelcollector:4317
 * OTEL_EXPORTER_OTLP_INSECURE=true
 * 
*/ package org.hyperledger.fabric.metrics; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/overview.html ================================================

This is the project to support the writing of Contracts with the JVM runtime - enabling development of using Java language or other JVM based languages

The Fabric programming model is described in the Developing Applications chapter of the Hyperledger Fabric documentation.

All Contracts should implement {@link org.hyperledger.fabric.contract.ContractInterface} interface, in addition to the {@link org.hyperledger.fabric.contract.annotation.Contract} annotation.

Configuration

Some Java chaincode settings are configurable on a per chaincode basis, including the maximum gRPC inbound message size, thread pool settings, and whether to enable chaincode metrics. The default settings are as follows:

      MAX_INBOUND_MESSAGE_SIZE=104857600
      CHAINCODE_METRICS_ENABLED=false
      TP_CORE_POOL_SIZE=5
      TP_MAX_POOL_SIZE=5
      TP_QUEUE_SIZE=5000
    

To override the defaults, a config.props Java properties file must be included in the default, unnamed, package on your contract code's classpath, which will then be loaded when the chaincode starts. Setting the metrics enabled flag to true will turn on default metrics logging. The TP_ values establish the core thread pool size, max thread poolsize, and the number of of tasks that will wait.

Open Telemetry

To use Open Telemetry, set the following properties:

      CHAINCODE_METRICS_ENABLED=true
      CHAINCODE_METRICS_PROVIDER=org.hyperledger.fabric.metrics.impl.OpenTelemetryMetricsProvider
    

Additionally, you can set properties after the OpenTelemetry specification.
Example:

      OTEL_EXPORTER_OTLP_ENDPOINT=otelcollector:4317
      OTEL_EXPORTER_OTLP_INSECURE=true
    

If you are building your chaincode using Gradle or Maven, create a config.props file in the src/main/resources directory and include the settings you want to override.

================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** Provides logging classes. */ package org.hyperledger.fabric; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/Chaincode.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.HashMap; import java.util.Map; /** Defines methods that all chaincodes must implement. */ public interface Chaincode { /** * Called during an instantiate transaction after the container has been established, allowing the chaincode to * initialize its internal data. * * @param stub the chaincode stub * @return the chaincode response */ Response init(ChaincodeStub stub); /** * Called for every Invoke transaction. The chaincode may change its state variables. * * @param stub the chaincode stub * @return the chaincode response */ Response invoke(ChaincodeStub stub); /** * Wrapper around protobuf Response, contains status, message and payload. Object returned by call to * {@link #init(ChaincodeStub)} and{@link #invoke(ChaincodeStub)} */ class Response { private final int statusCode; private final String message; private final byte[] payload; /** * Constructor. * * @param status a status object. * @param message a response message. * @param payload a response payload. */ @SuppressWarnings("PMD.ArrayIsStoredDirectly") public Response(final Status status, final String message, final byte[] payload) { this.statusCode = status.getCode(); this.message = message; this.payload = payload; } /** * Constructor. * * @param statusCode a status code. * @param message a response message. * @param payload a response payload. */ @SuppressWarnings("PMD.ArrayIsStoredDirectly") public Response(final int statusCode, final String message, final byte[] payload) { this.statusCode = statusCode; this.message = message; this.payload = payload; } /** * Get the response status. * * @return status. */ public Status getStatus() { if (Status.hasStatusForCode(statusCode)) { return Status.forCode(statusCode); } else { return null; } } /** * Get the response status code. * * @return status code. */ public int getStatusCode() { return statusCode; } /** * Get the response message. * * @return a message. */ public String getMessage() { return message; } /** * Get the response payload. * * @return payload bytes. */ @SuppressWarnings("PMD.MethodReturnsInternalArray") public byte[] getPayload() { return payload; } /** * Get the response payload as a UTF-8 string. * * @return a string. */ public String getStringPayload() { return (payload == null) ? null : new String(payload, UTF_8); } /** {@link Response} status enum. */ public enum Status { /** Successful response status. */ SUCCESS(200), /** Minimum threshold for as error status code. */ ERROR_THRESHOLD(400), /** Server-side error status. */ INTERNAL_SERVER_ERROR(500); private static final Map CODETOSTATUS = new HashMap<>(); private final int code; Status(final int code) { this.code = code; } /** * Get the status code associated with this status object. * * @return a status code. */ public int getCode() { return code; } /** * Get a status object for a given status code. * * @param code a status code. * @return a status object. */ public static Status forCode(final int code) { final Status result = CODETOSTATUS.get(code); if (result == null) { throw new IllegalArgumentException("no status for code " + code); } return result; } /** * Whether a status exists for a given status code. * * @param code a status code. * @return True if a status for the code exists; otherwise false. */ public static boolean hasStatusForCode(final int code) { return CODETOSTATUS.containsKey(code); } static { for (final Status status : values()) { CODETOSTATUS.put(status.code, status); } } } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeBase.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static java.util.logging.Level.ALL; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; import io.grpc.ManagedChannelBuilder; import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; import io.grpc.stub.StreamObserver; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.Security; import java.util.Base64; import java.util.Locale; import java.util.Properties; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.contract.ContractRouter; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.shim.impl.ChaincodeSupportClient; import org.hyperledger.fabric.shim.impl.InvocationTaskManager; import org.hyperledger.fabric.traces.Traces; /** * Abstract implementation of {@link Chaincode}. * *

All chaincode implementations must extend the abstract class ChaincodeBase. It is possible to * implement chaincode by extending ChaincodeBase directly however new projects should implement * {@link org.hyperledger.fabric.contract.ContractInterface} and use the contract programming model instead. * * @see org.hyperledger.fabric.contract */ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.GodClass"}) public abstract class ChaincodeBase implements Chaincode { /** */ public static final String CORE_CHAINCODE_LOGGING_SHIM = "CORE_CHAINCODE_LOGGING_SHIM"; /** */ public static final String CORE_CHAINCODE_LOGGING_LEVEL = "CORE_CHAINCODE_LOGGING_LEVEL"; private static final Logger LOGGER = Logger.getLogger(ChaincodeBase.class.getName()); /** */ @SuppressWarnings("PMD.AvoidUsingHardCodedIP") public static final String DEFAULT_HOST = "127.0.0.1"; /** */ public static final int DEFAULT_PORT = 7051; /** Default to 100MB for maximum inbound grpc message size. */ public static final String DEFAULT_MAX_INBOUND_MESSAGE_SIZE = "104857600"; private String host = DEFAULT_HOST; private int port = DEFAULT_PORT; private boolean tlsEnabled; private String tlsClientKeyPath; private String tlsClientCertPath; private String tlsClientKeyFile; private String tlsClientCertFile; private String tlsClientRootCertPath; private String id; private String localMspId = ""; private String chaincodeServerAddress = ""; private static final String CHAINCODE_SERVER_ADDRESS = "CHAINCODE_SERVER_ADDRESS"; private static final String CORE_CHAINCODE_ID_NAME = "CORE_CHAINCODE_ID_NAME"; private static final String CORE_PEER_ADDRESS = "CORE_PEER_ADDRESS"; private static final String CORE_PEER_TLS_ENABLED = "CORE_PEER_TLS_ENABLED"; private static final String CORE_PEER_TLS_ROOTCERT_FILE = "CORE_PEER_TLS_ROOTCERT_FILE"; private static final String ENV_TLS_CLIENT_KEY_PATH = "CORE_TLS_CLIENT_KEY_PATH"; private static final String ENV_TLS_CLIENT_CERT_PATH = "CORE_TLS_CLIENT_CERT_PATH"; private static final String ENV_TLS_CLIENT_KEY_FILE = "CORE_TLS_CLIENT_KEY_FILE"; private static final String ENV_TLS_CLIENT_CERT_FILE = "CORE_TLS_CLIENT_CERT_FILE"; private static final String CORE_PEER_LOCALMSPID = "CORE_PEER_LOCALMSPID"; private static final String MAX_INBOUND_MESSAGE_SIZE = "MAX_INBOUND_MESSAGE_SIZE"; private Properties props; private Level logLevel; private CCState state = CCState.CREATED; static { Security.addProvider(new BouncyCastleProvider()); } @Override public abstract Response init(ChaincodeStub stub); @Override public abstract Response invoke(ChaincodeStub stub); private int getMaxInboundMessageSize() { if (this.props == null) { throw new IllegalStateException("Chaincode config not available"); } final int maxMsgSize = Integer.parseInt(this.props.getProperty(MAX_INBOUND_MESSAGE_SIZE, DEFAULT_MAX_INBOUND_MESSAGE_SIZE)); final String msgSizeInfo = String.format("Maximum Inbound Message Size [%s] = %d", MAX_INBOUND_MESSAGE_SIZE, maxMsgSize); LOGGER.info(msgSizeInfo); return maxMsgSize; } /** * Start chaincode. * * @param args command line arguments */ @SuppressWarnings("PMD.AvoidCatchingGenericException") public void start(final String[] args) { try { initializeLogging(); processEnvironmentOptions(); processCommandLineOptions(args); validateOptions(); final Properties props = getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); connectToPeer(); } catch (final Exception e) { LOGGER.severe(() -> "Chaincode could not start" + Logging.formatError(e)); } } protected final void connectToPeer() throws IOException { // The ChaincodeSupport Client is a wrapper around the gRPC streams that // come from the single 'register' call that is made back to the peer // // Once this has been created, the InvocationTaskManager that is responsible // for the thread management can be created. // // This is then passed to the ChaincodeSupportClient to be connected to the // gRPC streams final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName(this.id).build(); final ManagedChannelBuilder channelBuilder = newChannelBuilder(); final ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(channelBuilder); final InvocationTaskManager itm = InvocationTaskManager.getManager(this, chaincodeId); // This is a critical method - it is the one time that a // protobuf service is invoked. The single 'register' call // is made, and two streams are created. // // It is confusing how these streams are then used to send messages // to and from the peer. // // the response stream is the message flow FROM the peer // the 'request observer' is the message flow TO the peer // // Messages coming from the peer will be requests to invoke // chaincode, or will be the responses to stub APIs, such as getState // Message to the peer will be the getState APIs, and the results of // transaction invocations // The InnvocationTaskManager's way of being told there is a new // message, until this is received and processed there is now // knowing if this is a new transaction function or the answer to say getState LOGGER.info("making the grpc call"); // for any error - shut everything down // as this is long lived (well forever) then any completion means something // has stopped in the peer or the network comms, so also shutdown final StreamObserver requestObserver = chaincodeSupportClient .getStub() .register(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage chaincodeMessage) { // message off to the ITM... itm.onChaincodeMessage(chaincodeMessage); } @Override public void onError(final Throwable t) { LOGGER.severe( () -> "An error occurred on the chaincode stream. Shutting down the chaincode stream." + Logging.formatError(t)); chaincodeSupportClient.shutdown(itm); } @Override public void onCompleted() { LOGGER.severe("Chaincode stream is complete. Shutting down the chaincode stream."); chaincodeSupportClient.shutdown(itm); } }); chaincodeSupportClient.start(itm, requestObserver); } /** * connect external chaincode to peer for chat. * * @param requestObserver reqeust from peer * @return itm - The InnvocationTask Manager handles the message level communication with the peer. * @throws IOException validation fields exception */ protected StreamObserver connectToPeer(final StreamObserver requestObserver) throws IOException { validateOptions(); if (requestObserver == null) { throw new IOException("StreamObserver 'requestObserver' for chat with peer can't be null"); } // The ChaincodeSupport Client is a wrapper around the gRPC streams that // come from the single 'register' call that is made back to the peer // // Once this has been created, the InnvocationTaskManager that is responsible // for the thread management can be created. // // This is then passed to the ChaincodeSupportClient to be connected to the // gRPC streams final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName(this.id).build(); final ManagedChannelBuilder channelBuilder = newChannelBuilder(); final ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(channelBuilder); final InvocationTaskManager itm = InvocationTaskManager.getManager(this, chaincodeId); chaincodeSupportClient.start(itm, requestObserver); return new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage chaincodeMessage) { itm.onChaincodeMessage(chaincodeMessage); } @Override public void onError(final Throwable t) { LOGGER.severe(() -> "An error occurred on the chaincode stream. Shutting down the chaincode stream." + Logging.formatError(t)); chaincodeSupportClient.shutdown(itm); } @Override public void onCompleted() { LOGGER.severe("Chaincode stream is complete. Shutting down the chaincode stream."); chaincodeSupportClient.shutdown(itm); } }; } protected final void initializeLogging() { // the VM wide formatting string. System.setProperty( "java.util.logging.SimpleFormatter.format", "%1$tH:%1$tM:%1$tS:%1$tL %4$-7.7s %2$-80.80s %5$s%6$s%n"); final Logger rootLogger = Logger.getLogger(""); var formatter = new SimpleFormatter() { @Override public String format(final LogRecord record) { return Thread.currentThread() + " " + super.format(record); } }; for (final java.util.logging.Handler handler : rootLogger.getHandlers()) { handler.setLevel(ALL); handler.setFormatter(formatter); } rootLogger.info("Updated all handlers the format"); // set logging level of chaincode logger final Level chaincodeLogLevel = mapLevel(System.getenv(CORE_CHAINCODE_LOGGING_LEVEL)); final Package chaincodePackage = this.getClass().getPackage(); if (chaincodePackage != null) { Logger.getLogger(chaincodePackage.getName()).setLevel(chaincodeLogLevel); } else { // If chaincode declared without package, i.e. default package, lets set level // to root logger // Chaincode should never be declared without package Logger.getLogger("").setLevel(chaincodeLogLevel); } // set logging level of shim logger final Level shimLogLevel = mapLevel(System.getenv(CORE_CHAINCODE_LOGGING_SHIM)); Logger.getLogger(ChaincodeBase.class.getPackage().getName()).setLevel(shimLogLevel); Logger.getLogger(ContractRouter.class.getPackage().getName()).setLevel(chaincodeLogLevel); } private Level mapLevel(final String level) { if (level != null) { switch (level.toUpperCase(Locale.getDefault()).trim()) { case "CRITICAL": case "ERROR": return Level.SEVERE; case "WARNING": case "WARN": return Level.WARNING; case "INFO": return Level.INFO; case "NOTICE": return Level.CONFIG; case "DEBUG": return Level.FINEST; default: break; } } return Level.INFO; } private SocketAddress parseHostPort(final String hostAddrStr) throws URISyntaxException { // WORKAROUND: add any scheme to make the resulting URI valid. URI uri = new URI("my://" + hostAddrStr); // may throw URISyntaxException String host = uri.getHost(); int port = uri.getPort(); if (host == null || port == -1) { throw new URISyntaxException(uri.toString(), "URI must have host and port parts"); } // validation succeeded return new InetSocketAddress(host, port); } /** * Use the CHAINCODE_SERVER_ADDRESS as the key to swap mode. * * @return true if this should be run as `chaincode-as-a-service` */ public boolean isServer() { return !chaincodeServerAddress.isEmpty(); } /** Validate init parameters from env chaincode base. */ @SuppressWarnings("PMD.CyclomaticComplexity") public void validateOptions() { if (this.id == null || this.id.isEmpty()) { throw new IllegalArgumentException(String.format( "The chaincode id must be specified using either the -i or --i command line options or the %s environment variable.", CORE_CHAINCODE_ID_NAME)); } if (this.tlsEnabled) { if (tlsClientCertPath == null) { throw new IllegalArgumentException(String.format( "Client key certificate chain (%s) was not specified.", ENV_TLS_CLIENT_CERT_PATH)); } if (tlsClientKeyPath == null) { throw new IllegalArgumentException( String.format("Client key (%s) was not specified.", ENV_TLS_CLIENT_KEY_PATH)); } if (tlsClientRootCertPath == null) { throw new IllegalArgumentException(String.format( "Peer certificate trust store (%s) was not specified.", CORE_PEER_TLS_ROOTCERT_FILE)); } } } @SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition", "PMD.AvoidCatchingGenericException", "PMD.ExceptionAsFlowControl" }) protected final void processCommandLineOptions(final String[] args) { final Options options = new Options(); options.addOption("a", "peer.address", true, "Address of peer to connect to"); options.addOption(null, "peerAddress", true, "Address of peer to connect to"); options.addOption("i", "id", true, "Identity of chaincode"); try { final CommandLine cl = new DefaultParser().parse(options, args); if (cl.hasOption("peerAddress") || cl.hasOption('a')) { String hostAddrStr; if (cl.hasOption('a')) { hostAddrStr = cl.getOptionValue('a'); } else { hostAddrStr = cl.getOptionValue("peerAddress"); } final String[] hostArr = hostAddrStr.split(":"); if (hostArr.length == 2) { port = Integer.parseInt(hostArr[1].trim()); host = hostArr[0].trim(); } else { final String msg = String.format( "peer address argument should be in host:port format, current %s in wrong", hostAddrStr); LOGGER.severe(msg); throw new IllegalArgumentException(msg); } } if (cl.hasOption('i')) { id = cl.getOptionValue('i'); } } catch (final Exception e) { LOGGER.warning(() -> "cli parsing failed with exception" + Logging.formatError(e)); } LOGGER.info(() -> "<<<<<<<<<<<<>>>>>>>>>>>" + "\nCORE_CHAINCODE_ID_NAME: " + this.id + "\nCORE_PEER_ADDRESS: " + this.host + ":" + this.port); } /** set fields from env. */ @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") public final void processEnvironmentOptions() { if (System.getenv().containsKey(CORE_CHAINCODE_ID_NAME)) { this.id = System.getenv(CORE_CHAINCODE_ID_NAME); } if (System.getenv().containsKey(CORE_PEER_ADDRESS)) { final String[] hostArr = System.getenv(CORE_PEER_ADDRESS).split(":"); if (hostArr.length == 2) { this.port = Integer.parseInt(hostArr[1].trim()); this.host = hostArr[0].trim(); } else { final String msg = String.format( "peer address argument should be in host:port format, ignoring current %s", System.getenv(CORE_PEER_ADDRESS)); LOGGER.severe(msg); } } if (System.getenv().containsKey(CHAINCODE_SERVER_ADDRESS)) { this.chaincodeServerAddress = System.getenv(CHAINCODE_SERVER_ADDRESS); } if (System.getenv().containsKey(CORE_PEER_LOCALMSPID)) { this.localMspId = System.getenv(CORE_PEER_LOCALMSPID); } this.tlsEnabled = Boolean.parseBoolean(System.getenv(CORE_PEER_TLS_ENABLED)); if (this.tlsEnabled) { this.tlsClientRootCertPath = System.getenv(CORE_PEER_TLS_ROOTCERT_FILE); this.tlsClientKeyPath = System.getenv(ENV_TLS_CLIENT_KEY_PATH); this.tlsClientCertPath = System.getenv(ENV_TLS_CLIENT_CERT_PATH); this.tlsClientKeyFile = System.getenv(ENV_TLS_CLIENT_KEY_FILE); this.tlsClientCertFile = System.getenv(ENV_TLS_CLIENT_CERT_FILE); } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("<<<<<<<<<<<<>>>>>>>>>>>"); LOGGER.info("CORE_CHAINCODE_ID_NAME: " + this.id); LOGGER.info("CORE_PEER_ADDRESS: " + this.host); LOGGER.info("CORE_PEER_TLS_ENABLED: " + this.tlsEnabled); LOGGER.info("CORE_PEER_TLS_ROOTCERT_FILE: " + this.tlsClientRootCertPath); LOGGER.info("CORE_TLS_CLIENT_KEY_PATH: " + this.tlsClientKeyPath); LOGGER.info("CORE_TLS_CLIENT_CERT_PATH: " + this.tlsClientCertPath); LOGGER.info("CORE_TLS_CLIENT_KEY_FILE: " + this.tlsClientKeyFile); LOGGER.info("CORE_TLS_CLIENT_CERT_FILE: " + this.tlsClientCertFile); LOGGER.info("CORE_PEER_LOCALMSPID: " + this.localMspId); LOGGER.info("CHAINCODE_SERVER_ADDRESS: " + this.chaincodeServerAddress); LOGGER.info("LOGLEVEL: " + this.logLevel); } } /** * Obtains configuration specifically for running the chaincode and settable on a per chaincode basis rather than * taking properties from the Peers' configuration. * * @return Configuration */ public Properties getChaincodeConfig() { if (this.props == null) { final ClassLoader cl = this.getClass().getClassLoader(); // determine the location of the properties file to control the metrics etc. props = new Properties(); try (InputStream inStream = cl.getResourceAsStream("config.props")) { if (inStream != null) { props.load(inStream); } } catch (final IOException e) { LOGGER.warning(() -> "Can not open the properties file for input " + Logging.formatError(e)); } // will be useful props.setProperty(CORE_CHAINCODE_ID_NAME, this.id); props.setProperty(CORE_PEER_ADDRESS, this.host); LOGGER.info("<<<<<<<<<<<<>>>>>>>>>>>"); LOGGER.info(this.props::toString); } return this.props; } /** * The properties for starting as chaincode-as-a-service. * * @return ChaincodeServerProperties populated */ public final ChaincodeServerProperties getChaincodeServerConfig() throws URISyntaxException { ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setServerAddress(parseHostPort(chaincodeServerAddress)); if (tlsEnabled) { // set values on the server properties chaincodeServerProperties.setTlsEnabled(true); chaincodeServerProperties.setKeyFile(this.tlsClientKeyFile); chaincodeServerProperties.setKeyCertChainFile(this.tlsClientCertFile); } return chaincodeServerProperties; } /** * create NettyChannel for host:port with tls if tlsEnabled. * * @return ManagedChannelBuilder * @throws IOException while createSSLContext() */ @SuppressWarnings("deprecation") public final ManagedChannelBuilder newChannelBuilder() throws IOException { // Consider moving this to be pure GRPC // This is being reworked in master so leaving this 'as-is' final NettyChannelBuilder builder = NettyChannelBuilder.forAddress(host, port); LOGGER.info("Configuring channel connection to peer."); builder.maxInboundMessageSize(getMaxInboundMessageSize()); if (tlsEnabled) { builder.negotiationType(NegotiationType.TLS); builder.sslContext(createSSLContext()); } else { builder.usePlaintext(); } // there is a optional in GRPC to use 'directExecutor' rather than the inbuilt // gRPC thread management // not seen to make a marked difference in performance. // However if it ever does, then this is where it should be enabled return builder; } final SslContext createSSLContext() throws IOException { final byte[] ckb = Files.readAllBytes(Paths.get(this.tlsClientKeyPath)); final byte[] ccb = Files.readAllBytes(Paths.get(this.tlsClientCertPath)); return GrpcSslContexts.forClient() .trustManager(new File(this.tlsClientRootCertPath)) .keyManager( new ByteArrayInputStream(Base64.getDecoder().decode(ccb)), new ByteArrayInputStream(Base64.getDecoder().decode(ckb))) .build(); } @Deprecated protected static Response newSuccessResponse(final String message, final byte[] payload) { return ResponseUtils.newSuccessResponse(message, payload); } @Deprecated protected static Response newSuccessResponse() { return ResponseUtils.newSuccessResponse(); } @Deprecated protected static Response newSuccessResponse(final String message) { return ResponseUtils.newSuccessResponse(message); } @Deprecated protected static Response newSuccessResponse(final byte[] payload) { return ResponseUtils.newSuccessResponse(payload); } @Deprecated protected static Response newErrorResponse(final String message, final byte[] payload) { return ResponseUtils.newErrorResponse(message, payload); } @Deprecated protected static Response newErrorResponse() { return ResponseUtils.newErrorResponse(); } @Deprecated protected static Response newErrorResponse(final String message) { return ResponseUtils.newErrorResponse(message); } @Deprecated protected static Response newErrorResponse(final byte[] payload) { return ResponseUtils.newErrorResponse(payload); } @Deprecated protected static Response newErrorResponse(final Throwable throwable) { return ResponseUtils.newErrorResponse(throwable); } final String getHost() { return host; } final int getPort() { return port; } final boolean isTlsEnabled() { return tlsEnabled; } final String getTlsClientKeyPath() { return tlsClientKeyPath; } final String getTlsClientCertPath() { return tlsClientCertPath; } final String getTlsClientRootCertPath() { return tlsClientRootCertPath; } /** * Chaincode name / Chaincode id. * * @return string */ String getId() { return id; } /** Chaincode State. */ public enum CCState { /** */ CREATED, /** */ ESTABLISHED, /** */ READY } /** @return State */ public final CCState getState() { return this.state; } /** @param newState */ public final void setState(final CCState newState) { this.state = newState; } /** * Debug Message. * * @param message * @return JSON Form of message */ public static String toJsonString(final ChaincodeMessage message) { try { return JsonFormat.printer().print(message); } catch (final InvalidProtocolBufferException e) { return String.format("{ Type: %s, TxId: %s }", message.getType(), message.getTxid()); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeException.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static java.nio.charset.StandardCharsets.UTF_8; /** * Contracts should use {@code ChaincodeException} to indicate when an error occurs in Smart Contract logic. * *

When a {@code ChaincodeException} is thrown an error response will be returned from the chaincode container * containing the exception message and payload, if specified. * *

{@code ChaincodeException} may be extended to provide application specific error information. Subclasses should * ensure that {@link #getPayload} returns a serialized representation of the error in a suitable format for client * applications to process. */ public class ChaincodeException extends RuntimeException { private static final long serialVersionUID = 3664437023130016393L; private byte[] payload; /** Constructs a new {@code ChaincodeException} with no detail message. */ public ChaincodeException() { super(); } /** * Constructs a new {@code ChaincodeException} with the specified detail message. * * @param message the detail message. */ public ChaincodeException(final String message) { super(message); } /** * Constructs a new {@code ChaincodeException} with the specified cause. * * @param cause the cause. */ public ChaincodeException(final Throwable cause) { super(cause); } /** * Constructs a new {@code ChaincodeException} with the specified detail message and cause. * * @param message the detail message. * @param cause the cause. */ public ChaincodeException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new {@code ChaincodeException} with the specified detail message and response payload. * * @param message the detail message. * @param payload the response payload. */ @SuppressWarnings("PMD.ArrayIsStoredDirectly") public ChaincodeException(final String message, final byte[] payload) { super(message); this.payload = payload; } /** * Constructs a new {@code ChaincodeException} with the specified detail message, response payload and cause. * * @param message the detail message. * @param payload the response payload. * @param cause the cause. */ @SuppressWarnings("PMD.ArrayIsStoredDirectly") public ChaincodeException(final String message, final byte[] payload, final Throwable cause) { super(message, cause); this.payload = payload; } /** * Constructs a new {@code ChaincodeException} with the specified detail message and response payload. * * @param message the detail message. * @param payload the response payload. */ public ChaincodeException(final String message, final String payload) { super(message); this.payload = payload.getBytes(UTF_8); } /** * Constructs a new {@code ChaincodeException} with the specified detail message, response payload and cause. * * @param message the detail message. * @param payload the response payload. * @param cause the cause. */ public ChaincodeException(final String message, final String payload, final Throwable cause) { super(message, cause); this.payload = payload.getBytes(UTF_8); } /** * Returns the response payload or {@code null} if there is no response. * *

The payload should represent the chaincode error in a way that client applications written in different * programming languages can interpret. For example it could include a domain specific error code, in addition to * any state information which would allow client applications to respond appropriately. * * @return the response payload or {@code null} if there is no response. */ @SuppressWarnings("PMD.MethodReturnsInternalArray") public byte[] getPayload() { return payload; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServer.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.io.IOException; /** External chaincode server. */ public interface ChaincodeServer { /** * run external chaincode server. * * @throws IOException problem while start grpc server * @throws InterruptedException thrown when block and awaiting shutdown gprc server */ void start() throws IOException, InterruptedException; /** shutdown now grpc server. */ void stop(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeServerProperties.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.net.SocketAddress; public final class ChaincodeServerProperties { private SocketAddress serverAddress; private int maxInboundMetadataSize = 100 * 1024 * 1024; private int maxInboundMessageSize = 100 * 1024 * 1024; private int maxConnectionAgeSeconds = 5; private int keepAliveTimeoutSeconds = 20; private int permitKeepAliveTimeMinutes = 1; private int keepAliveTimeMinutes = 1; private boolean permitKeepAliveWithoutCalls = true; private String keyPassword; private String keyCertChainFile; private String keyFile; private String trustCertCollectionFile; private boolean tlsEnabled; /** Constructor using default configuration. */ public ChaincodeServerProperties() { // Nothing to do } /** * Constructor. * * @param portChaincodeServer ignored. * @param maxInboundMetadataSize the maximum metadata size allowed to be received by the server. * @param maxInboundMessageSize the maximum message size allowed to be received by the server. * @param maxConnectionAgeSeconds the maximum connection age in seconds. * @param keepAliveTimeoutSeconds timeout for a keep-alive ping request in seconds. * @param permitKeepAliveTimeMinutes the most aggressive keep-alive time clients are permitted to configure in * minutes. * @param keepAliveTimeMinutes delay before server sends a keep-alive in minutes. * @param permitKeepAliveWithoutCalls whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are * no outstanding RPCs on the connection. */ @SuppressWarnings({"PMD.NullAssignment"}) public ChaincodeServerProperties( final int portChaincodeServer, final int maxInboundMetadataSize, final int maxInboundMessageSize, final int maxConnectionAgeSeconds, final int keepAliveTimeoutSeconds, final int permitKeepAliveTimeMinutes, final int keepAliveTimeMinutes, final boolean permitKeepAliveWithoutCalls) { this.serverAddress = null; this.maxInboundMetadataSize = maxInboundMetadataSize; this.maxInboundMessageSize = maxInboundMessageSize; this.maxConnectionAgeSeconds = maxConnectionAgeSeconds; this.keepAliveTimeoutSeconds = keepAliveTimeoutSeconds; this.permitKeepAliveTimeMinutes = permitKeepAliveTimeMinutes; this.keepAliveTimeMinutes = keepAliveTimeMinutes; this.permitKeepAliveWithoutCalls = permitKeepAliveWithoutCalls; } /** * The maximum size of metadata allowed to be received. * * @return The maximum metadata size allowed. */ public int getMaxInboundMetadataSize() { return maxInboundMetadataSize; } /** * Sets the maximum metadata size allowed to be received by the server. * * @param maxInboundMetadataSize The new maximum size allowed for incoming metadata. */ public void setMaxInboundMetadataSize(final int maxInboundMetadataSize) { this.maxInboundMetadataSize = maxInboundMetadataSize; } /** * The maximum message size allowed to be received by the server. * * @return the maximum message size allowed. */ public int getMaxInboundMessageSize() { return maxInboundMessageSize; } /** * Sets the maximum message size allowed to be received by the server. * * @param maxInboundMessageSize The new maximum size allowed for incoming messages. */ public void setMaxInboundMessageSize(final int maxInboundMessageSize) { this.maxInboundMessageSize = maxInboundMessageSize; } /** * The maximum connection age. * * @return The maximum connection age in seconds. */ public int getMaxConnectionAgeSeconds() { return maxConnectionAgeSeconds; } /** * Specify a maximum connection age. * * @param maxConnectionAgeSeconds The maximum connection age in seconds. */ public void setMaxConnectionAgeSeconds(final int maxConnectionAgeSeconds) { this.maxConnectionAgeSeconds = maxConnectionAgeSeconds; } /** * The timeout for a keep-alive ping requests. * * @return timeout in seconds. */ public int getKeepAliveTimeoutSeconds() { return keepAliveTimeoutSeconds; } /** * Set the timeout for keep-alive ping requests. * * @param keepAliveTimeoutSeconds timeout in seconds. */ public void setKeepAliveTimeoutSeconds(final int keepAliveTimeoutSeconds) { this.keepAliveTimeoutSeconds = keepAliveTimeoutSeconds; } /** * The most aggressive keep-alive time clients are permitted to configure. * * @return time in minutes. */ public int getPermitKeepAliveTimeMinutes() { return permitKeepAliveTimeMinutes; } /** * Specify the most aggressive keep-alive time clients are permitted to configure. * * @param permitKeepAliveTimeMinutes time in minutes. */ public void setPermitKeepAliveTimeMinutes(final int permitKeepAliveTimeMinutes) { this.permitKeepAliveTimeMinutes = permitKeepAliveTimeMinutes; } /** * The delay before the server sends a keep-alive. * * @return delay in minutes. */ public int getKeepAliveTimeMinutes() { return keepAliveTimeMinutes; } /** * Set the delay before the server sends a keep-alive. * * @param keepAliveTimeMinutes delay in minutes. */ public void setKeepAliveTimeMinutes(final int keepAliveTimeMinutes) { this.keepAliveTimeMinutes = keepAliveTimeMinutes; } /** * Whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the * connection. * * @return true if clients are allowed to send keep-alive requests without calls; otherwise false. */ @SuppressWarnings("PMD.BooleanGetMethodName") public boolean getPermitKeepAliveWithoutCalls() { return permitKeepAliveWithoutCalls; } /** * Get the server socket address. * * @return a socket address. */ public SocketAddress getServerAddress() { return serverAddress; } /** * Set the server socket address. * * @param address a socket address. */ public void setServerAddress(final SocketAddress address) { this.serverAddress = address; } /** * Whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the * connection. * * @return true if clients are allowed to send keep-alive requests without calls; otherwise false. */ public boolean isPermitKeepAliveWithoutCalls() { return permitKeepAliveWithoutCalls; } /** * Specify whether clients are allowed to send keep-alive HTTP/2 PINGs even if there are no outstanding RPCs on the * connection. * * @param permitKeepAliveWithoutCalls Whether to allow clients to send keep-alive requests without calls. */ public void setPermitKeepAliveWithoutCalls(final boolean permitKeepAliveWithoutCalls) { this.permitKeepAliveWithoutCalls = permitKeepAliveWithoutCalls; } /** * Password used to access the server key. * * @return a password. */ public String getKeyPassword() { return keyPassword; } /** * Set the password used to access the server key. * * @param keyPassword a password. */ public void setKeyPassword(final String keyPassword) { this.keyPassword = keyPassword; } /** * Server keychain file name. * * @return a file name. */ public String getKeyCertChainFile() { return keyCertChainFile; } /** * Set the server keychain file name. * * @param keyCertChainFile a file name. */ public void setKeyCertChainFile(final String keyCertChainFile) { this.keyCertChainFile = keyCertChainFile; } /** * Server key file name. * * @return a file name. */ public String getKeyFile() { return keyFile; } /** * Set the server key file name. * * @param keyFile a file name. */ public void setKeyFile(final String keyFile) { this.keyFile = keyFile; } /** * Server trust certificate collection file name. * * @return a file name. */ public String getTrustCertCollectionFile() { return trustCertCollectionFile; } /** * Set the server trust certificate collection file name. * * @param trustCertCollectionFile a file name. */ public void setTrustCertCollectionFile(final String trustCertCollectionFile) { this.trustCertCollectionFile = trustCertCollectionFile; } /** * Whether TLS is enabled for the server. * * @return true if TLS is enabled; otherwise false. */ public boolean isTlsEnabled() { return tlsEnabled; } /** * Set whether TLS is enabled for the server. * * @param tlsEnabled true to enable TLS; otherwise false. */ public void setTlsEnabled(final boolean tlsEnabled) { this.tlsEnabled = tlsEnabled; } /** * Check that all the server property values are valid. * * @throws IllegalArgumentException if any properties are not valid. */ @SuppressWarnings("PMD.CyclomaticComplexity") public void validate() { if (this.getServerAddress() == null) { throw new IllegalArgumentException("chaincodeServerProperties.getServerAddress() must be set"); } if (this.getKeepAliveTimeMinutes() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getKeepAliveTimeMinutes() must be more then 0"); } if (this.getKeepAliveTimeoutSeconds() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getKeepAliveTimeoutSeconds() must be more then 0"); } if (this.getPermitKeepAliveTimeMinutes() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getPermitKeepAliveTimeMinutes() must be more then 0"); } if (this.getMaxConnectionAgeSeconds() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getMaxConnectionAgeSeconds() must be more then 0"); } if (this.getMaxInboundMetadataSize() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getMaxInboundMetadataSize() must be more then 0"); } if (this.getMaxInboundMessageSize() <= 0) { throw new IllegalArgumentException( "chaincodeServerProperties.getMaxInboundMessageSize() must be more then 0"); } if (this.isTlsEnabled() && (this.getKeyCertChainFile() == null || this.getKeyCertChainFile().isEmpty() || this.getKeyFile() == null || this.getKeyFile().isEmpty())) { throw new IllegalArgumentException("if chaincodeServerProperties.isTlsEnabled() must be more specified" + " chaincodeServerProperties.getKeyCertChainFile() and chaincodeServerProperties.getKeyFile()" + " with optional chaincodeServerProperties.getKeyPassword()"); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.Map; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.SignedProposal; import org.hyperledger.fabric.shim.Chaincode.Response; import org.hyperledger.fabric.shim.ledger.CompositeKey; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; /** * An object which manages the transaction context, provides access to state variables, and supports calls to other * chaincode implementations. */ @SuppressWarnings("PMD.ExcessivePublicCount") public interface ChaincodeStub { /** * Returns the arguments corresponding to the call to {@link Chaincode#init(ChaincodeStub)} or * {@link Chaincode#invoke(ChaincodeStub)}, each argument represented as byte array. * * @return a list of arguments (bytes arrays) */ List getArgs(); /** * Returns the arguments corresponding to the call to {@link Chaincode#init(ChaincodeStub)} or * {@link Chaincode#invoke(ChaincodeStub)}, cast to UTF-8 string. * * @return a list of arguments cast to UTF-8 strings */ List getStringArgs(); /** * A convenience method that returns the first argument of the chaincode invocation for use as a function name. * *

The bytes of the first argument are decoded as a UTF-8 string. * * @return the function name */ String getFunction(); /** * A convenience method that returns all except the first argument of the chaincode invocation for use as the * parameters to the function returned by #{@link ChaincodeStub#getFunction()}. * *

The bytes of the arguments are decoded as a UTF-8 strings and returned as a list of string parameters. * * @return a list of parameters */ List getParameters(); /** * Returns the transaction id for the current chaincode invocation request. * *

The transaction id uniquely identifies the transaction within the scope of the channel. * * @return the transaction id */ String getTxId(); /** * Returns the channel id for the current proposal. * *

This would be the 'channel_id' of the transaction proposal except where the chaincode is calling another on a * different channel. * * @return the channel id */ String getChannelId(); /** * Locally calls the specified chaincode invoke() using the same transaction context. * *

chaincode calling chaincode doesn't create a new transaction message. * *

If the called chaincode is on the same channel, it simply adds the called chaincode read set and write set to * the calling transaction. * *

If the called chaincode is on a different channel, only the Response is returned to the calling chaincode; any * putState calls from the called chaincode will not have any effect on the ledger; that is, the called * chaincode on a different channel will not have its read set and write set applied to the transaction. Only the * calling chaincode's read set and write set will be applied to the transaction. Effectively the called chaincode * on a different channel is a `Query`, which does not participate in state validation checks in subsequent commit * phase. * *

If `channel` is empty, the caller's channel is assumed. * *

Invoke another chaincode using the same transaction context. * * @param chaincodeName Name of chaincode to be invoked. * @param args Arguments to pass on to the called chaincode. * @param channel If not specified, the caller's channel is assumed. * @return {@link Response} object returned by called chaincode */ Response invokeChaincode(String chaincodeName, List args, String channel); /** * Returns the value of the specified key from the ledger. * *

Note that getState doesn't read data from the writeset, which has not been committed to the ledger. In other * words, GetState doesn't consider data modified by PutState that has not been committed. * * @param key name of the value * @return value the value read from the ledger */ byte[] getState(String key); /** * retrieves the key-level endorsement policy for key. Note that this will introduce a read dependency * on key in the transaction's readset. * * @param key key to get key level endorsement * @return endorsement policy */ byte[] getStateValidationParameter(String key); /** * Puts the specified key and value into the transaction's writeset as a data-write * proposal. * *

putState doesn't effect the ledger until the transaction is validated and successfully committed. Simple keys * must not be an empty string and must not start with 0x00 character, in order to avoid range query collisions with * composite keys * * @param key name of the value * @param value the value to write to the ledger */ void putState(String key, byte[] value); /** * Sets the key-level endorsement policy for key. * * @param key key to set key level endorsement * @param value endorsement policy */ void setStateValidationParameter(String key, byte[] value); /** * Records the specified key to be deleted in the writeset of the transaction proposal. * *

The key and its value will be deleted from the ledger when the transaction is validated and * successfully committed. * * @param key name of the value to be deleted */ void delState(String key); /** * Returns all existing keys, and their values, that are lexicographically between startkey (inclusive) * and the endKey (exclusive). * *

The keys are returned by the iterator in lexical order. Note that startKey and endKey can be empty string, * which implies unbounded range query on start or end. * *

Call close() on the returned {@link QueryResultsIterator#close()} object when done. * * @param startKey key as the start of the key range (inclusive) * @param endKey key as the end of the key range (exclusive) * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getStateByRange(String startKey, String endKey); /** * Returns a range iterator over a set of keys in the ledger. The iterator can be used to fetch keys between the * startKey (inclusive) and endKey (exclusive). When an empty string is passed as a value * to the bookmark argument, the returned iterator can be used to fetch the first pageSize * keys between the startKey and endKey. When the bookmark is a non-empty * string, the iterator can be used to fetch first pageSize keys between the bookmark and * endKey. Note that only the bookmark present in a prior page of query results * ({@link org.hyperledger.fabric.protos.peer.QueryResponseMetadata}) can be used as a value to the bookmark * argument. Otherwise, an empty string must be passed as bookmark. The keys are returned by the iterator in lexical * order. Note that startKey and endKey can be empty string, which implies unbounded range * query on start or end. This call is only supported in a read only transaction. * * @param startKey the start key * @param endKey the end key * @param pageSize the page size * @param bookmark the bookmark * @return QueryIterator */ QueryResultsIteratorWithMetadata getStateByRangeWithPagination( String startKey, String endKey, int pageSize, String bookmark); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey}. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * *

This method takes responsibility to correctly parse the {@link CompositeKey} from a String and behaves exactly * as {@link ChaincodeStub#getStateByPartialCompositeKey(CompositeKey)}. * *

Call close() on the returned {@link QueryResultsIterator#close()} object when done. * * @param compositeKey partial composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getStateByPartialCompositeKey(String compositeKey); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey}. * *

It combines the attributes and the objectType to form a partial composite key. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * *

This method takes responsibility to correctly combine Object type and attributes creating a * {@link CompositeKey} and behaves exactly as {@link ChaincodeStub#getStateByPartialCompositeKey(CompositeKey)}. * Call close() on the returned {@link QueryResultsIterator#close()} object when done. * * @param objectType ObjectType of the compositeKey * @param attributes Attributes of the composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getStateByPartialCompositeKey(String objectType, String... attributes); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey}. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * * @param compositeKey partial composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getStateByPartialCompositeKey(CompositeKey compositeKey); /** * Queries the state in the ledger based on a given partial composite key. This function returns an iterator which * can be used to iterate over the composite keys whose prefix matches the given partial composite key. * *

When an empty string is passed as a value to the bookmark argument, the returned iterator can be * used to fetch the first pageSize composite keys whose prefix matches the given partial composite * key. * *

When the bookmark is a non-empty string, the iterator can be used to fetch first pageSize * keys between the bookmark (inclusive) and and the last matching composite key. * *

Note that only the bookmark present in a prior page of query results * ({@link org.hyperledger.fabric.protos.peer.QueryResponseMetadata}) can be used as a value to the bookmark * argument. Otherwise, an empty string must be passed as bookmark. * *

This call is only supported in a read only transaction. * * @param compositeKey the composite key * @param pageSize the page size * @param bookmark the bookmark * @return QueryIterator */ QueryResultsIteratorWithMetadata getStateByPartialCompositeKeyWithPagination( CompositeKey compositeKey, int pageSize, String bookmark); /** * Given a set of attributes, this method combines these attributes to return a composite key. * * @param objectType A string used as the prefix of the resulting key * @param attributes List of attribute values to concatenate into the key * @return a composite key */ CompositeKey createCompositeKey(String objectType, String... attributes); /** * Parses a composite key {@link CompositeKey} from a string. * * @param compositeKey a composite key string * @return a composite key */ CompositeKey splitCompositeKey(String compositeKey); /** * Performs a "rich" query against a state database. * *

It is only supported for state databases that support rich query, e.g. CouchDB. The query string is in the * native syntax of the underlying state database. An {@link QueryResultsIterator} is returned which can be used to * iterate (next) over the query result set. * * @param query query string in a syntax supported by the underlying state database * @return {@link QueryResultsIterator} object contains query results * @throws UnsupportedOperationException if the underlying state database does not support rich queries. */ QueryResultsIterator getQueryResult(String query); /** * Performs a "rich" query against a state database. It is only supported for state databases that support rich * query, e.g., CouchDB. The query string is in the native syntax of the underlying state database. An iterator is * returned which can be used to iterate over keys in the query result set. When an empty string is passed as a * value to the bookmark argument, the returned iterator can be used to fetch the first pageSize * of query results.. * *

When the bookmark is a non-empty string, the iterator can be used to fetch first pageSize * keys between the bookmark (inclusive) and the last key in the query result. * *

Note that only the bookmark present in a prior page of query results * ({@link org.hyperledger.fabric.protos.peer.QueryResponseMetadata}) can be used as a value to the bookmark * argument. Otherwise, an empty string must be passed as bookmark. * *

This call is only supported in a read only transaction. * * @param query the query * @param pageSize the page size * @param bookmark the bookmark * @return QueryIterator */ QueryResultsIteratorWithMetadata getQueryResultWithPagination( String query, int pageSize, String bookmark); /** * Returns a history of key values across time. * *

For each historic key update, the historic value and associated transaction id and timestamp are returned. The * timestamp is the timestamp provided by the client in the proposal header. This method requires peer configuration * core.ledger.history.enableHistoryDatabase to be true. * * @param key The state variable key * @return an {@link Iterable} of {@link KeyModification} */ QueryResultsIterator getHistoryForKey(String key); /** * Returns the value of the specified key from the specified collection. * *

Note that {@link #getPrivateData(String, String)} doesn't read data from the private writeset, which has not * been committed to the collection. In other words, {@link #getPrivateData(String, String)} doesn't * consider data modified by {@link #putPrivateData(String, String, byte[])} * that has not been committed. * * @param collection name of the collection * @param key name of the value * @return value the value read from the collection */ byte[] getPrivateData(String collection, String key); /** * @param collection name of the collection * @param key name of the value * @return the private data hash */ byte[] getPrivateDataHash(String collection, String key); /** * Retrieves the key-level endorsement policy for the private data specified by key. Note that this * introduces a read dependency on key in the transaction's readset. * * @param collection name of the collection * @param key key to get endorsement policy * @return Key Level endorsement as byte array */ byte[] getPrivateDataValidationParameter(String collection, String key); /** * Puts the specified key and value into the transaction's private writeset. * *

Note that only hash of the private writeset goes into the transaction proposal response (which is sent to the * client who issued the transaction) and the actual private writeset gets temporarily stored in a transient store. * putPrivateData doesn't effect the collection until the transaction is validated and successfully * committed. Simple keys must not be an empty string and must not start with null character (0x00), in order to * avoid range query collisions with composite keys, which internally get prefixed with 0x00 as composite key * namespace. * * @param collection name of the collection * @param key name of the value * @param value the value to write to the ledger */ void putPrivateData(String collection, String key, byte[] value); /** * Sets the key-level endorsement policy for the private data specified by key. * * @param collection name of the collection * @param key key to set endorsement policy * @param value endorsement policy */ void setPrivateDataValidationParameter(String collection, String key, byte[] value); /** * Records the specified key to be deleted in the private writeset of the transaction. * *

Note that only hash of the private writeset goes into the transaction proposal response (which is sent to the * client who issued the transaction) and the actual private writeset gets temporarily stored in a transient store. * The key and its value will be deleted from the collection when the transaction is validated and * successfully committed. * * @param collection name of the collection * @param key name of the value to be deleted */ void delPrivateData(String collection, String key); /** * Reqauests purging of the specified key to be from the private data stores. * *

Note that only hash of the private writeset goes into the transaction proposal response (which is sent to the * client who issued the transaction) and the actual private writeset gets temporarily stored in a transient store. * The key and its value will be purged from the collection. This is an asynchronous activity. * *

Purge is a complete removal of the history of the key. There is existing purge possible mased on block height. * This API allows the contract to be pro-active in requesting data be purged. This can contribute towards meeting * privacy requirements. * * @param collection name of the collection * @param key name of the value to be deleted */ void purgePrivateData(String collection, String key); /** * Returns all existing keys, and their values, that are lexicographically between startkey (inclusive) * and the endKey (exclusive) in a given private collection. * *

Note that startKey and endKey can be empty string, which implies unbounded range query on start or end. The * query is re-executed during validation phase to ensure result set has not changed since transaction endorsement * (phantom reads detected). * * @param collection name of the collection * @param startKey private data variable key as the start of the key range (inclusive) * @param endKey private data variable key as the end of the key range (exclusive) * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getPrivateDataByRange(String collection, String startKey, String endKey); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey} in a * given private collection. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * *

The query is re-executed during validation phase to ensure result set has not changed since transaction * endorsement (phantom reads detected). * *

This method takes responsibility to correctly parse the {@link CompositeKey} from a String and behaves exactly * as {@link ChaincodeStub#getPrivateDataByPartialCompositeKey(String, CompositeKey)}. * * @param collection name of the collection * @param compositeKey partial composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getPrivateDataByPartialCompositeKey(String collection, String compositeKey); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey} in a * given private collection. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * *

The query is re-executed during validation phase to ensure result set has not changed since transaction * endorsement (phantom reads detected). * * @param collection name of the collection * @param compositeKey partial composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getPrivateDataByPartialCompositeKey(String collection, CompositeKey compositeKey); /** * Returns all existing keys, and their values, that are prefixed by the specified partial {@link CompositeKey} in a * given private collection. * *

If a full composite key is specified, it will not match itself, resulting in no keys being returned. * *

The query is re-executed during validation phase to ensure result set has not changed since transaction * endorsement (phantom reads detected). * *

This method takes responsibility to correctly combine Object type and attributes creating a * {@link CompositeKey} and behaves exactly as {@link ChaincodeStub#getPrivateDataByPartialCompositeKey(String, * CompositeKey)}. * * @param collection name of the collection * @param objectType ObjectType of the compositeKey * @param attributes Attributes of the composite key * @return an {@link Iterable} of {@link KeyValue} */ QueryResultsIterator getPrivateDataByPartialCompositeKey( String collection, String objectType, String... attributes); /** * Perform a rich query against a given private collection. * *

It is only supported for state databases that support rich query, e.g.CouchDB. The query string is in the * native syntax of the underlying state database. An iterator is returned which can be used to iterate (next) over * the query result set. The query is NOT re-executed during validation phase, phantom reads are not detected. That * is, other committed transactions may have added, updated, or removed keys that impact the result set, and this * would not be detected at validation/commit time. Applications susceptible to this should therefore not use * GetQueryResult as part of transactions that update ledger, and should limit use to read-only chaincode * operations. * * @param collection name of the collection * @param query query string in a syntax supported by the underlying state database * @return {@link QueryResultsIterator} object contains query results * @throws UnsupportedOperationException if the underlying state database does not support rich queries. */ QueryResultsIterator getPrivateDataQueryResult(String collection, String query); /** * Allows the chaincode to propose an event on the transaction proposal response. When the transaction is included * in a block and the block is successfully committed to the ledger, the block event (including transaction level * chaincode events) will be delivered to the current client application event listeners that have been registered * with the peer's event producer. Consult each SDK's documentation for details. Only a single chaincode event can * be included in a transaction. If setEvent() is called multiple times only the last event will be included in the * transaction. The event must originate from the outer-most invoked chaincode in chaincode-to-chaincode scenarios. * The marshaled ChaincodeEvent will be available in the transaction's ChaincodeAction.events field. * * @param name Name of event. Cannot be null or empty string. * @param payload Optional event payload. */ void setEvent(String name, byte[] payload); /** * Invoke another chaincode using the same transaction context. * *

Same as {@link #invokeChaincode(String, List, String)} using channelId to null * * @param chaincodeName Name of chaincode to be invoked. * @param args Arguments to pass on to the called chaincode. * @return {@link Response} object returned by called chaincode */ default Response invokeChaincode(final String chaincodeName, final List args) { return invokeChaincode(chaincodeName, args, null); } /** * Invoke another chaincode using the same transaction context. * *

This is a convenience version of {@link #invokeChaincode(String, List, String)}. The string args will be * encoded into as UTF-8 bytes. * * @param chaincodeName Name of chaincode to be invoked. * @param args Arguments to pass on to the called chaincode. * @param channel If not specified, the caller's channel is assumed. * @return {@link Response} object returned by called chaincode */ default Response invokeChaincodeWithStringArgs( final String chaincodeName, final List args, final String channel) { return invokeChaincode( chaincodeName, args.stream().map(x -> x.getBytes(UTF_8)).collect(toList()), channel); } /** * Invoke another chaincode using the same transaction context. * *

This is a convenience version of {@link #invokeChaincode(String, List)}. The string args will be encoded into * as UTF-8 bytes. * * @param chaincodeName Name of chaincode to be invoked. * @param args Arguments to pass on to the called chaincode. * @return {@link Response} object returned by called chaincode */ default Response invokeChaincodeWithStringArgs(final String chaincodeName, final List args) { return invokeChaincodeWithStringArgs(chaincodeName, args, null); } /** * Invoke another chaincode using the same transaction context. * *

This is a convenience version of {@link #invokeChaincode(String, List)}. The string args will be encoded into * as UTF-8 bytes. * * @param chaincodeName Name of chaincode to be invoked. * @param args Arguments to pass on to the called chaincode. * @return {@link Response} object returned by called chaincode */ default Response invokeChaincodeWithStringArgs(final String chaincodeName, final String... args) { return invokeChaincodeWithStringArgs(chaincodeName, Arrays.asList(args), null); } /** * Returns the byte array value specified by the key and decoded as a UTF-8 encoded string, from the ledger. * *

This is a convenience version of {@link #getState(String)} * * @param key name of the value * @return value the value read from the ledger */ default String getStringState(final String key) { return new String(getState(key), UTF_8); } /** * Writes the specified value and key into the sidedb collection value converted to byte array. * * @param collection collection name * @param key name of the value * @param value the value to write to the ledger */ default void putPrivateData(final String collection, final String key, final String value) { putPrivateData(collection, key, value.getBytes(UTF_8)); } /** * Returns the byte array value specified by the key and decoded as a UTF-8 encoded string, from the sidedb * collection. * * @param collection collection name * @param key name of the value * @return value the value read from the ledger */ default String getPrivateDataUTF8(final String collection, final String key) { return new String(getPrivateData(collection, key), UTF_8); } /** * Writes the specified value and key into the ledger. * * @param key name of the value * @param value the value to write to the ledger */ default void putStringState(final String key, final String value) { putState(key, value.getBytes(UTF_8)); } /** * Returns the CHAINCODE type event that will be posted to interested clients when the chaincode's result is * committed to the ledger. * * @return the chaincode event or null */ ChaincodeEvent getEvent(); /** * Returns the signed transaction proposal currently being executed. * * @return null if the current transaction is an internal call to a system chaincode. */ SignedProposal getSignedProposal(); /** * Returns the timestamp when the transaction was created. * * @return timestamp as specified in the transaction's channel header. */ Instant getTxTimestamp(); /** * Returns the identity of the agent (or user) submitting the transaction. * * @return the bytes of the creator field of the proposal's signature header. */ byte[] getCreator(); /** * Returns the transient map associated with the current transaction. * * @return map of transient field */ Map getTransient(); /** * Returns the transaction binding. * * @return binding between application data and proposal */ byte[] getBinding(); /** * Get the MSPID of the peer that started this chaincode. * * @return string MSPID */ String getMspId(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ChatChaincodeWithPeer.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.protos.peer.ChaincodeGrpc; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; public class ChatChaincodeWithPeer extends ChaincodeGrpc.ChaincodeImplBase { private static Logger logger = Logger.getLogger(ChatChaincodeWithPeer.class.getName()); private final ChaincodeBase chaincodeBase; ChatChaincodeWithPeer(final ChaincodeBase chaincodeBase) throws IOException { super(); if (chaincodeBase == null) { throw new IOException("chaincodeBase can't be null"); } chaincodeBase.validateOptions(); this.chaincodeBase = chaincodeBase; } /** * Chaincode as a server - peer establishes a connection to the chaincode as a client Currently only supports a * stream connection. * * @param responseObserver * @return */ @Override @SuppressWarnings("PMD.AvoidCatchingGenericException") public StreamObserver connect(final StreamObserver responseObserver) { if (responseObserver == null) { return null; } try { return chaincodeBase.connectToPeer(responseObserver); } catch (Exception e) { logger.severe(() -> "catch exception while chaincodeBase.connectToPeer(responseObserver)." + Logging.formatError(e)); return null; } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/GrpcServer.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.io.IOException; /** Common interface for grpc server. */ public interface GrpcServer { /** * start grpc server. * * @throws IOException problem while start grpc server */ void start() throws IOException; /** shutdown now grpc server. */ void stop(); /** * Await termination on the main thread since the grpc library uses daemon threads. * * @throws InterruptedException */ void blockUntilShutdown() throws InterruptedException; } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/NettyChaincodeServer.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.io.IOException; public class NettyChaincodeServer implements ChaincodeServer { /** Server. */ private final GrpcServer grpcServer; /** * configure and init server. * * @param chaincodeBase - chaincode implementation (invoke, init) * @param chaincodeServerProperties - setting for grpc server * @throws IOException */ public NettyChaincodeServer( final ChaincodeBase chaincodeBase, final ChaincodeServerProperties chaincodeServerProperties) throws IOException { // create listener and grpc server grpcServer = new NettyGrpcServer(chaincodeBase, chaincodeServerProperties); } /** * run external chaincode server. * * @throws IOException problem while start grpc server * @throws InterruptedException thrown when block and awaiting shutdown gprc server */ @Override public void start() throws IOException, InterruptedException { grpcServer.start(); grpcServer.blockUntilShutdown(); } /** shutdown now grpc server. */ @Override public void stop() { grpcServer.stop(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/NettyGrpcServer.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import io.grpc.Server; import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder; import io.grpc.netty.shaded.io.netty.handler.ssl.ApplicationProtocolConfig; import io.grpc.netty.shaded.io.netty.handler.ssl.ApplicationProtocolNames; import io.grpc.netty.shaded.io.netty.handler.ssl.ClientAuth; import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder; import java.io.File; import java.io.IOException; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.net.ssl.SSLException; /** implementation grpc server with NettyGrpcServer. */ public final class NettyGrpcServer implements GrpcServer { private static final Logger LOGGER = Logger.getLogger(NettyGrpcServer.class.getName()); private final Server server; /** * init netty grpc server. * * @param chaincodeBase - chaincode implementation (invoke, init) * @param chaincodeServerProperties - setting for grpc server * @throws IOException */ public NettyGrpcServer(final ChaincodeBase chaincodeBase, final ChaincodeServerProperties chaincodeServerProperties) throws IOException { if (chaincodeBase == null) { throw new IllegalArgumentException("chaincode must be specified"); } if (chaincodeServerProperties == null) { throw new IllegalArgumentException("chaincodeServerProperties must be specified"); } chaincodeServerProperties.validate(); final NettyServerBuilder serverBuilder = NettyServerBuilder.forAddress( chaincodeServerProperties.getServerAddress()) .addService(new ChatChaincodeWithPeer(chaincodeBase)) .keepAliveTime(chaincodeServerProperties.getKeepAliveTimeMinutes(), TimeUnit.MINUTES) .keepAliveTimeout(chaincodeServerProperties.getKeepAliveTimeoutSeconds(), TimeUnit.SECONDS) .permitKeepAliveTime(chaincodeServerProperties.getPermitKeepAliveTimeMinutes(), TimeUnit.MINUTES) .permitKeepAliveWithoutCalls(chaincodeServerProperties.isPermitKeepAliveWithoutCalls()) .maxConnectionAge(chaincodeServerProperties.getMaxConnectionAgeSeconds(), TimeUnit.SECONDS) .maxInboundMetadataSize(chaincodeServerProperties.getMaxInboundMetadataSize()) .maxInboundMessageSize(chaincodeServerProperties.getMaxInboundMessageSize()); if (chaincodeServerProperties.isTlsEnabled()) { configureTls(serverBuilder, chaincodeServerProperties); } if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("<<<<<<<<<<<<>>>>>>>>>>>:\n"); LOGGER.info("ServerAddress:" + chaincodeServerProperties.getServerAddress().toString()); LOGGER.info("MaxInboundMetadataSize:" + chaincodeServerProperties.getMaxInboundMetadataSize()); LOGGER.info("MaxInboundMessageSize:" + chaincodeServerProperties.getMaxInboundMessageSize()); LOGGER.info("MaxConnectionAgeSeconds:" + chaincodeServerProperties.getMaxConnectionAgeSeconds()); LOGGER.info("KeepAliveTimeoutSeconds:" + chaincodeServerProperties.getKeepAliveTimeoutSeconds()); LOGGER.info("PermitKeepAliveTimeMinutes:" + chaincodeServerProperties.getPermitKeepAliveTimeMinutes()); LOGGER.info("KeepAliveTimeMinutes:" + chaincodeServerProperties.getKeepAliveTimeMinutes()); LOGGER.info("PermitKeepAliveWithoutCalls:" + chaincodeServerProperties.getPermitKeepAliveWithoutCalls()); LOGGER.info("KeyPassword:" + chaincodeServerProperties.getKeyPassword()); LOGGER.info("KeyCertChainFile:" + chaincodeServerProperties.getKeyCertChainFile()); LOGGER.info("KeyFile:" + chaincodeServerProperties.getKeyFile()); LOGGER.info("isTlsEnabled:" + chaincodeServerProperties.isTlsEnabled()); LOGGER.info("\n"); } this.server = serverBuilder.build(); } private static void configureTls( final NettyServerBuilder serverBuilder, final ChaincodeServerProperties chaincodeServerProperties) throws SSLException { final File keyCertChainFile = Paths.get(chaincodeServerProperties.getKeyCertChainFile()).toFile(); final File keyFile = Paths.get(chaincodeServerProperties.getKeyFile()).toFile(); final SslContextBuilder sslContextBuilder; if (chaincodeServerProperties.getKeyPassword() == null || chaincodeServerProperties.getKeyPassword().isEmpty()) { sslContextBuilder = SslContextBuilder.forServer(keyCertChainFile, keyFile); } else { sslContextBuilder = SslContextBuilder.forServer(keyCertChainFile, keyFile, chaincodeServerProperties.getKeyPassword()); } ApplicationProtocolConfig apn = new ApplicationProtocolConfig( ApplicationProtocolConfig.Protocol.ALPN, ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE, ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT, ApplicationProtocolNames.HTTP_2); sslContextBuilder.applicationProtocolConfig(apn); if (chaincodeServerProperties.getTrustCertCollectionFile() != null) { final File trustCertCollectionFile = Paths.get(chaincodeServerProperties.getTrustCertCollectionFile()) .toFile(); sslContextBuilder.clientAuth(ClientAuth.REQUIRE); sslContextBuilder.trustManager(trustCertCollectionFile); } serverBuilder.sslContext(sslContextBuilder.build()); } /** * start grpc server. * * @throws IOException */ @SuppressWarnings("PMD.SystemPrintln") @Override public void start() throws IOException { LOGGER.info("start grpc server"); Runtime.getRuntime().addShutdownHook(new Thread(() -> { // Use stderr here since the logger may have been reset by its JVM shutdown hook. System.err.println("*** shutting down gRPC server since JVM is shutting down"); stop(); System.err.println("*** server shut down"); })); server.start(); } /** * Waits for the server to become terminated. * * @throws InterruptedException */ @Override public void blockUntilShutdown() throws InterruptedException { LOGGER.info("Waits for the server to become terminated."); server.awaitTermination(); } /** shutdown now grpc server. */ @Override public void stop() { LOGGER.info("shutdown now grpc server."); server.shutdownNow(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ResponseUtils.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR; import static org.hyperledger.fabric.shim.Chaincode.Response.Status.SUCCESS; import org.hyperledger.fabric.Logger; public final class ResponseUtils { private static Logger logger = Logger.getLogger(ResponseUtils.class.getName()); private ResponseUtils() {} /** * @param message * @param payload * @return Chaincode.Response */ public static Chaincode.Response newSuccessResponse(final String message, final byte[] payload) { return new Chaincode.Response(SUCCESS, message, payload); } /** @return Chaincode.Response */ public static Chaincode.Response newSuccessResponse() { return newSuccessResponse(null, null); } /** * @param message * @return Chaincode.Response */ public static Chaincode.Response newSuccessResponse(final String message) { return newSuccessResponse(message, null); } /** * @param payload * @return Chaincode.Response */ public static Chaincode.Response newSuccessResponse(final byte[] payload) { return newSuccessResponse(null, payload); } /** * @param message * @param payload * @return Chaincode.Response */ public static Chaincode.Response newErrorResponse(final String message, final byte[] payload) { return new Chaincode.Response(INTERNAL_SERVER_ERROR, message, payload); } /** @return Chaincode.Response */ public static Chaincode.Response newErrorResponse() { return newErrorResponse(null, null); } /** * @param message * @return Chaincode.Response */ public static Chaincode.Response newErrorResponse(final String message) { return newErrorResponse(message, null); } /** * @param payload * @return Chaincode.Response */ public static Chaincode.Response newErrorResponse(final byte[] payload) { return newErrorResponse(null, payload); } /** * @param throwable * @return Chaincode.Response */ public static Chaincode.Response newErrorResponse(final Throwable throwable) { // Responses should not include internals like stack trace but make sure it gets // logged logger.error(() -> logger.formatError(throwable)); if (throwable instanceof ChaincodeException) { String message = throwable.getMessage(); byte[] payload = ((ChaincodeException) throwable).getPayload(); return new Chaincode.Response(INTERNAL_SERVER_ERROR, message, payload); } return newErrorResponse("Unexpected error", null); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/StateBasedEndorsement.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe; import java.util.HashMap; import java.util.List; import java.util.Map; /** * StateBasedEndorsement provides a set of convenience methods to create and modify a state-based endorsement policy. * Endorsement policies created by this convenience layer will always be a logical AND of "ORG.peer" principals for one * or more ORGs specified by the caller. */ public interface StateBasedEndorsement { /** * Get the endorsement policy as bytes. * * @return the endorsement policy as bytes */ byte[] policy(); /** * Adds the specified orgs to the list of orgs that are required to endorse. All orgs MSP role types will be set to * the role that is specified in the first parameter. Among other aspects the desired role depends on the channel's * configuration: if it supports node OUs, it is likely going to be the PEER role, while the MEMBER role is the * suited one if it does not. * * @param roleType the MSP role type * @param organizations the list of organizations */ void addOrgs(RoleType roleType, String... organizations); /** * deletes the specified channel orgs from the existing key-level endorsement policy for this KVS key. * * @param organizations the list of organizations */ void delOrgs(String... organizations); /** * Returns an array of channel orgs that are required to endorse changes. * * @return List of organizations */ List listOrgs(); /** RoleType of an endorsement policy's identity. */ @SuppressWarnings("PMD.FieldNamingConventions") enum RoleType { /** RoleTypeMember identifies an org's member identity. */ RoleTypeMember("MEMBER"), /** RoleTypePeer identifies an org's peer identity. */ RoleTypePeer("PEER"); private static final Map reverseLookup = new HashMap<>(); static { for (final RoleType item : values()) { reverseLookup.put(item.getVal(), item); } } private final String val; RoleType(final String val) { this.val = val; } /** @return String value */ public String getVal() { return val; } /** * @param val * @return RoleType */ public static RoleType forVal(final String val) { if (!reverseLookup.containsKey(val)) { throw new IllegalArgumentException("role type " + val + " does not exist"); } return reverseLookup.get(val); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementFactory.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; /** Factory for {@link StateBasedEndorsement} objects. */ public class StateBasedEndorsementFactory { private static final StateBasedEndorsementFactory INSTANCE = new StateBasedEndorsementFactory(); /** @return Endorsement Factory */ public static StateBasedEndorsementFactory getInstance() { return INSTANCE; } /** * Constructs a state-based endorsement policy from a given serialized EP byte array. If the byte array is empty, a * new EP is created. * * @param ep serialized endorsement policy * @return New StateBasedEndorsement instance */ public StateBasedEndorsement newStateBasedEndorsement(final byte[] ep) { return new StateBasedEndorsementImpl(ep); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImpl.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperledger.fabric.protos.common.MSPPrincipal; import org.hyperledger.fabric.protos.common.MSPPrincipal.Classification; import org.hyperledger.fabric.protos.common.MSPRole; import org.hyperledger.fabric.protos.common.MSPRole.MSPRoleType; import org.hyperledger.fabric.protos.common.SignaturePolicy; import org.hyperledger.fabric.protos.common.SignaturePolicyEnvelope; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; /** Implements {@link StateBasedEndorsement}. */ public final class StateBasedEndorsementImpl implements StateBasedEndorsement { @SuppressWarnings("PMD.ProperLogger") // PMD 7.12.0 reports a false positive private static final Log LOGGER = LogFactory.getLog(StateBasedEndorsementImpl.class); private final Map orgs = new HashMap<>(); StateBasedEndorsementImpl(final byte[] ep) { byte[] sbe; if (ep == null) { sbe = new byte[] {}; } else { sbe = ep; } try { final SignaturePolicyEnvelope spe = SignaturePolicyEnvelope.parseFrom(sbe); setMSPIDsFromSP(spe); } catch (final InvalidProtocolBufferException e) { throw new IllegalArgumentException("error unmarshalling endorsement policy bytes", e); } } @Override public byte[] policy() { final SignaturePolicyEnvelope spe = policyFromMSPIDs(); return spe.toByteArray(); } @Override public void addOrgs(final RoleType role, final String... organizations) { MSPRoleType mspRole; if (RoleType.RoleTypeMember == role) { mspRole = MSPRoleType.MEMBER; } else { mspRole = MSPRoleType.PEER; } for (final String neworg : organizations) { orgs.put(neworg, mspRole); } } @Override public void delOrgs(final String... organizations) { for (final String delorg : organizations) { orgs.remove(delorg); } } @Override public List listOrgs() { final List res = new ArrayList<>(); res.addAll(orgs.keySet()); return res; } private void setMSPIDsFromSP(final SignaturePolicyEnvelope spe) { spe.getIdentitiesList().stream() .filter(identity -> Classification.ROLE == identity.getPrincipalClassification()) .forEach(this::addOrg); } private void addOrg(final MSPPrincipal identity) { try { final MSPRole mspRole = MSPRole.parseFrom(identity.getPrincipal()); orgs.put(mspRole.getMspIdentifier(), mspRole.getRole()); } catch (final InvalidProtocolBufferException e) { LOGGER.warn("error unmarshalling msp principal"); throw new IllegalArgumentException("error unmarshalling msp principal", e); } } private SignaturePolicyEnvelope policyFromMSPIDs() { final List mspids = listOrgs(); mspids.sort(Comparator.naturalOrder()); final List principals = new ArrayList<>(); final List sigpolicy = new ArrayList<>(); for (int i = 0; i < mspids.size(); i++) { final String mspid = mspids.get(i); principals.add(MSPPrincipal.newBuilder() .setPrincipalClassification(Classification.ROLE) .setPrincipal(MSPRole.newBuilder() .setMspIdentifier(mspid) .setRole(orgs.get(mspid)) .build() .toByteString()) .build()); sigpolicy.add(StateBasedEndorsementUtils.signedBy(i)); } // create the policy: it requires exactly 1 signature from all of the principals return SignaturePolicyEnvelope.newBuilder() .setVersion(0) .setRule(StateBasedEndorsementUtils.nOutOf(mspids.size(), sigpolicy)) .addAllIdentities(principals) .build(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementUtils.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; import java.util.Arrays; import java.util.List; import org.hyperledger.fabric.protos.common.MSPPrincipal; import org.hyperledger.fabric.protos.common.MSPPrincipal.Classification; import org.hyperledger.fabric.protos.common.MSPRole; import org.hyperledger.fabric.protos.common.MSPRole.MSPRoleType; import org.hyperledger.fabric.protos.common.SignaturePolicy; import org.hyperledger.fabric.protos.common.SignaturePolicy.NOutOf; import org.hyperledger.fabric.protos.common.SignaturePolicyEnvelope; /** Utility to create {@link SignaturePolicy} and {@link SignaturePolicyEnvelope}. */ public final class StateBasedEndorsementUtils { private StateBasedEndorsementUtils() {} /** * Creates a SignaturePolicy requiring a given signer's signature. * * @param index * @return SignaturePolicy */ static SignaturePolicy signedBy(final int index) { return SignaturePolicy.newBuilder().setSignedBy(index).build(); } /** * Create a policy. * *

Creates a policy which requires N out of the slice of policies to evaluate to true * * @param n * @param policies * @return SignaturePolicy */ static SignaturePolicy nOutOf(final int n, final List policies) { return SignaturePolicy.newBuilder() .setNOutOf(NOutOf.newBuilder().setN(n).addAllRules(policies).build()) .build(); } /** * Creates a {@link SignaturePolicyEnvelope} requiring 1 signature from any fabric entity, having the passed role, * of the specified MSP. * * @param mspId * @param role * @return SignaturePolicy */ static SignaturePolicyEnvelope signedByFabricEntity(final String mspId, final MSPRoleType role) { // specify the principal: it's a member of the msp we just found final MSPPrincipal principal = MSPPrincipal.newBuilder() .setPrincipalClassification(Classification.ROLE) .setPrincipal(MSPRole.newBuilder() .setMspIdentifier(mspId) .setRole(role) .build() .toByteString()) .build(); // create the policy: it requires exactly 1 signature from the first (and only) // principal return SignaturePolicyEnvelope.newBuilder() .setVersion(0) .setRule(nOutOf(1, Arrays.asList(signedBy(0)))) .addIdentities(principal) .build(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/impl/package-info.java ================================================ /* * Copyright 2023 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ext/sbe/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** Provides an interface for creating and modifying state-based endorsement policies. */ package org.hyperledger.fabric.shim.ext.sbe; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeInvocationTask.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.COMPLETED; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.ERROR; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.RESPONSE; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.StatusCode; import java.security.NoSuchAlgorithmException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.traces.Traces; /** A 'Callable' implementation the has the job of invoking the chaincode, and matching the response and requests. */ @SuppressWarnings("PMD.MoreThanOneLogger") public class ChaincodeInvocationTask implements Callable { private static final Logger LOGGER = Logger.getLogger(ChaincodeInvocationTask.class.getName()); private static final Logger PERFLOGGER = Logger.getLogger(Logging.PERFLOGGER); private final String key; private final Type type; private final String txId; private final Consumer outgoingMessageConsumer; // default to size 2: Based on the protocol there *should* only ever be one thing within this // ArrayBlockingQueue provides the correct semantics out-of-the-box. // We want consumers to be block waiting on a message to be 'posted' but for the putter to not be held // up if there's no body waiting. // // Usual case should be the main thread is waiting for something to come back private final BlockingQueue postbox = new ArrayBlockingQueue<>(2, true); private final ChaincodeMessage message; private final Chaincode chaincode; /** * @param message The incoming message that has triggered this task into execution * @param type Is this init or invoke? (v2 Fabric deprecates init) * @param outgoingMessage The Consumer functional interface to send any requests for ledger state * @param chaincode A instance of the end users chaincode */ public ChaincodeInvocationTask( final ChaincodeMessage message, final Type type, final Consumer outgoingMessage, final Chaincode chaincode) { this.key = message.getChannelId() + message.getTxid(); this.type = type; this.outgoingMessageConsumer = outgoingMessage; this.txId = message.getTxid(); this.chaincode = chaincode; this.message = message; } /** Main method to power the invocation of the chaincode. */ @Override @SuppressWarnings("PMD.AvoidCatchingGenericException") public ChaincodeMessage call() { ChaincodeMessage finalResponseMessage; Span span = null; try { try { PERFLOGGER.fine(() -> "> task:start TX::" + this.txId); // A key interface for the chaincode's invoke() method implementation // is the 'ChaincodeStub' interface. An instance of this is created // per transaction invocation. // // This needs to be passed the message triggering the invoke, as well // as the interface to be used for sending any requests to the peer final ChaincodeStub stub = new InvocationStubImpl(message, this); span = Traces.getProvider().createSpan(stub); // result is what will be sent to the peer as a response to this invocation final Chaincode.Response result; PERFLOGGER.fine(() -> "> task:invoke TX::" + this.txId); // Call chaincode's invoke // Note in Fabric v2, there won't be any INIT if (this.type == Type.INIT) { result = chaincode.init(stub); } else { result = chaincode.invoke(stub); } PERFLOGGER.fine(() -> "< task:invoke TX::" + this.txId); if (result.getStatus().getCode() >= Chaincode.Response.Status.INTERNAL_SERVER_ERROR.getCode()) { // Send ERROR with entire result.Message as payload LOGGER.severe(() -> String.format( "[%-8.8s] Invoke failed with error code %d. Sending %s", message.getTxid(), result.getStatus().getCode(), ERROR)); finalResponseMessage = ChaincodeMessageFactory.newCompletedEventMessage( message.getChannelId(), message.getTxid(), result, stub.getEvent()); if (span != null) { span.setStatus(StatusCode.ERROR, result.getMessage()); } } else { // Send COMPLETED with entire result as payload LOGGER.fine( () -> String.format("[%-8.8s] Invoke succeeded. Sending %s", message.getTxid(), COMPLETED)); finalResponseMessage = ChaincodeMessageFactory.newCompletedEventMessage( message.getChannelId(), message.getTxid(), result, stub.getEvent()); } } catch (InvalidProtocolBufferException | NoSuchAlgorithmException | RuntimeException e) { LOGGER.severe( () -> String.format("[%-8.8s] Invoke failed. Sending %s: %s", message.getTxid(), ERROR, e)); finalResponseMessage = ChaincodeMessageFactory.newErrorEventMessage(message.getChannelId(), message.getTxid(), e); if (span != null) { span.setStatus(StatusCode.ERROR, e.getMessage()); } } // send the final response message to the peer outgoingMessageConsumer.accept(finalResponseMessage); PERFLOGGER.fine(() -> "< task:end TX::" + this.txId); } finally { if (span != null) { span.end(); } } return null; } /** * Identifier of this task, channel id and transaction id. * * @return String */ public String getTxKey() { return this.key; } /** * Use the Key as to determine equality. * * @param other * @return equality */ @Override public boolean equals(final Object other) { if (!(other instanceof ChaincodeInvocationTask)) { return false; } ChaincodeInvocationTask that = (ChaincodeInvocationTask) other; return this.key.equals(that.getTxKey()); } @Override public int hashCode() { return key.hashCode(); } /** * Posts the message that the peer has responded with to this task's request Uses an 'ArrayBlockingQueue'. This lets * the producer post messages without waiting for the consumer. And the consumer can block until a message is * posted. * *

In this case the data is only passed to the executing tasks. * * @param msg Chaincode message to pass pack * @throws InterruptedException should something really really go wrong */ public void postMessage(final ChaincodeMessage msg) throws InterruptedException { // put to the postbox waiting for space to become available if needed postbox.put(msg); } /** * Send the chaincode message back to the peer. * *

Implementation of the Functional interface 'InvokeChaincodeSupport' * *

It will send the message, via the outgoingMessageConsumer, and then block on the 'Exchanger' to wait for the * response to come. * *

This Exchange is an atomic operation between the thread that is running this task, and the thread that is * handling the communication from the peer. * * @param message The chaincode message from the peer * @return ByteString to be parsed by the caller */ protected ByteString invoke(final ChaincodeMessage message) { // send the message LOGGER.fine(() -> "Task Sending message to the peer " + message.getTxid()); outgoingMessageConsumer.accept(message); // wait for response ChaincodeMessage response; try { PERFLOGGER.fine(() -> "> task:answer TX::" + message.getTxid()); response = postbox.take(); PERFLOGGER.fine(() -> "< task:answer TX::" + message.getTxid()); } catch (final InterruptedException e) { LOGGER.severe(() -> "Interrupted exchanging messages "); throw new ContractRuntimeException(String.format("[%-8.8s]InterruptedException received.", txId), e); } // handle response switch (response.getType()) { case RESPONSE: LOGGER.fine(() -> String.format("[%-8.8s] Successful response received.", txId)); return response.getPayload(); case ERROR: LOGGER.severe(() -> String.format("[%-8.8s] Unsuccessful response received.", txId)); throw new ContractRuntimeException(String.format("[%-8.8s]Unsuccessful response received.", txId)); default: LOGGER.severe(() -> String.format( "[%-8.8s] Unexpected %s response received. Expected %s or %s.", txId, response.getType(), RESPONSE, ERROR)); throw new IllegalStateException(String.format( "[%-8.8s] Unexpected %s response received. Expected %s or %s.", txId, response.getType(), RESPONSE, ERROR)); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeMessageFactory.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.COMPLETED; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.DEL_STATE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.ERROR; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_PRIVATE_DATA_HASH; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_STATE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_STATE_METADATA; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.INVOKE_CHAINCODE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.PUT_STATE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.PUT_STATE_METADATA; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.REGISTER; import com.google.protobuf.ByteString; import java.io.PrintWriter; import java.io.StringWriter; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type; import org.hyperledger.fabric.protos.peer.DelState; import org.hyperledger.fabric.protos.peer.GetState; import org.hyperledger.fabric.protos.peer.GetStateMetadata; import org.hyperledger.fabric.protos.peer.PutState; import org.hyperledger.fabric.protos.peer.PutStateMetadata; import org.hyperledger.fabric.protos.peer.Response; import org.hyperledger.fabric.protos.peer.Response.Builder; import org.hyperledger.fabric.protos.peer.StateMetadata; import org.hyperledger.fabric.shim.Chaincode; public final class ChaincodeMessageFactory { private ChaincodeMessageFactory() {} static ChaincodeMessage newGetPrivateDataHashEventMessage( final String channelId, final String txId, final String collection, final String key) { return newEventMessage( GET_PRIVATE_DATA_HASH, channelId, txId, GetState.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString()); } static ChaincodeMessage newGetStateEventMessage( final String channelId, final String txId, final String collection, final String key) { return newEventMessage( GET_STATE, channelId, txId, GetState.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString()); } static ChaincodeMessage newGetStateMetadataEventMessage( final String channelId, final String txId, final String collection, final String key) { return newEventMessage( GET_STATE_METADATA, channelId, txId, GetStateMetadata.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString()); } static ChaincodeMessage newPutStateEventMessage( final String channelId, final String txId, final String collection, final String key, final ByteString value) { return newEventMessage( PUT_STATE, channelId, txId, PutState.newBuilder() .setCollection(collection) .setKey(key) .setValue(value) .build() .toByteString()); } static ChaincodeMessage newPutStateMetadataEventMessage( final String channelId, final String txId, final String collection, final String key, final String metakey, final ByteString value) { return newEventMessage( PUT_STATE_METADATA, channelId, txId, PutStateMetadata.newBuilder() .setCollection(collection) .setKey(key) .setMetadata(StateMetadata.newBuilder() .setMetakey(metakey) .setValue(value) .build()) .build() .toByteString()); } static ChaincodeMessage newDeleteStateEventMessage( final String channelId, final String txId, final String collection, final String key) { return newEventMessage( DEL_STATE, channelId, txId, DelState.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString()); } static ChaincodeMessage newPurgeStateEventMessage( final String channelId, final String txId, final String collection, final String key) { return newEventMessage( Type.PURGE_PRIVATE_DATA, channelId, txId, DelState.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString()); } static ChaincodeMessage newErrorEventMessage(final String channelId, final String txId, final Throwable throwable) { return newErrorEventMessage(channelId, txId, printStackTrace(throwable)); } static ChaincodeMessage newErrorEventMessage(final String channelId, final String txId, final String message) { return newErrorEventMessage(channelId, txId, message, null); } static ChaincodeMessage newErrorEventMessage( final String channelId, final String txId, final String message, final ChaincodeEvent event) { return newEventMessage(ERROR, channelId, txId, ByteString.copyFromUtf8(message), event); } static ChaincodeMessage newCompletedEventMessage( final String channelId, final String txId, final Chaincode.Response response, final ChaincodeEvent event) { return newEventMessage( COMPLETED, channelId, txId, toProtoResponse(response).toByteString(), event); } static ChaincodeMessage newInvokeChaincodeMessage( final String channelId, final String txId, final ByteString payload) { return newEventMessage(INVOKE_CHAINCODE, channelId, txId, payload, null); } static ChaincodeMessage newRegisterChaincodeMessage(final ChaincodeID chaincodeId) { return ChaincodeMessage.newBuilder() .setType(REGISTER) .setPayload(chaincodeId.toByteString()) .build(); } static ChaincodeMessage newEventMessage( final Type type, final String channelId, final String txId, final ByteString payload) { return newEventMessage(type, channelId, txId, payload, null); } static ChaincodeMessage newEventMessage( final Type type, final String channelId, final String txId, final ByteString payload, final ChaincodeEvent event) { final ChaincodeMessage.Builder builder = ChaincodeMessage.newBuilder() .setType(type) .setChannelId(channelId) .setTxid(txId) .setPayload(payload); if (event != null) { builder.setChaincodeEvent(event); } return builder.build(); } private static Response toProtoResponse(final Chaincode.Response response) { final Builder builder = Response.newBuilder(); builder.setStatus(response.getStatus().getCode()); if (response.getMessage() != null) { builder.setMessage(response.getMessage()); } if (response.getPayload() != null) { builder.setPayload(ByteString.copyFrom(response.getPayload())); } return builder.build(); } private static String printStackTrace(final Throwable throwable) { if (throwable == null) { return null; } final StringWriter buffer = new StringWriter(); throwable.printStackTrace(new PrintWriter(buffer)); return buffer.toString(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/ChaincodeSupportClient.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import io.grpc.ClientInterceptor; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc; import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc.ChaincodeSupportStub; import org.hyperledger.fabric.traces.Traces; public class ChaincodeSupportClient { private static final int DEFAULT_TIMEOUT = 5; private static final Logger PERFLOGGER = Logger.getLogger(Logging.PERFLOGGER); private final ManagedChannel channel; private final ChaincodeSupportStub stub; /** @param channelBuilder */ public ChaincodeSupportClient(final ManagedChannelBuilder channelBuilder) { ClientInterceptor interceptor = Traces.getProvider().createInterceptor(); if (interceptor != null) { channelBuilder.intercept(interceptor); } this.channel = channelBuilder.build(); this.stub = ChaincodeSupportGrpc.newStub(channel); } /** @param itm */ public void shutdown(final InvocationTaskManager itm) { // first shutdown the thread pool itm.shutdown(); try { this.channel.shutdown(); if (!channel.awaitTermination(DEFAULT_TIMEOUT, TimeUnit.SECONDS)) { channel.shutdownNow(); } } catch (final InterruptedException e) { channel.shutdownNow(); Thread.currentThread().interrupt(); } } /** * @param itm * @param requestObserver * @throws IOException verify parameters error */ public void start(final InvocationTaskManager itm, final StreamObserver requestObserver) throws IOException { if (requestObserver == null) { throw new IOException("StreamObserver 'requestObserver' for chat with peer can't be null"); } if (itm == null) { throw new IOException("InnvocationTaskManager 'itm' can't be null"); } // Consumer function for response messages (those going back to the peer) // gRPC streams need to be accessed by one thread at a time, so // use a lock to protect this. // // Previous implementations used a dedicated thread for this. However this extra // thread is not really required. The main thread executing the transaction will // not be // held up for long, nor can any one transaction invoke more that one stub api // at a time. // create a lock, with fair property final ReentrantLock lock = new ReentrantLock(true); final Consumer consumer = t -> { lock.lock(); PERFLOGGER.fine(() -> "> sendToPeer TX::" + t.getTxid()); requestObserver.onNext(t); PERFLOGGER.fine(() -> "< sendToPeer TX::" + t.getTxid()); lock.unlock(); }; // Pass a Consumer interface back to the the task manager. This is for tasks to // use to respond back to the peer. // // NOTE the register() - very important - as this triggers the ITM to send the // first message to the peer; otherwise the both sides will sit there waiting itm.setResponseConsumer(consumer); itm.register(); } /** * ChaincodeSupportStub. * * @return stub */ public ChaincodeSupportStub getStub() { return stub; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationStubImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static java.util.stream.Collectors.toList; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.COMPLETED; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_HISTORY_FOR_KEY; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_PRIVATE_DATA_HASH; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_QUERY_RESULT; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_STATE_BY_RANGE; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Timestamp; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Function; import java.util.logging.Logger; import java.util.stream.Collectors; import org.hyperledger.fabric.protos.common.ChannelHeader; import org.hyperledger.fabric.protos.common.Header; import org.hyperledger.fabric.protos.common.HeaderType; import org.hyperledger.fabric.protos.common.SignatureHeader; import org.hyperledger.fabric.protos.ledger.queryresult.KV; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeInput; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeProposalPayload; import org.hyperledger.fabric.protos.peer.ChaincodeSpec; import org.hyperledger.fabric.protos.peer.GetQueryResult; import org.hyperledger.fabric.protos.peer.GetState; import org.hyperledger.fabric.protos.peer.GetStateByRange; import org.hyperledger.fabric.protos.peer.MetaDataKeys; import org.hyperledger.fabric.protos.peer.Proposal; import org.hyperledger.fabric.protos.peer.QueryMetadata; import org.hyperledger.fabric.protos.peer.QueryResultBytes; import org.hyperledger.fabric.protos.peer.Response; import org.hyperledger.fabric.protos.peer.SignedProposal; import org.hyperledger.fabric.protos.peer.StateMetadataResult; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ledger.CompositeKey; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.GodClass"}) class InvocationStubImpl implements ChaincodeStub { private static final String UNSPECIFIED_START_KEY = new String(Character.toChars(0x000001)); private static final String UNSPECIFIED_END_KEY = ""; private static final Logger LOGGER = Logger.getLogger(InvocationStubImpl.class.getName()); public static final String MAX_UNICODE_RUNE = "\udbff\udfff"; private static final String CORE_PEER_LOCALMSPID = "CORE_PEER_LOCALMSPID"; private static final Function QUERY_RESULT_BYTES_TO_KEY_MODIFICATION = queryResultBytes -> { try { return org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.parseFrom( queryResultBytes.getResultBytes()); } catch (final InvalidProtocolBufferException e) { throw new UncheckedIOException(e); } }; private static final Function QUERY_RESULT_BYTES_TO_KV = queryResultBytes -> { try { return KV.parseFrom(queryResultBytes.getResultBytes()); } catch (final InvalidProtocolBufferException e) { throw new UncheckedIOException(e); } }; private final String channelId; private final String txId; private final ChaincodeInvocationTask handler; private final List args; private final SignedProposal signedProposal; private final Instant txTimestamp; private final ByteString creator; private final Map transientMap; private final byte[] binding; private ChaincodeEvent event; /** * @param message * @param handler * @throws InvalidProtocolBufferException */ InvocationStubImpl(final ChaincodeMessage message, final ChaincodeInvocationTask handler) throws InvalidProtocolBufferException, NoSuchAlgorithmException { this.channelId = message.getChannelId(); this.txId = message.getTxid(); this.handler = handler; final ChaincodeInput input = ChaincodeInput.parseFrom(message.getPayload()); this.args = Collections.unmodifiableList(input.getArgsList()); this.signedProposal = message.getProposal(); if (this.signedProposal.getProposalBytes().isEmpty()) { this.creator = null; this.txTimestamp = null; this.transientMap = Collections.emptyMap(); this.binding = null; } else { final Proposal proposal = Proposal.parseFrom(signedProposal.getProposalBytes()); final Header header = Header.parseFrom(proposal.getHeader()); final ChannelHeader channelHeader = ChannelHeader.parseFrom(header.getChannelHeader()); validateProposalType(channelHeader); final SignatureHeader signatureHeader = SignatureHeader.parseFrom(header.getSignatureHeader()); final ChaincodeProposalPayload chaincodeProposalPayload = ChaincodeProposalPayload.parseFrom(proposal.getPayload()); final Timestamp timestamp = channelHeader.getTimestamp(); this.txTimestamp = Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos()); this.creator = signatureHeader.getCreator(); this.transientMap = chaincodeProposalPayload.getTransientMapMap(); this.binding = computeBinding(channelHeader, signatureHeader); } } private static boolean isEmptyString(final String str) { for (int i = 0; i < str.length(); i++) { if (!Character.isWhitespace(str.charAt(i))) { return false; } } return true; } private byte[] computeBinding(final ChannelHeader channelHeader, final SignatureHeader signatureHeader) throws NoSuchAlgorithmException { final MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(signatureHeader.getNonce().asReadOnlyByteBuffer()); messageDigest.update(this.creator.asReadOnlyByteBuffer()); final ByteBuffer epochBytes = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN).putLong(channelHeader.getEpoch()); epochBytes.flip(); messageDigest.update(epochBytes); return messageDigest.digest(); } private void validateProposalType(final ChannelHeader channelHeader) { switch (HeaderType.forNumber(channelHeader.getType())) { case ENDORSER_TRANSACTION: case CONFIG: return; default: throw new IllegalArgumentException(String.format( "Unexpected transaction type: %s", HeaderType.forNumber(channelHeader.getType()))); } } @Override public List getArgs() { return args.stream().map(ByteString::toByteArray).collect(toList()); } @Override public List getStringArgs() { return args.stream().map(ByteString::toStringUtf8).collect(toList()); } @Override public String getFunction() { return getStringArgs().isEmpty() ? null : getStringArgs().get(0); } @Override public List getParameters() { return getStringArgs().stream().skip(1).collect(toList()); } @Override public void setEvent(final String name, final byte[] payload) { if (null == name || isEmptyString(name)) { throw new IllegalArgumentException("event name can not be nil string"); } if (payload != null) { this.event = ChaincodeEvent.newBuilder() .setEventName(name) .setPayload(ByteString.copyFrom(payload)) .build(); } else { this.event = ChaincodeEvent.newBuilder().setEventName(name).build(); } } @Override public ChaincodeEvent getEvent() { return event; } @Override public String getChannelId() { return channelId; } @Override public String getTxId() { return txId; } @Override public byte[] getState(final String key) { return this.handler .invoke(ChaincodeMessageFactory.newGetStateEventMessage(channelId, txId, "", key)) .toByteArray(); } @Override @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull") public byte[] getStateValidationParameter(final String key) { final ByteString payload = handler.invoke(ChaincodeMessageFactory.newGetStateMetadataEventMessage(channelId, txId, "", key)); try { final StateMetadataResult stateMetadataResult = StateMetadataResult.parseFrom(payload); final Map stateMetadataMap = new HashMap<>(); stateMetadataResult .getEntriesList() .forEach(entry -> stateMetadataMap.put(entry.getMetakey(), entry.getValue())); if (stateMetadataMap.containsKey(MetaDataKeys.VALIDATION_PARAMETER.toString())) { return stateMetadataMap .get(MetaDataKeys.VALIDATION_PARAMETER.toString()) .toByteArray(); } } catch (final InvalidProtocolBufferException e) { LOGGER.severe(() -> String.format("[%-8.8s] unmarshalling error", txId)); throw new UncheckedIOException("Error unmarshalling StateMetadataResult.", e); } return null; } @Override public void putState(final String key, final byte[] value) { validateKey(key); this.handler.invoke( ChaincodeMessageFactory.newPutStateEventMessage(channelId, txId, "", key, ByteString.copyFrom(value))); } @Override public void setStateValidationParameter(final String key, final byte[] value) { validateKey(key); final ChaincodeMessage msg = ChaincodeMessageFactory.newPutStateMetadataEventMessage( channelId, txId, "", key, MetaDataKeys.VALIDATION_PARAMETER.toString(), ByteString.copyFrom(value)); this.handler.invoke(msg); } @Override public void delState(final String key) { final ChaincodeMessage msg = ChaincodeMessageFactory.newDeleteStateEventMessage(channelId, txId, "", key); this.handler.invoke(msg); } @Override public QueryResultsIterator getStateByRange(final String startKey, final String endKey) { String start = startKey; String end = endKey; if (startKey == null || startKey.isEmpty()) { start = UNSPECIFIED_START_KEY; } if (endKey == null) { end = UNSPECIFIED_END_KEY; } CompositeKey.validateSimpleKeys(start, end); return executeGetStateByRange("", start, end); } private QueryResultsIterator executeGetStateByRange( final String collection, final String startKey, final String endKey) { final ByteString requestPayload = GetStateByRange.newBuilder() .setCollection(collection) .setStartKey(startKey) .setEndKey(endKey) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_STATE_BY_RANGE, channelId, txId, requestPayload); final ByteString response = handler.invoke(requestMessage); return new QueryResultsIteratorImpl<>( this.handler, channelId, txId, response, QUERY_RESULT_BYTES_TO_KV.andThen(KeyValueImpl::new)); } @Override public QueryResultsIteratorWithMetadata getStateByRangeWithPagination( final String startKey, final String endKey, final int pageSize, final String bookmark) { String start = startKey; String end = endKey; if (startKey == null || startKey.isEmpty()) { start = UNSPECIFIED_START_KEY; } if (endKey == null) { end = UNSPECIFIED_END_KEY; } CompositeKey.validateSimpleKeys(start, end); final QueryMetadata queryMetadata = QueryMetadata.newBuilder() .setBookmark(bookmark) .setPageSize(pageSize) .build(); return executeGetStateByRangeWithMetadata("", start, end, queryMetadata.toByteString()); } private QueryResultsIteratorWithMetadataImpl executeGetStateByRangeWithMetadata( final String collection, final String startKey, final String endKey, final ByteString metadata) { final ByteString payload = GetStateByRange.newBuilder() .setCollection(collection) .setStartKey(startKey) .setEndKey(endKey) .setMetadata(metadata) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_STATE_BY_RANGE, channelId, txId, payload); final ByteString response = this.handler.invoke(requestMessage); return new QueryResultsIteratorWithMetadataImpl<>( this.handler, getChannelId(), getTxId(), response, QUERY_RESULT_BYTES_TO_KV.andThen(KeyValueImpl::new)); } @Override public QueryResultsIterator getStateByPartialCompositeKey(final String compositeKey) { CompositeKey key; if (compositeKey.startsWith(CompositeKey.NAMESPACE)) { key = CompositeKey.parseCompositeKey(compositeKey); } else { key = new CompositeKey(compositeKey); } return getStateByPartialCompositeKey(key); } @Override public QueryResultsIterator getStateByPartialCompositeKey( final String objectType, final String... attributes) { return getStateByPartialCompositeKey(new CompositeKey(objectType, attributes)); } @Override public QueryResultsIterator getStateByPartialCompositeKey(final CompositeKey compositeKey) { String cKeyAsString; if (compositeKey == null) { cKeyAsString = new CompositeKey(UNSPECIFIED_START_KEY).toString(); } else { cKeyAsString = compositeKey.toString(); } return executeGetStateByRange("", cKeyAsString, cKeyAsString + MAX_UNICODE_RUNE); } @Override public QueryResultsIteratorWithMetadata getStateByPartialCompositeKeyWithPagination( final CompositeKey compositeKey, final int pageSize, final String bookmark) { String cKeyAsString; if (compositeKey == null) { cKeyAsString = new CompositeKey(UNSPECIFIED_START_KEY).toString(); } else { cKeyAsString = compositeKey.toString(); } final QueryMetadata queryMetadata = QueryMetadata.newBuilder() .setBookmark(bookmark) .setPageSize(pageSize) .build(); return executeGetStateByRangeWithMetadata( "", cKeyAsString, cKeyAsString + MAX_UNICODE_RUNE, queryMetadata.toByteString()); } @Override public CompositeKey createCompositeKey(final String objectType, final String... attributes) { return new CompositeKey(objectType, attributes); } @Override public CompositeKey splitCompositeKey(final String compositeKey) { return CompositeKey.parseCompositeKey(compositeKey); } @Override public QueryResultsIterator getQueryResult(final String query) { final ByteString requestPayload = GetQueryResult.newBuilder() .setCollection("") .setQuery(query) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_QUERY_RESULT, channelId, txId, requestPayload); final ByteString response = handler.invoke(requestMessage); return new QueryResultsIteratorImpl<>( this.handler, channelId, txId, response, QUERY_RESULT_BYTES_TO_KV.andThen(KeyValueImpl::new)); } @Override public QueryResultsIteratorWithMetadata getQueryResultWithPagination( final String query, final int pageSize, final String bookmark) { final ByteString queryMetadataPayload = QueryMetadata.newBuilder() .setBookmark(bookmark) .setPageSize(pageSize) .build() .toByteString(); final ByteString requestPayload = GetQueryResult.newBuilder() .setCollection("") .setQuery(query) .setMetadata(queryMetadataPayload) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_QUERY_RESULT, channelId, txId, requestPayload); final ByteString response = handler.invoke(requestMessage); return new QueryResultsIteratorWithMetadataImpl<>( this.handler, channelId, txId, response, QUERY_RESULT_BYTES_TO_KV.andThen(KeyValueImpl::new)); } @Override public QueryResultsIterator getHistoryForKey(final String key) { final ByteString requestPayload = GetQueryResult.newBuilder() .setCollection("") .setQuery(key) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_HISTORY_FOR_KEY, channelId, txId, requestPayload); final ByteString response = handler.invoke(requestMessage); return new QueryResultsIteratorImpl<>( this.handler, channelId, txId, response, QUERY_RESULT_BYTES_TO_KEY_MODIFICATION.andThen(KeyModificationImpl::new)); } @Override public byte[] getPrivateData(final String collection, final String key) { validateCollection(collection); return this.handler .invoke(ChaincodeMessageFactory.newGetStateEventMessage(channelId, txId, collection, key)) .toByteArray(); } @Override public byte[] getPrivateDataHash(final String collection, final String key) { validateCollection(collection); final ByteString requestPayload = GetState.newBuilder() .setCollection(collection) .setKey(key) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_PRIVATE_DATA_HASH, channelId, txId, requestPayload); return handler.invoke(requestMessage).toByteArray(); } @Override @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull") public byte[] getPrivateDataValidationParameter(final String collection, final String key) { validateCollection(collection); final ByteString payload = handler.invoke( ChaincodeMessageFactory.newGetStateMetadataEventMessage(channelId, txId, collection, key)); try { final StateMetadataResult stateMetadataResult = StateMetadataResult.parseFrom(payload); final Map stateMetadataMap = new HashMap<>(); stateMetadataResult .getEntriesList() .forEach(entry -> stateMetadataMap.put(entry.getMetakey(), entry.getValue())); if (stateMetadataMap.containsKey(MetaDataKeys.VALIDATION_PARAMETER.toString())) { return stateMetadataMap .get(MetaDataKeys.VALIDATION_PARAMETER.toString()) .toByteArray(); } } catch (final InvalidProtocolBufferException e) { LOGGER.severe(() -> String.format("[%-8.8s] unmarshalling error", txId)); throw new UncheckedIOException("Error unmarshalling StateMetadataResult.", e); } return null; } @Override public void putPrivateData(final String collection, final String key, final byte[] value) { validateKey(key); validateCollection(collection); this.handler.invoke(ChaincodeMessageFactory.newPutStateEventMessage( channelId, txId, collection, key, ByteString.copyFrom(value))); } @Override public void setPrivateDataValidationParameter(final String collection, final String key, final byte[] value) { validateKey(key); validateCollection(collection); final ChaincodeMessage msg = ChaincodeMessageFactory.newPutStateMetadataEventMessage( channelId, txId, collection, key, MetaDataKeys.VALIDATION_PARAMETER.toString(), ByteString.copyFrom(value)); this.handler.invoke(msg); } @Override public void delPrivateData(final String collection, final String key) { validateCollection(collection); final ChaincodeMessage msg = ChaincodeMessageFactory.newDeleteStateEventMessage(channelId, txId, collection, key); this.handler.invoke(msg); } @Override public void purgePrivateData(final String collection, final String key) { validateCollection(collection); final ChaincodeMessage msg = ChaincodeMessageFactory.newPurgeStateEventMessage(channelId, txId, collection, key); this.handler.invoke(msg); } @Override public QueryResultsIterator getPrivateDataByRange( final String collection, final String startKey, final String endKey) { String start = startKey; String end = endKey; validateCollection(collection); if (startKey == null || startKey.isEmpty()) { start = UNSPECIFIED_START_KEY; } if (endKey == null) { end = UNSPECIFIED_END_KEY; } CompositeKey.validateSimpleKeys(start, end); return executeGetStateByRange(collection, start, end); } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String compositeKey) { CompositeKey key; if (compositeKey == null) { key = new CompositeKey(""); } else if (compositeKey.startsWith(CompositeKey.NAMESPACE)) { key = CompositeKey.parseCompositeKey(compositeKey); } else { key = new CompositeKey(compositeKey); } return getPrivateDataByPartialCompositeKey(collection, key); } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final CompositeKey compositeKey) { String cKeyAsString; if (compositeKey == null) { cKeyAsString = new CompositeKey(UNSPECIFIED_START_KEY).toString(); } else { cKeyAsString = compositeKey.toString(); } return executeGetStateByRange(collection, cKeyAsString, cKeyAsString + MAX_UNICODE_RUNE); } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String objectType, final String... attributes) { return getPrivateDataByPartialCompositeKey(collection, new CompositeKey(objectType, attributes)); } @Override public QueryResultsIterator getPrivateDataQueryResult(final String collection, final String query) { validateCollection(collection); final ByteString requestPayload = GetQueryResult.newBuilder() .setCollection(collection) .setQuery(query) .build() .toByteString(); final ChaincodeMessage requestMessage = ChaincodeMessageFactory.newEventMessage(GET_QUERY_RESULT, channelId, txId, requestPayload); final ByteString response = handler.invoke(requestMessage); return new QueryResultsIteratorImpl<>( this.handler, channelId, txId, response, QUERY_RESULT_BYTES_TO_KV.andThen(KeyValueImpl::new)); } @Override public Chaincode.Response invokeChaincode( final String chaincodeName, final List args, final String channel) { // internally we handle chaincode name as a composite name final String compositeName; if (channel != null && !isEmptyString(channel)) { compositeName = chaincodeName + "/" + channel; } else { compositeName = chaincodeName; } // create invocation specification of the chaincode to invoke final ByteString invocationSpecPayload = ChaincodeSpec.newBuilder() .setChaincodeId(ChaincodeID.newBuilder().setName(compositeName).build()) .setInput(ChaincodeInput.newBuilder() .addAllArgs(args.stream().map(ByteString::copyFrom).collect(toList())) .build()) .build() .toByteString(); final ChaincodeMessage invokeChaincodeMessage = ChaincodeMessageFactory.newInvokeChaincodeMessage(this.channelId, this.txId, invocationSpecPayload); final ByteString response = this.handler.invoke(invokeChaincodeMessage); try { // response message payload should be yet another chaincode // message (the actual response message) final ChaincodeMessage responseMessage = ChaincodeMessage.parseFrom(response); // the actual response message must be of type COMPLETED LOGGER.fine(() -> String.format( "[%-8.8s] %s response received from other chaincode.", txId, responseMessage.getType())); if (responseMessage.getType() == COMPLETED) { // success final Response r = Response.parseFrom(responseMessage.getPayload()); return new Chaincode.Response( Chaincode.Response.Status.forCode(r.getStatus()), r.getMessage(), r.getPayload().toByteArray()); } else { // error final String message = responseMessage.getPayload().toStringUtf8(); return new Chaincode.Response(Chaincode.Response.Status.INTERNAL_SERVER_ERROR, message, null); } } catch (final InvalidProtocolBufferException e) { throw new UncheckedIOException(e); } } @Override public SignedProposal getSignedProposal() { return signedProposal; } @Override public Instant getTxTimestamp() { return txTimestamp; } @Override @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull") public byte[] getCreator() { if (creator == null) { return null; } return creator.toByteArray(); } @Override public Map getTransient() { return transientMap.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, x -> x.getValue().toByteArray())); } @Override @SuppressWarnings("PMD.MethodReturnsInternalArray") public byte[] getBinding() { return this.binding; } private void validateKey(final String key) { Objects.requireNonNull(key, "key cannot be null"); if (key.isEmpty()) { throw new IllegalArgumentException("key cannot not be an empty string"); } } private void validateCollection(final String collection) { Objects.requireNonNull(collection, "collection cannot be null"); if (collection.isEmpty()) { throw new IllegalArgumentException("collection must not be an empty string"); } } @Override public String getMspId() { if (System.getenv().containsKey(CORE_PEER_LOCALMSPID)) { return System.getenv(CORE_PEER_LOCALMSPID); } throw new IllegalStateException("CORE_PEER_LOCALMSPID is unset in chaincode process"); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskExecutor.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import org.hyperledger.fabric.metrics.TaskMetricsCollector; /** */ public final class InvocationTaskExecutor extends ThreadPoolExecutor implements TaskMetricsCollector { private static Logger logger = Logger.getLogger(InvocationTaskExecutor.class.getName()); private final AtomicInteger count = new AtomicInteger(); /** * @param corePoolSize * @param maximumPoolSize * @param keepAliveTime * @param unit * @param workQueue * @param factory * @param handler */ public InvocationTaskExecutor( final int corePoolSize, final int maximumPoolSize, final long keepAliveTime, final TimeUnit unit, final BlockingQueue workQueue, final ThreadFactory factory, final RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, factory, handler); prestartCoreThread(); logger.info("Thread pool created"); } @Override protected void beforeExecute(final Thread thread, final Runnable task) { super.beforeExecute(thread, task); count.incrementAndGet(); } @Override protected void afterExecute(final Runnable task, final Throwable throwable) { count.decrementAndGet(); super.afterExecute(task, throwable); } @Override public int getCurrentTaskCount() { return count.get(); } @Override public int getCurrentQueueCount() { return this.getQueue().size(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/InvocationTaskManager.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.READY; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.REGISTERED; import java.util.Map; import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.logging.Logger; import org.hyperledger.fabric.Logging; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type; import org.hyperledger.fabric.shim.ChaincodeBase; /** * The InvocationTask Manager handles the message level communication with the peer. * *

In the current 1.4 Fabric Protocol this is in practice a singleton - because the peer will ignore multiple * 'register' calls. And an instance of this will be created per register call for a given chaincodeID. */ @SuppressWarnings("PMD.MoreThanOneLogger") public final class InvocationTaskManager { private static final Logger LOGGER = Logger.getLogger(InvocationTaskManager.class.getName()); private static final Logger PERFLOGGER = Logger.getLogger(Logging.PERFLOGGER); private static final String CANNOT_HANDLE_FORMAT = "[%-8.8s] Received %s: cannot handle"; private static final int SHUTDOWN_TIMEOUT = 60; // Keeping a map here of the tasks that are currently ongoing, and the key // // Key = txid + channleid // One task = one transaction invocation private final Map innvocationTasks = new ConcurrentHashMap<>(); // Way to send back the events and data that make up the requests private Consumer outgoingMessage; // references to the chaincode, and the chaincode id private final ChaincodeBase chaincode; private final ChaincodeID chaincodeId; // Thread Pool creation and settings private final int queueSize; private final int maximumPoolSize; private final int corePoolSize; private final long keepAliveTime; private static final TimeUnit UNIT = TimeUnit.MILLISECONDS; private final BlockingQueue workQueue; // Minor customization of the ThreadFactory to give a more recognizable name to the threads private final ThreadFactory threadFactory = new ThreadFactory() { private final AtomicInteger next = new AtomicInteger(0); @Override public Thread newThread(final Runnable r) { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setName("fabric-txinvoke:" + next.incrementAndGet()); return thread; } }; // This is the handler that is run if the workqueue is full, usual options are // you can abort (as here) or the requesting thread can do the work. // DO NOT opt for the later. As this would then be run on the thread coming from gRPC // and will block it. If there are any tasks that are waiting for a response from the // peer for a ledger call, it is this gRPC thread that will do the return. but if it's // waiting for a response it will block the whole chaincode. private final RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); private final InvocationTaskExecutor taskService; /** * Get an instance of the Invocation Task Manager. * * @param chaincode Chaincode Instance * @param chaincodeId ID of the chaincode * @return InvocationTaskManager */ public static InvocationTaskManager getManager(final ChaincodeBase chaincode, final ChaincodeID chaincodeId) { return new InvocationTaskManager(chaincode, chaincodeId); } /** * New InvocationTaskManager. * * @param chaincode Chaincode Instance * @param chaincodeId ID of the chaincode */ public InvocationTaskManager(final ChaincodeBase chaincode, final ChaincodeID chaincodeId) { if (chaincode == null) { throw new IllegalArgumentException("chaincode can't be null"); } if (chaincodeId == null) { throw new IllegalArgumentException("chaincodeId can't be null"); } this.chaincode = chaincode; this.chaincodeId = chaincodeId; // setup the thread pool here final Properties props = chaincode.getChaincodeConfig(); queueSize = Integer.parseInt((String) props.getOrDefault("TP_QUEUE_SIZE", "5000")); maximumPoolSize = Integer.parseInt((String) props.getOrDefault("TP_MAX_POOL_SIZE", "5")); corePoolSize = Integer.parseInt((String) props.getOrDefault("TP_CORE_POOL_SIZE", "5")); keepAliveTime = Long.parseLong((String) props.getOrDefault("TP_KEEP_ALIVE_MS", "5000")); LOGGER.info(() -> "Max Pool Size [TP_MAX_POOL_SIZE]" + maximumPoolSize); LOGGER.info(() -> "Queue Size [TP_CORE_POOL_SIZE]" + queueSize); LOGGER.info(() -> "Core Pool Size [TP_QUEUE_SIZE]" + corePoolSize); LOGGER.info(() -> "Keep Alive Time [TP_KEEP_ALIVE_MS]" + keepAliveTime); workQueue = new LinkedBlockingQueue<>(queueSize); taskService = new InvocationTaskExecutor( corePoolSize, maximumPoolSize, keepAliveTime, UNIT, workQueue, threadFactory, handler); Metrics.getProvider().setTaskMetricsCollector(taskService); } /** * Called when a new message has arrived that needs to be processed. * * @throws IllegalArgumentException validation fields and arguments * @param chaincodeMessage ChaincodeMessage */ @SuppressWarnings("PMD.AvoidCatchingGenericException") public void onChaincodeMessage(final ChaincodeMessage chaincodeMessage) { if (null == chaincodeMessage) { throw new IllegalArgumentException("chaincodeMessage is null"); } LOGGER.fine(() -> String.format("[%-8.8s] %s", chaincodeMessage.getTxid(), ChaincodeBase.toJsonString(chaincodeMessage))); try { processChaincodeMessage(chaincodeMessage); } catch (final RuntimeException e) { // catch any issues with say the comms dropping or something else completely // unknown // and shutdown the pool this.shutdown(); throw e; } } private void processChaincodeMessage(final ChaincodeMessage chaincodeMessage) { final Type msgType = chaincodeMessage.getType(); switch (chaincode.getState()) { case CREATED: if (msgType == REGISTERED) { chaincode.setState(ChaincodeBase.CCState.ESTABLISHED); LOGGER.fine(() -> String.format( "[%-8.8s] Received REGISTERED: moving to established state", chaincodeMessage.getTxid())); } else { LOGGER.warning(() -> String.format(CANNOT_HANDLE_FORMAT, chaincodeMessage.getTxid(), msgType)); } break; case ESTABLISHED: if (msgType == READY) { chaincode.setState(ChaincodeBase.CCState.READY); LOGGER.fine(() -> String.format( "[%-8.8s] Received READY: ready for invocations", chaincodeMessage.getTxid())); } else { LOGGER.warning(() -> String.format(CANNOT_HANDLE_FORMAT, chaincodeMessage.getTxid(), msgType)); } break; case READY: handleMsg(chaincodeMessage, msgType); break; } } /** * Key method to take the message, determine if it is a new transaction or an answer (good or bad) to a stub api. * * @param message * @param msgType */ private void handleMsg(final ChaincodeMessage message, final Type msgType) { LOGGER.fine(() -> String.format("[%-8.8s] Received %s", message.getTxid(), msgType.toString())); switch (msgType) { case RESPONSE: case ERROR: sendToTask(message); break; case INIT: case TRANSACTION: newTask(message, msgType); break; default: LOGGER.warning(() -> String.format(CANNOT_HANDLE_FORMAT, message.getTxid(), message.getType())); break; } } /** * Send a message from the peer to the correct task. This will be a response to something like a getState() call. * * @param message ChaincodeMessage from the peer */ private void sendToTask(final ChaincodeMessage message) { try { PERFLOGGER.fine(() -> "> sendToTask TX::" + message.getTxid()); final String key = message.getChannelId() + message.getTxid(); final ChaincodeInvocationTask task = this.innvocationTasks.get(key); if (task == null) { sendFailure(message, new InterruptedException("Task map missing entry: " + key)); } else { task.postMessage(message); PERFLOGGER.fine(() -> "< sendToTask TX::" + message.getTxid()); } } catch (final InterruptedException e) { sendFailure(message, e); } } private void sendFailure(final ChaincodeMessage message, final InterruptedException e) { LOGGER.severe(() -> "Failed to send response to the task task " + message.getTxid() + Logging.formatError(e)); final ChaincodeMessage m = ChaincodeMessageFactory.newErrorEventMessage( message.getChannelId(), message.getTxid(), "Failed to send response to task"); this.outgoingMessage.accept(m); } /** * Create a new task to handle this transaction function. * * @param message ChaincodeMessage to process * @param type Type of message = INIT or INVOKE. INIT is deprecated in future versions * @throws InterruptedException */ private void newTask(final ChaincodeMessage message, final Type type) { String txid = message.getTxid(); final ChaincodeInvocationTask task = new ChaincodeInvocationTask(message, type, this.outgoingMessage, this.chaincode); PERFLOGGER.fine(() -> "> newTask:created TX::" + txid); this.innvocationTasks.put(task.getTxKey(), task); try { PERFLOGGER.fine(() -> "> newTask:submitting TX::" + txid); // submit the task to run, with the taskService providing the // threading support. final CompletableFuture response = CompletableFuture.runAsync( () -> { task.call(); }, taskService); // we have a future of the chaincode message that should be returned. // but waiting for this does not need to block this thread // it is important to wait for it however, as we need to remove it from the task // list response.thenRun(() -> { innvocationTasks.remove(task.getTxKey()); PERFLOGGER.fine(() -> "< newTask:completed TX::" + txid); }); PERFLOGGER.fine(() -> "< newTask:submitted TX::" + txid); } catch (final RejectedExecutionException e) { LOGGER.warning(() -> "Failed to submit task " + txid + Logging.formatError(e)); // this means that there is no way that this can be handed off to another // thread for processing, and there's no space left in the queue to hold // it pending final ChaincodeMessage m = ChaincodeMessageFactory.newErrorEventMessage( message.getChannelId(), txid, "Failed to submit task for processing"); this.outgoingMessage.accept(m); } } /** * Set the Consumer function to be used for sending messages back to the peer. * * @param outgoingMessage * @return InvocationTaskManager */ public void setResponseConsumer(final Consumer outgoingMessage) { this.outgoingMessage = outgoingMessage; } /** * Send the initial protocol message for the 'register' phase. * * @throws IllegalArgumentException validation fields and arguments */ public void register() { if (outgoingMessage == null) { throw new IllegalArgumentException("outgoingMessage is null"); } LOGGER.info(() -> "Registering new chaincode " + this.chaincodeId); chaincode.setState(ChaincodeBase.CCState.CREATED); this.outgoingMessage.accept(ChaincodeMessageFactory.newRegisterChaincodeMessage(this.chaincodeId)); } /** */ @SuppressWarnings("PMD.SystemPrintln") public void shutdown() { // Recommended shutdown process from // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html // Disable new tasks from being submitted this.taskService.shutdown(); try { // Wait a while for existing tasks to terminate if (!taskService.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) { // Cancel currently executing tasks taskService.shutdownNow(); // Wait a while for tasks to respond to being cancelled if (!taskService.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) { System.err.println("Pool did not terminate"); } } } catch (final InterruptedException ex) { // (Re-)Cancel if current thread also interrupted taskService.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/KeyModificationImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import com.google.protobuf.ByteString; import java.time.Instant; import org.hyperledger.fabric.shim.ledger.KeyModification; public final class KeyModificationImpl implements KeyModification { private final String txId; private final ByteString value; private final Instant timestamp; private final boolean deleted; KeyModificationImpl(final org.hyperledger.fabric.protos.ledger.queryresult.KeyModification km) { this.txId = km.getTxId(); this.value = km.getValue(); this.timestamp = Instant.ofEpochSecond( km.getTimestamp().getSeconds(), km.getTimestamp().getNanos()); this.deleted = km.getIsDelete(); } @Override public String getTxId() { return txId; } @Override public byte[] getValue() { return value.toByteArray(); } @Override public String getStringValue() { return value.toStringUtf8(); } @Override public Instant getTimestamp() { return timestamp; } @Override public boolean isDeleted() { return deleted; } @Override public int hashCode() { final int prime = 31; int result = Boolean.hashCode(deleted); result = prime * result + timestamp.hashCode(); result = prime * result + txId.hashCode(); result = prime * result + value.hashCode(); return result; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null) { return false; } if (getClass() != other.getClass()) { return false; } final KeyModificationImpl that = (KeyModificationImpl) other; return this.deleted == that.deleted && this.timestamp.equals(that.timestamp) && this.txId.equals(that.txId) && this.value.equals(that.value); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/KeyValueImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import com.google.protobuf.ByteString; import org.hyperledger.fabric.protos.ledger.queryresult.KV; import org.hyperledger.fabric.shim.ledger.KeyValue; class KeyValueImpl implements KeyValue { private final String key; private final ByteString value; KeyValueImpl(final KV kv) { this.key = kv.getKey(); this.value = kv.getValue(); } @Override public String getKey() { return key; } @Override public byte[] getValue() { return value.toByteArray(); } @Override public String getStringValue() { return value.toStringUtf8(); } @Override public int hashCode() { final int prime = 31; int result = key.hashCode(); result = prime * result + value.hashCode(); return result; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (other == null) { return false; } if (getClass() != other.getClass()) { return false; } final KeyValueImpl that = (KeyValueImpl) other; return this.key.equals(that.key) && this.value.equals(that.value); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/QueryResultsIteratorImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.QUERY_STATE_CLOSE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.QUERY_STATE_NEXT; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import java.io.UncheckedIOException; import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.function.Function; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.protos.peer.QueryResultBytes; import org.hyperledger.fabric.protos.peer.QueryStateClose; import org.hyperledger.fabric.protos.peer.QueryStateNext; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; /** * This class provides an ITERABLE object of query results. * *

NOTE the class name is misleading - as this class is not an iterator itself, rather it implements * java.lang.Iterable via the QueryResultsIterator * *

public interface QueryResultsIterator extends Iterable, AutoCloseable * * @param */ class QueryResultsIteratorImpl implements QueryResultsIterator { private final ChaincodeInvocationTask handler; private final String channelId; private final String txId; private Iterator currentIterator; private QueryResponse currentQueryResponse; private final Function mapper; QueryResultsIteratorImpl( final ChaincodeInvocationTask handler, final String channelId, final String txId, final ByteString responseBuffer, final Function mapper) { try { this.handler = handler; this.channelId = channelId; this.txId = txId; this.currentQueryResponse = QueryResponse.parseFrom(responseBuffer); this.currentIterator = currentQueryResponse.getResultsList().iterator(); this.mapper = mapper; } catch (final InvalidProtocolBufferException e) { throw new UncheckedIOException(e); } } @Override public Iterator iterator() { return new Iterator<>() { @Override public boolean hasNext() { return currentIterator.hasNext() || currentQueryResponse.getHasMore(); } @Override public T next() { // return next fetched result, if any if (currentIterator.hasNext()) { return mapper.apply(currentIterator.next()); } // throw exception if there are no more expected results if (!currentQueryResponse.getHasMore()) { throw new NoSuchElementException(); } // get more results from peer final ByteString requestPayload = QueryStateNext.newBuilder() .setId(currentQueryResponse.getId()) .build() .toByteString(); final ChaincodeMessage requestNextMessage = ChaincodeMessageFactory.newEventMessage(QUERY_STATE_NEXT, channelId, txId, requestPayload); final ByteString responseMessage = QueryResultsIteratorImpl.this.handler.invoke(requestNextMessage); try { currentQueryResponse = QueryResponse.parseFrom(responseMessage); } catch (final InvalidProtocolBufferException e) { throw new UncheckedIOException(e); } currentIterator = currentQueryResponse.getResultsList().iterator(); // return next fetched result return mapper.apply(currentIterator.next()); } }; } @Override public void close() { final ByteString requestPayload = QueryStateClose.newBuilder() .setId(currentQueryResponse.getId()) .build() .toByteString(); final ChaincodeMessage requestNextMessage = ChaincodeMessageFactory.newEventMessage(QUERY_STATE_CLOSE, channelId, txId, requestPayload); this.handler.invoke(requestNextMessage); this.currentIterator = Collections.emptyIterator(); this.currentQueryResponse = QueryResponse.newBuilder().setHasMore(false).build(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/QueryResultsIteratorWithMetadataImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import java.io.UncheckedIOException; import java.util.function.Function; import java.util.logging.Logger; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.protos.peer.QueryResponseMetadata; import org.hyperledger.fabric.protos.peer.QueryResultBytes; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; /** * QueryResult Iterator. * *

Implementation of {@link QueryResultsIteratorWithMetadata}, by extending * {@link org.hyperledger.fabric.shim.ledger.QueryResultsIterator} implementations, {@link QueryResultsIteratorImpl} * * @param */ public final class QueryResultsIteratorWithMetadataImpl extends QueryResultsIteratorImpl implements QueryResultsIteratorWithMetadata { private static final Logger LOGGER = Logger.getLogger(QueryResultsIteratorWithMetadataImpl.class.getName()); private final QueryResponseMetadata metadata; /** * @param handler * @param channelId * @param txId * @param responseBuffer * @param mapper */ public QueryResultsIteratorWithMetadataImpl( final ChaincodeInvocationTask handler, final String channelId, final String txId, final ByteString responseBuffer, final Function mapper) { super(handler, channelId, txId, responseBuffer, mapper); try { final QueryResponse queryResponse = QueryResponse.parseFrom(responseBuffer); metadata = QueryResponseMetadata.parseFrom(queryResponse.getMetadata()); } catch (final InvalidProtocolBufferException e) { LOGGER.warning("can't parse response metadata"); throw new UncheckedIOException(e); } } @Override public QueryResponseMetadata getMetadata() { return metadata; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/impl/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** */ package org.hyperledger.fabric.shim.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKey.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; import static java.util.stream.Collectors.joining; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CompositeKey { private static final String DELIMITER = new String(Character.toChars(Character.MIN_CODE_POINT)); private static final String INVALID_SEGMENT_CHAR = new String(Character.toChars(Character.MAX_CODE_POINT)); private static final String INVALID_SEGMENT_PATTERN = String.format("(?:%s|%s)", INVALID_SEGMENT_CHAR, DELIMITER); /** */ public static final String NAMESPACE = DELIMITER; private final String objectType; private final List attributes; private final String key; /** * @param objectType * @param attributes */ public CompositeKey(final String objectType, final String... attributes) { this(objectType, attributes == null ? Collections.emptyList() : Arrays.asList(attributes)); } /** * @param objectType * @param attributes */ public CompositeKey(final String objectType, final List attributes) { Objects.requireNonNull(objectType, "objectType cannot be null"); this.objectType = objectType; this.attributes = attributes; this.key = generateCompositeKeyString(objectType, attributes); } /** @return object type */ public String getObjectType() { return objectType; } /** @return List of string arguments */ public List getAttributes() { return attributes; } /** */ @Override public String toString() { return key; } /** * @param compositeKey * @return Composite Key */ public static CompositeKey parseCompositeKey(final String compositeKey) { if (compositeKey == null) { return null; } if (!compositeKey.startsWith(NAMESPACE)) { throw CompositeKeyFormatException.forInputString(compositeKey, compositeKey, 0); } // relying on the fact that NAMESPACE == DELIMETER final String[] segments = compositeKey.split(DELIMITER, 0); return new CompositeKey(segments[1], Arrays.stream(segments).skip(2).toArray(String[]::new)); } /** * To ensure that simple keys do not go into composite key namespace, we validate simple key to check whether the * key starts with 0x00 (which is the namespace for compositeKey). This helps in avoiding simple/composite key * collisions. * * @param keys the list of simple keys * @throws CompositeKeyFormatException if First character of the key */ public static void validateSimpleKeys(final String... keys) { for (final String key : keys) { if (!key.isEmpty() && key.startsWith(NAMESPACE)) { throw CompositeKeyFormatException.forSimpleKey(key); } } } private String generateCompositeKeyString(final String objectType, final List attributes) { // object type must be a valid composite key segment validateCompositeKeySegment(objectType); if (attributes == null || attributes.isEmpty()) { return NAMESPACE + objectType + DELIMITER; } // the attributes must be valid composite key segments attributes.forEach(this::validateCompositeKeySegment); // return NAMESPACE + objectType + DELIMITER + (attribute + DELIMITER)* return attributes.stream().collect(joining(DELIMITER, NAMESPACE + objectType + DELIMITER, DELIMITER)); } private void validateCompositeKeySegment(final String segment) { final Matcher matcher = Pattern.compile(INVALID_SEGMENT_PATTERN).matcher(segment); if (matcher.find()) { throw CompositeKeyFormatException.forInputString(segment, matcher.group(), matcher.start()); } } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/CompositeKeyFormatException.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; final class CompositeKeyFormatException extends IllegalArgumentException { private static final long serialVersionUID = 1L; private CompositeKeyFormatException(final String s) { super(s); } static CompositeKeyFormatException forInputString(final String s, final String group, final int index) { return new CompositeKeyFormatException( String.format("For input string '%s', found 'U+%06X' at index %d.", s, group.codePointAt(0), index)); } static CompositeKeyFormatException forSimpleKey(final String key) { return new CompositeKeyFormatException(String.format( "First character of the key [%s] contains a 'U+%06X' which is not allowed", key, CompositeKey.NAMESPACE.codePointAt(0))); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/KeyModification.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; /** * QueryResult for history query. Holds a transaction ID, value, timestamp, and delete marker which resulted from a * history query. */ public interface KeyModification { /** * Returns the transaction id. * * @return tx id of modification */ String getTxId(); /** * Returns the key's value at the time returned by {@link #getTimestamp()}. * * @return value */ byte[] getValue(); /** * Returns the key's value at the time returned by {@link #getTimestamp()}, decoded as a UTF-8 string. * * @return value as string */ String getStringValue(); /** * Returns the timestamp of the key modification entry. * * @return timestamp */ java.time.Instant getTimestamp(); /** * Returns the deletion marker. * * @return is key was deleted */ boolean isDeleted(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/KeyValue.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; /** Query Result associating a state key with a value. */ public interface KeyValue { /** * Returns the state key. * * @return key as string */ String getKey(); /** * Returns the state value. * * @return value as byte array */ byte[] getValue(); /** * Returns the state value, decoded as a UTF-8 string. * * @return value as string */ String getStringValue(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/QueryResultsIterator.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; /** * QueryResultsIterator allows a chaincode to iterate over a set of key/value pairs returned by range, execute and * history queries. * * @param the type of elements returned by the iterator */ public interface QueryResultsIterator extends Iterable, AutoCloseable { @Override void close(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/QueryResultsIteratorWithMetadata.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; import org.hyperledger.fabric.protos.peer.QueryResponseMetadata; /** * QueryResultsIteratorWithMetadata allows a chaincode to iterate over a set of key/value pairs returned by range, * execute and history queries. In addition, it store {@link org.hyperledger.fabric.protos.peer.QueryResponseMetadata}, * returned by pagination range queries * * @param the type of elements returned by the iterator */ public interface QueryResultsIteratorWithMetadata extends Iterable, AutoCloseable { /** @return Query Metadata */ QueryResponseMetadata getMetadata(); @Override void close(); } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/ledger/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Provides interfaces and classes for querying state variables. * * @see org.hyperledger.fabric.shim.ChaincodeStub */ package org.hyperledger.fabric.shim.ledger; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/shim/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Provides interfaces and classes required for chaincode development and state variable access. * *

It is possible to implement Java chaincode by extending the {@link org.hyperledger.fabric.shim.ChaincodeBase} * class however new projects should should implement {@link org.hyperledger.fabric.contract.ContractInterface} and use * the contract programming model instead. * * @see org.hyperledger.fabric.contract * @see org.hyperledger.fabric.shim.ChaincodeBase */ package org.hyperledger.fabric.shim; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/Traces.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces; import java.util.Properties; import java.util.logging.Logger; import org.hyperledger.fabric.traces.impl.DefaultTracesProvider; import org.hyperledger.fabric.traces.impl.NullProvider; /** * Traces Interface. * *

Traces setups up the provider in use from the configuration supplied * *

If not enabled, nothing happens. */ public final class Traces { private static final String CHAINCODE_TRACES_ENABLED = "CHAINCODE_TRACES_ENABLED"; private static final String CHAINCODE_TRACES_PROVIDER = "CHAINCODE_TRACES_PROVIDER"; private static Logger logger = Logger.getLogger(Traces.class.getName()); private static TracesProvider provider; private Traces() {} /** * @param props the configuration of the chaincode * @return The traces provider */ @SuppressWarnings("PMD.AvoidCatchingGenericException") public static TracesProvider initialize(final Properties props) { if (Boolean.parseBoolean((String) props.get(CHAINCODE_TRACES_ENABLED))) { try { logger.info("Traces enabled"); if (props.containsKey(CHAINCODE_TRACES_PROVIDER)) { final String providerClass = (String) props.get(CHAINCODE_TRACES_PROVIDER); @SuppressWarnings("unchecked") // it must be this type otherwise an error final Class clazz = (Class) Class.forName(providerClass); provider = clazz.getConstructor().newInstance(); } else { logger.info("Using default traces provider"); provider = new DefaultTracesProvider(); } } catch (Exception e) { throw new IllegalStateException("Unable to start traces", e); } } else { // return a 'null' provider logger.info("Traces disabled"); provider = new NullProvider(); } provider.initialize(props); return provider; } /** @return TracesProvider */ public static TracesProvider getProvider() { if (provider == null) { throw new IllegalStateException("No provider set, this should have been set"); } return provider; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/TracesProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces; import io.grpc.ClientInterceptor; import io.opentelemetry.api.trace.Span; import java.util.Properties; import org.hyperledger.fabric.shim.ChaincodeStub; /** * Interface to be implemented to send traces on the chaincode to the 'backend-of-choice'. * *

An instance of this will be created, and provided with the resources from which chaincode specific metrics can be * collected. (via the no-argument constructor). * *

The choice of when, where and what to collect etc are within the remit of the provider. * *

This is the effective call sequence. * *

MyTracesProvider mmp = new MyTracesProvider() mmp.initialize(props_from_environment); // short while later.... * mmp.setTaskTracesCollector(taskService); */ public interface TracesProvider { /** * Initialize method that is called immediately after creation. * * @param props */ default void initialize(final Properties props) { // Do nothing by default } /** * Creates a span with metadata of the current chaincode execution, possibly linked to the execution arguments. * * @param stub the context of the chaincode execution * @return a new span if traces are enabled, or null. The caller is responsible for closing explicitly the span. */ default Span createSpan(ChaincodeStub stub) { return null; } /** * Creates an interceptor of gRPC messages that can be injected in processing incoming messages to extract trace * information. * * @return a new client interceptor, or null if no interceptor is set. */ default ClientInterceptor createInterceptor() { return null; } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/DefaultTracesProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import org.hyperledger.fabric.traces.TracesProvider; public final class DefaultTracesProvider implements TracesProvider {} ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/NullProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import org.hyperledger.fabric.traces.TracesProvider; public final class NullProvider implements TracesProvider {} ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryProperties.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.time.Duration; import java.util.AbstractMap; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.annotation.Nullable; final class OpenTelemetryProperties implements ConfigProperties { private final Map config; @SafeVarargs OpenTelemetryProperties(final Map... arrayOfProperties) { Map config = new HashMap<>(); for (Map props : arrayOfProperties) { props.forEach( (key, value) -> config.put(key.toLowerCase(Locale.ROOT).replace('-', '.'), value)); } this.config = config; } @Override @Nullable public String getString(final String name) { return config.get(name); } @Override @Nullable public Boolean getBoolean(final String name) { String value = config.get(name); if (value == null || value.isEmpty()) { return null; } return Boolean.valueOf(value); } @Override @Nullable public Integer getInt(final String name) { String value = config.get(name); if (value == null || value.isEmpty()) { return null; } try { return Integer.valueOf(value); } catch (NumberFormatException ex) { throw newInvalidPropertyException(name, value, "integer", ex); } } @Override @Nullable public Long getLong(final String name) { String value = config.get(name); if (value == null || value.isEmpty()) { return null; } try { return Long.valueOf(value); } catch (NumberFormatException ex) { throw newInvalidPropertyException(name, value, "long", ex); } } @Override @Nullable public Double getDouble(final String name) { String value = config.get(name); if (value == null || value.isEmpty()) { return null; } try { return Double.valueOf(value); } catch (NumberFormatException ex) { throw newInvalidPropertyException(name, value, "double", ex); } } @Override @Nullable public Duration getDuration(final String name) { String value = config.get(name); if (value == null || value.isEmpty()) { return null; } String unitString = getUnitString(value); String numberString = value.substring(0, value.length() - unitString.length()); try { long rawNumber = Long.parseLong(numberString.trim()); TimeUnit unit = getDurationUnit(unitString.trim()) .orElseThrow(() -> new ConfigurationException( "Invalid duration property " + name + "=" + value + ". Invalid duration unit.")); return Duration.ofMillis(TimeUnit.MILLISECONDS.convert(rawNumber, unit)); } catch (NumberFormatException ex) { var e = new ConfigurationException( "Invalid duration property " + name + "=" + value + ". Expected number, found: " + numberString); e.addSuppressed(ex); throw e; } } @Override public List getList(final String name) { String value = config.get(name); if (value == null) { return Collections.emptyList(); } return filterBlanksAndNulls(value.split(",")); } @Override @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") public Map getMap(final String name) { return getList(name).stream() .map(keyValuePair -> filterBlanksAndNulls(keyValuePair.split("=", 2))) .map(splitKeyValuePairs -> { if (splitKeyValuePairs.size() != 2) { throw new ConfigurationException("Invalid map property: " + name + "=" + config.get(name)); } return new AbstractMap.SimpleImmutableEntry<>(splitKeyValuePairs.get(0), splitKeyValuePairs.get(1)); }) // If duplicate keys, prioritize later ones similar to duplicate system properties on a // Java command line. .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (first, next) -> next, LinkedHashMap::new)); } private static ConfigurationException newInvalidPropertyException( final String name, final String value, final String type, final Exception cause) { var e = new ConfigurationException( "Invalid value for property " + name + "=" + value + ". Must be a " + type + "."); e.addSuppressed(cause); throw e; } private static List filterBlanksAndNulls(final String[] values) { return Arrays.stream(values).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList()); } /** * Returns the TimeUnit associated with a unit string. Defaults to milliseconds. * * @param unitString the time unit as a string * @return the parsed TimeUnit */ private static Optional getDurationUnit(final String unitString) { switch (unitString) { case "": // Fallthrough expected case "ms": return Optional.of(TimeUnit.MILLISECONDS); case "s": return Optional.of(TimeUnit.SECONDS); case "m": return Optional.of(TimeUnit.MINUTES); case "h": return Optional.of(TimeUnit.HOURS); case "d": return Optional.of(TimeUnit.DAYS); default: return Optional.empty(); } } /** * Fragments the 'units' portion of a config value from the 'value' portion. * *

E.g. "1ms" would return the string "ms". * * @param rawValue the raw value of a unit and value * @return the unit string */ private static String getUnitString(final String rawValue) { int lastDigitIndex = rawValue.length() - 1; while (lastDigitIndex >= 0) { char c = rawValue.charAt(lastDigitIndex); if (Character.isDigit(c)) { break; } lastDigitIndex -= 1; } // Pull everything after the last digit. return rawValue.substring(lastDigitIndex + 1); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import io.grpc.ClientInterceptor; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import io.opentelemetry.semconv.ServiceAttributes; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.traces.TracesProvider; public final class OpenTelemetryTracesProvider implements TracesProvider { private static final String TX_ID = "transaction_id"; private static final String CHANNEL_ID = "channel_id"; private static final String CORE_CHAINCODE_ID_NAME = "CORE_CHAINCODE_ID_NAME"; private Tracer tracer; private GrpcTelemetry grpcTracer; @Override public void initialize(final Properties props) { String serviceName = props.getProperty(CORE_CHAINCODE_ID_NAME, "unknown"); props.setProperty(ServiceAttributes.SERVICE_NAME.getKey(), serviceName); OpenTelemetry openTelemetry = AutoConfiguredOpenTelemetrySdk.builder() .addPropertiesSupplier(() -> getOpenTelemetryProperties(props)) .build() .getOpenTelemetrySdk(); tracer = openTelemetry.getTracerProvider().get("org.hyperledger.traces"); grpcTracer = GrpcTelemetry.create(openTelemetry); } private Map getOpenTelemetryProperties(final Properties props) { Map results = new HashMap<>(System.getenv()); Properties systemProps = System.getProperties(); systemProps.stringPropertyNames().forEach(key -> results.put(key, systemProps.getProperty(key))); props.stringPropertyNames().forEach(key -> results.put(key, props.getProperty(key))); return results; } @Override public Span createSpan(final ChaincodeStub stub) { Context parentContext = Context.current(); return tracer.spanBuilder(stub.getFunction()) .setSpanKind(SpanKind.INTERNAL) .setAttribute(TX_ID, stub.getTxId()) .setAttribute(CHANNEL_ID, stub.getChannelId()) .setParent(parentContext) .startSpan(); } @Override public ClientInterceptor createInterceptor() { return grpcTracer.createClientInterceptor(); } } ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/impl/package-info.java ================================================ /* * Copyright 2023 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; ================================================ FILE: fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/traces/package-info.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ /** * Supports collection of traces * *

This creates traces at the root level of chaincode calls. * *

To enable traces ensure that there is a standard format Java properties file called `config.props` in the root of * your contract code. For example this path * *

 * myjava - contract - project / java / src / main / resources / config.props
 * 
* * This should contain the following * *
 * CHAINCODE_TRACES_ENABLED=true
 * 
* * The traces enabled flag will turn on default traces logging. (it's off by default). * *

If no file is supplied traces are not enabled, the values shown for the thread pool are used. * *

Open Telemetry To use Open Telemetry, set the following properties: * *

 * CHAINCODE_TRACES_ENABLED=true
 * CHAINCODE_TRACES_PROVIDER=org.hyperledger.fabric.traces.impl.OpenTelemetryTracesProvider
 * 
* * Additionally, you can set properties after the specification: * https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md * *

Example: * *

 * OTEL_EXPORTER_OTLP_ENDPOINT=otelcollector:4317
 * OTEL_EXPORTER_OTLP_INSECURE=true
 * 
*/ package org.hyperledger.fabric.traces; ================================================ FILE: fabric-chaincode-shim/src/main/resources/contract-schema.json ================================================ { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "title": "Hyperledger Fabric Contract Definition JSON Schema", "required": [ "info", "contracts" ], "properties": { "info": { "$ref": "#/definitions/info" }, "contracts": { "type": "object", "patternProperties": { "^.*$": { "$ref": "#/definitions/contract" } } }, "components": { "$ref": "#/definitions/components" } }, "definitions": { "info": { "type": "object", "description": "General information about the API.", "required": [ "version", "title" ], "properties": { "title": { "type": "string", "description": "A unique and precise title of the API." }, "version": { "type": "string", "description": "A semantic version number of the API." }, "description": { "type": "string", "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." }, "termsOfService": { "type": "string", "description": "The terms of service for the API." }, "contact": { "$ref": "#/definitions/contact" }, "license": { "$ref": "#/definitions/license" } } }, "contact": { "type": "object", "description": "Contact information for the owners of the API.", "properties": { "name": { "type": "string", "description": "The identifying name of the contact person/organization." }, "url": { "type": "string", "description": "The URL pointing to the contact information.", "format": "uri" }, "email": { "type": "string", "description": "The email address of the contact person/organization.", "format": "email" } } }, "license": { "type": "object", "required": [ "name" ], "additionalProperties": false, "properties": { "name": { "type": "string", "description": "The name of the license type. It's encouraged to use an OSI compatible license." }, "url": { "type": "string", "description": "The URL pointing to the license.", "format": "uri" } } }, "contract": { "type": "object", "description": "", "required": [ "name", "transactions" ], "properties": { "info": { "$ref": "#/definitions/info" }, "name": { "type": "string", "description": "A unique and precise title of the API." }, "transactions": { "type": "array", "items": { "$ref": "#/definitions/transaction" } } } }, "objecttype": { "type": "object", "description": "A complex type used in a domain", "required": [ "$id", "properties" ], "properties": { "$id": { "type": "string" }, "properties": { "^.*$": { "$ref": "#/definitions/schema" } }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" } } }, "parametersList": { "type": "array", "description": "The parameters needed to send a valid API call.", "additionalItems": false, "items": { "oneOf": [ { "$ref": "#/definitions/parameter" }, { "$ref": "#/definitions/jsonReference" } ] }, "uniqueItems": true }, "transaction": { "type": "object", "description": "single transaction specification", "required": [ "name" ], "properties": { "name": { "type": "string", "description": "name of the transaction " }, "tag": { "type": "array", "items": { "type": "string", "description": "free format tags" } }, "parameters": { "$ref": "#/definitions/parametersList" }, "returns": { "$ref": "#/definitions/schema" } } }, "parameter": { "type": "object", "required": [ "name", "schema" ], "properties": { "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "required": { "type": "boolean", "description": "Determines whether or not this parameter is required or optional.", "default": false }, "schema": { "$ref": "#/definitions/schema" } }, "additionalProperties": false }, "jsonReference": { "type": "object", "required": [ "$ref" ], "additionalProperties": false, "properties": { "$ref": { "type": "string" } } }, "schema": { "type": "object", "description": "A deterministic version of a JSON Schema object.", "properties": { "$ref": { "type": "string" }, "format": { "type": "string" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "maxProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "additionalProperties": { "anyOf": [ { "$ref": "#/definitions/schema" }, { "type": "boolean" } ], "default": {} }, "type": { "$ref": "http://json-schema.org/draft-04/schema#/properties/type" }, "items": { "anyOf": [ { "$ref": "#/definitions/schema" }, { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/schema" } } ], "default": {} }, "allOf": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/schema" } }, "properties": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schema" }, "default": {} }, "discriminator": { "type": "string" }, "readOnly": { "type": "boolean", "default": false }, "example": {} }, "additionalProperties": false }, "components": { "type": "object", "properties": { "schemas": { "type": "object", "patternProperties": { "^.*$": { "$ref": "#/definitions/objecttype" } } } } } } } ================================================ FILE: fabric-chaincode-shim/src/main/resources/json-schema-draft-04-schema.json ================================================ { "id": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "positiveInteger": { "type": "integer", "minimum": 0 }, "positiveIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "type": "object", "properties": { "id": { "type": "string" }, "$schema": { "type": "string" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "$ref": "#/definitions/positiveInteger" }, "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/positiveInteger" }, "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "$ref": "#/definitions/positiveInteger" }, "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "format": { "type": "string" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "dependencies": { "exclusiveMaximum": [ "maximum" ], "exclusiveMinimum": [ "minimum" ] }, "default": {} } ================================================ FILE: fabric-chaincode-shim/src/test/java/ChaincodeWithoutPackageTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.READY; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.REGISTER; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.mock.peer.ChaincodeMockPeer; import org.hyperledger.fabric.shim.mock.peer.RegisterStep; import org.hyperledger.fabric.shim.mock.peer.ScenarioStep; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; final class ChaincodeWithoutPackageTest { private ChaincodeMockPeer server; @AfterEach void afterTest() throws Exception { if (server != null) { server.stop(); server = null; } } @Test void testRegisterChaincodeWithoutPackage() throws Exception { final ChaincodeBase cb = new EmptyChaincodeWithoutPackage(); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType()).isEqualTo(READY); assertThat(server.getLastMessageRcvd().getType()).isEqualTo(REGISTER); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/EmptyChaincodeWithoutPackage.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ResponseUtils; public final class EmptyChaincodeWithoutPackage extends ChaincodeBase { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/contract/Greeting.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package contract; import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONObject; @DataType() public final class Greeting { @Property() private String text; @Property() private int textLength; public String getText() { return text; } public void setText(final String text) { this.text = text; } public int getTextLength() { return textLength; } public void setTextLength(final int textLength) { this.textLength = textLength; } public int getWordCount() { return wordCount; } public void setWordCount(final int wordCount) { this.wordCount = wordCount; } private int wordCount; public Greeting(final String text) { this.text = text; this.textLength = text.length(); this.wordCount = text.split(" ").length; } public static void validate(final Greeting greeting) { final String text = greeting.text; assertThat(text).as("greeting length").hasSize(greeting.textLength); assertThat(text.split(" ")).as("word count").hasSize(greeting.wordCount); } public String toJSONString() { return new JSONObject(this).toString(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/contract/SampleContract.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package contract; import java.util.List; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.annotation.Contact; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Default; import org.hyperledger.fabric.contract.annotation.Info; import org.hyperledger.fabric.contract.annotation.License; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.shim.ChaincodeException; @Contract( name = "samplecontract", info = @Info( contact = @Contact(email = "fred@example.com"), license = @License(name = "fred", url = "http://fred.me"), version = "0.0.1", title = "samplecontract")) @SuppressWarnings("PMD.SystemPrintln") @Default() public class SampleContract implements ContractInterface { public static int getBeforeInvoked() { return beforeInvoked; } public static int getAfterInvoked() { return afterInvoked; } public static int getDoWorkInvoked() { return doWorkInvoked; } public static int getT1Invoked() { return t1Invoked; } public static int getI1Invoked() { return i1Invoked; } public static void setBeforeInvoked(final int beforeInvoked) { SampleContract.beforeInvoked = beforeInvoked; } public static void setAfterInvoked(final int afterInvoked) { SampleContract.afterInvoked = afterInvoked; } public static void setDoWorkInvoked(final int doWorkInvoked) { SampleContract.doWorkInvoked = doWorkInvoked; } public static void setT1Invoked(final int t1Invoked) { SampleContract.t1Invoked = t1Invoked; } public static void setI1Invoked(final int i1Invoked) { SampleContract.i1Invoked = i1Invoked; } private static int beforeInvoked = 0; private static int afterInvoked = 0; private static int doWorkInvoked = 0; private static int t1Invoked = 0; private static int i1Invoked = 0; /** * @param ctx * @return */ @Transaction public String t5(final Context ctx) { doSomeWork(); System.out.println("SampleContract::T5 Done"); return null; } /** * @param ctx * @return */ @Transaction(name = "t4") public String tFour(final Context ctx) { System.out.println("SampleContract::T4 Done"); return "Transaction 4"; } /** * @param ctx * @param exception * @param message * @return */ @Transaction public String t3(final Context ctx, final String exception, final String message) { if ("TransactionException".equals(exception)) { if (message.isEmpty()) { throw new ChaincodeException(null, "T3ERR1"); } else { throw new ChaincodeException(message, "T3ERR1"); } } else { throw new IllegalArgumentException(message); } } /** * @param ctx * @return */ @Transaction public String t2(final Context ctx) { System.out.println("SampleContract::T2 Done"); return "Transaction 2"; } /** @param ctx */ @Transaction public void noReturn(final Context ctx) { System.out.println("SampleContract::noReturn done"); } /** * @param ctx * @param arg1 * @return */ @Transaction public String t1(final Context ctx, final String arg1) { t1Invoked++; final List args = ctx.getStub().getStringArgs(); doSomeWork(); System.out.println("SampleContract::T1 Done"); return args.get(1); } /** */ @Override public void beforeTransaction(final Context ctx) { beforeInvoked++; } /** */ @Override public void afterTransaction(final Context ctx, final Object value) { afterInvoked++; } private void doSomeWork() { doWorkInvoked++; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/LoggerTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.junit.jupiter.api.Test; class LoggerTest { @Test void logger() { Logger.getLogger(LoggerTest.class); Logger.getLogger(LoggerTest.class.getName()); } @Test void testContractException() { final Logger logger = Logger.getLogger(LoggerTest.class); final ContractRuntimeException cre1 = new ContractRuntimeException(""); logger.formatError(cre1); final ContractRuntimeException cre2 = new ContractRuntimeException("", cre1); final ContractRuntimeException cre3 = new ContractRuntimeException("", cre2); logger.formatError(cre3); logger.error("all gone wrong"); logger.error(() -> "all gone wrong"); } @Test void testDebug() { Logger.getLogger(LoggerTest.class).debug("debug message"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/LoggingTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.logging.Level; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; final class LoggingTest { @Test void testMapLevel() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { assertEquals(Level.SEVERE, proxyMapLevel("ERROR"), "Error maps"); assertEquals(Level.SEVERE, proxyMapLevel("critical"), "Critical maps"); assertEquals(Level.WARNING, proxyMapLevel("WARNING"), "Warn maps"); assertEquals(Level.INFO, proxyMapLevel("INFO"), "Info maps"); assertEquals(Level.CONFIG, proxyMapLevel(" notice"), "Config maps"); assertEquals(Level.INFO, proxyMapLevel(" info"), "Info maps"); assertEquals(Level.FINEST, proxyMapLevel("debug "), "Debug maps"); assertEquals(Level.INFO, proxyMapLevel("wibble "), "Info maps"); assertEquals(Level.INFO, proxyMapLevel(new Object[] {null}), "Info maps"); } private Object proxyMapLevel(final Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { final Method m = Logging.class.getDeclaredMethod("mapLevel", String.class); m.setAccessible(true); return m.invoke(null, args); } @Test void testFormatError() { final Exception e1 = new Exception("Computer says no"); assertThat(Logging.formatError(e1), containsString("Computer says no")); final NullPointerException npe1 = new NullPointerException("Nothing here"); npe1.initCause(e1); assertThat(Logging.formatError(npe1), containsString("Computer says no")); assertThat(Logging.formatError(npe1), containsString("Nothing here")); assertThat(Logging.formatError(null), CoreMatchers.nullValue()); } @Test void testSetLogLevel() { final java.util.logging.Logger l = java.util.logging.Logger.getLogger("org.hyperledger.fabric.test"); final java.util.logging.Logger another = java.util.logging.Logger.getLogger("acme.wibble"); final Level anotherLevel = another.getLevel(); Logging.setLogLevel("debug"); assertThat(l.getLevel(), CoreMatchers.equalTo(Level.FINEST)); assertThat(another.getLevel(), CoreMatchers.equalTo(anotherLevel)); Logging.setLogLevel("dsomethoig"); assertThat(l.getLevel(), CoreMatchers.equalTo(Level.INFO)); assertThat(another.getLevel(), CoreMatchers.equalTo(anotherLevel)); Logging.setLogLevel("ERROR"); assertThat(l.getLevel(), CoreMatchers.equalTo(Level.SEVERE)); assertThat(another.getLevel(), CoreMatchers.equalTo(anotherLevel)); Logging.setLogLevel("debug"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/TestUtil.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric; import java.io.ByteArrayInputStream; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Base64; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; public final class TestUtil { private TestUtil() {} public static final String CERT_WITHOUT_ATTRS = "MIICXTCCAgSgAwIBAgIUeLy6uQnq8wwyElU/jCKRYz3tJiQwCgYIKoZIzj0EAwIw" + "eTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh" + "biBGcmFuY2lzY28xGTAXBgNVBAoTEEludGVybmV0IFdpZGdldHMxDDAKBgNVBAsT" + "A1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTcwOTA4MDAxNTAwWhcNMTgw" + "OTA4MDAxNTAwWjBdMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xp" + "bmExFDASBgNVBAoTC0h5cGVybGVkZ2VyMQ8wDQYDVQQLEwZGYWJyaWMxDjAMBgNV" + "BAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFq/90YMuH4tWugHa" + "oyZtt4Mbwgv6CkBSDfYulVO1CVInw1i/k16DocQ/KSDTeTfgJxrX1Ree1tjpaodG" + "1wWyM6OBhTCBgjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4E" + "FgQUhKs/VJ9IWJd+wer6sgsgtZmxZNwwHwYDVR0jBBgwFoAUIUd4i/sLTwYWvpVr" + "TApzcT8zv/kwIgYDVR0RBBswGYIXQW5pbHMtTWFjQm9vay1Qcm8ubG9jYWwwCgYI" + "KoZIzj0EAwIDRwAwRAIgCoXaCdU8ZiRKkai0QiXJM/GL5fysLnmG2oZ6XOIdwtsC" + "IEmCsI8Mhrvx1doTbEOm7kmIrhQwUVDBNXCWX1t3kJVN"; public static final String CERT_WITH_ATTRS = "MIIB6TCCAY+gAwIBAgIUHkmY6fRP0ANTvzaBwKCkMZZPUnUwCgYIKoZIzj0EAwIw" + "GzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0xNzA5MDgwMzQyMDBaFw0x" + "ODA5MDgwMzQyMDBaMB4xHDAaBgNVBAMTE015VGVzdFVzZXJXaXRoQXR0cnMwWTAT" + "BgcqhkjOPQIBBggqhkjOPQMBBwNCAATmB1r3CdWvOOP3opB3DjJnW3CnN8q1ydiR" + "dzmuA6A2rXKzPIltHvYbbSqISZJubsy8gVL6GYgYXNdu69RzzFF5o4GtMIGqMA4G" + "A1UdDwEB/wQEAwICBDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTYKLTAvJJK08OM" + "VGwIhjMQpo2DrjAfBgNVHSMEGDAWgBTEs/52DeLePPx1+65VhgTwu3/2ATAiBgNV" + "HREEGzAZghdBbmlscy1NYWNCb29rLVByby5sb2NhbDAmBggqAwQFBgcIAQQaeyJh" + "dHRycyI6eyJhdHRyMSI6InZhbDEifX0wCgYIKoZIzj0EAwIDSAAwRQIhAPuEqWUp" + "svTTvBqLR5JeQSctJuz3zaqGRqSs2iW+QB3FAiAIP0mGWKcgSGRMMBvaqaLytBYo" + "9v3hRt1r8j8vN0pMcg=="; public static final String CERT_WITH_DNS = "MIICGjCCAcCgAwIBAgIRAIPRwJHVLhHK47XK0BbFZJswCgYIKoZIzj0EAwIwczEL" + "MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG" + "cmFuY2lzY28xGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh" + "Lm9yZzIuZXhhbXBsZS5jb20wHhcNMTcwNjIzMTIzMzE5WhcNMjcwNjIxMTIzMzE5" + "WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN" + "U2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWVXNlcjFAb3JnMi5leGFtcGxlLmNvbTBZ" + "MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBd9SsEiFH1/JIb3qMEPLR2dygokFVKW" + "eINcB0Ni4TBRkfIWWUJeCANTUY11Pm/+5gs+fBTqBz8M2UzpJDVX7+2jTTBLMA4G" + "A1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIKfUfvpGproH" + "cwyFD+0sE3XfJzYNcif0jNwvgOUFZ4AFMAoGCCqGSM49BAMCA0gAMEUCIQC8NIMw" + "e4ym/QRwCJb5umbONNLSVQuEpnPsJrM/ssBPvgIgQpe2oYa3yO3USro9nBHjpM3L" + "KsFQrpVnF8O6hoHOYZQ="; public static final String CERT_MULTIPLE_ATTRIBUTES = "MIIChzCCAi6gAwIBAgIURilAHeqwLu/fNUv8eZoGPRh3H4IwCgYIKoZIzj0EAwIw" + "czELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh" + "biBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT" + "E2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTkwNzMxMTYxNzAwWhcNMjAwNzMwMTYy" + "MjAwWjAgMQ8wDQYDVQQLEwZjbGllbnQxDTALBgNVBAMTBHRlc3QwWTATBgcqhkjO" + "PQIBBggqhkjOPQMBBwNCAAR2taQK8w7D3hr3gBxCz+8eV4KSv7pFQfNjDHMMe9J9" + "LJwcLpVTT5hYiLLRaqQonLBxBE3Ey0FneySvFuBScas3o4HyMIHvMA4GA1UdDwEB" + "/wQEAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQi3mhXS/WzcjBniwAmPdYP" + "kHqVVzArBgNVHSMEJDAigCC7VXjmSEugjAB/A0S6vfMxLsUIgag9WVNwtwwebnRC" + "7TCBggYIKgMEBQYHCAEEdnsiYXR0cnMiOnsiYXR0cjEiOiJ2YWwxIiwiZm9vIjoi" + "YmFyIiwiaGVsbG8iOiJ3b3JsZCIsImhmLkFmZmlsaWF0aW9uIjoiIiwiaGYuRW5y" + "b2xsbWVudElEIjoidGVzdCIsImhmLlR5cGUiOiJjbGllbnQifX0wCgYIKoZIzj0E" + "AwIDRwAwRAIgQxEFvnZTEsf3CSZmp9IYsxcnEOtVYleOd86LAKtk1wICIH7XOPwW" + "/RE4Z8WLZzFei/78Oezbx6obOvBxPMsVWRe5"; /** * Function to create a certificate with dummy attributes * * @param attributeValue {String} value to be written to the identity attributes section of the certificate * @return encodedCert {String} encoded certificate with re-written attributes */ public static String createCertWithIdentityAttributes(final String attributeValue) throws Exception { // Use existing certificate with attributes final byte[] decodedCert = Base64.getDecoder().decode(CERT_MULTIPLE_ATTRIBUTES); // Create a certificate holder and builder final X509CertificateHolder certHolder = new X509CertificateHolder(decodedCert); final X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(certHolder); // special OID used by Fabric to save attributes in x.509 certificates final String fabricCertOid = "1.2.3.4.5.6.7.8.1"; // Write the new attribute value final byte[] extDataToWrite = attributeValue.getBytes(); certBuilder.replaceExtension(new ASN1ObjectIdentifier(fabricCertOid), true, extDataToWrite); // Create a privateKey final KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); generator.initialize(384); final KeyPair keyPair = generator.generateKeyPair(); // Create and build the Content Signer final JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder("SHA256withECDSA"); final ContentSigner contentSigner = contentSignerBuilder.build(keyPair.getPrivate()); // Build the Certificate from the certificate builder final X509CertificateHolder builtCert = certBuilder.build(contentSigner); final X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X509") .generateCertificate(new ByteArrayInputStream(builtCert.getEncoded())); return Base64.getEncoder().encodeToString(certificate.getEncoded()); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/AllTypesAsset.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; @DataType() public final class AllTypesAsset { @Property private byte theByte = 125; @Property private short theShort = 30000; @Property private int theInt = 1234577123; @Property private long theLong = 12384233333123L; @Property private float theFloat = 3.1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164f; @Property private double theDouble = 3.1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679d; @Property private boolean theBoolean = false; @Property private char theChar = 'a'; @Property private String theString = "Hello World"; @Property private MyType theCustomObject = new MyType().setValue("Hello World"); public byte getTheByte() { return theByte; } public void setTheByte(final byte aByte) { this.theByte = aByte; } public short getTheShort() { return theShort; } public void setTheShort(final short aShort) { this.theShort = aShort; } public int getTheInt() { return theInt; } public void setTheInt(final int aInt) { this.theInt = aInt; } public long getTheLong() { return theLong; } public void setTheLong(final long aLong) { this.theLong = aLong; } public float getTheFloat() { return theFloat; } public void setTheFloat(final float aFloat) { this.theFloat = aFloat; } public double getTheDouble() { return theDouble; } public void setTheDouble(final double aDouble) { this.theDouble = aDouble; } public boolean isTheBoolean() { return theBoolean; } public void setBoolean(final boolean aBoolean) { this.theBoolean = aBoolean; } public char getTheChar() { return theChar; } public void setTheChar(final char aChar) { this.theChar = aChar; } public String getTheString() { return theString; } public void setString(final String aString) { this.theString = aString; } public MyType getTheCustomObject() { return theCustomObject; } public void setTheCustomObject(final MyType customObject) { this.theCustomObject = customObject; } @Override public boolean equals(final Object other) { if (!(other instanceof AllTypesAsset)) { return false; } AllTypesAsset obj = (AllTypesAsset) other; return theByte == obj.getTheByte() && theShort == obj.getTheShort() && theInt == obj.getTheInt() && theLong == obj.getTheLong() && theFloat == obj.getTheFloat() && theDouble == obj.getTheDouble() && theBoolean == obj.isTheBoolean() && theString.equals(obj.getTheString()); } @Override public String toString() { return System.lineSeparator() + "byte=" + theByte + System.lineSeparator() + "short=" + theShort + System.lineSeparator() + "int=" + theInt + System.lineSeparator() + "long=" + theLong + System.lineSeparator() + "float=" + theFloat + System.lineSeparator() + "double=" + theDouble + System.lineSeparator() + "boolean=" + theBoolean + System.lineSeparator() + "char=" + theChar + System.lineSeparator() + "String=" + theString + System.lineSeparator() + "Mytype=" + theCustomObject + System.lineSeparator(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ChaincodeStubNaiveImpl.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import com.google.protobuf.ByteString; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.hyperledger.fabric.TestUtil; import org.hyperledger.fabric.protos.msp.SerializedIdentity; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.SignedProposal; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ledger.CompositeKey; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; public final class ChaincodeStubNaiveImpl implements ChaincodeStub { private List args; private List argsAsByte; private final Map state; private final Chaincode.Response resp; private String certificate = TestUtil.CERT_WITHOUT_ATTRS; public ChaincodeStubNaiveImpl() { args = new ArrayList<>(); args.add("func1"); args.add("param1"); args.add("param2"); state = new HashMap<>(); state.put("a", ByteString.copyFrom("asdf", StandardCharsets.UTF_8)); argsAsByte = null; resp = new Chaincode.Response(404, "Wrong cc name", new byte[] {}); } ChaincodeStubNaiveImpl(final List args) { this.args = args; state = new HashMap<>(); state.put("a", ByteString.copyFrom("asdf", StandardCharsets.UTF_8)); argsAsByte = null; resp = new Chaincode.Response(404, "Wrong cc name", new byte[] {}); } @Override public List getArgs() { if (argsAsByte == null) { argsAsByte = args.stream() .map(arg -> arg.getBytes(StandardCharsets.UTF_8)) .collect(Collectors.toList()); } return argsAsByte; } @Override public List getStringArgs() { return args; } @Override public String getFunction() { return args.get(0); } @Override public List getParameters() { return args.subList(1, args.size()); } @Override public String getTxId() { return "tx0"; } @Override public String getChannelId() { return "ch0"; } @Override public Chaincode.Response invokeChaincode( final String chaincodeName, final List args, final String channel) { return resp; } @Override public byte[] getState(final String key) { return state.get(key).toByteArray(); } @Override public byte[] getStateValidationParameter(final String key) { return new byte[0]; } @Override public void putState(final String key, final byte[] value) { state.put(key, ByteString.copyFrom(value)); } @Override public void setStateValidationParameter(final String key, final byte[] value) {} @Override public void delState(final String key) { state.remove(key); } @Override public QueryResultsIterator getStateByRange(final String startKey, final String endKey) { return null; } @Override public QueryResultsIteratorWithMetadata getStateByRangeWithPagination( final String startKey, final String endKey, final int pageSize, final String bookmark) { return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey(final String compositeKey) { return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey( final String objectType, final String... attributes) { return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey(final CompositeKey compositeKey) { return null; } @Override public QueryResultsIteratorWithMetadata getStateByPartialCompositeKeyWithPagination( final CompositeKey compositeKey, final int pageSize, final String bookmark) { return null; } @Override public CompositeKey createCompositeKey(final String objectType, final String... attributes) { return null; } @Override public CompositeKey splitCompositeKey(final String compositeKey) { return null; } @Override public QueryResultsIterator getQueryResult(final String query) { return null; } @Override public QueryResultsIteratorWithMetadata getQueryResultWithPagination( final String query, final int pageSize, final String bookmark) { return null; } @Override public QueryResultsIterator getHistoryForKey(final String key) { return null; } @Override public byte[] getPrivateData(final String collection, final String key) { return new byte[0]; } @Override public byte[] getPrivateDataHash(final String collection, final String key) { return new byte[0]; } @Override public byte[] getPrivateDataValidationParameter(final String collection, final String key) { return new byte[0]; } @Override public void putPrivateData(final String collection, final String key, final byte[] value) {} @Override public void setPrivateDataValidationParameter(final String collection, final String key, final byte[] value) {} @Override public void delPrivateData(final String collection, final String key) {} @Override public void purgePrivateData(final String collection, final String key) {} @Override public QueryResultsIterator getPrivateDataByRange( final String collection, final String startKey, final String endKey) { return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String compositeKey) { return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final CompositeKey compositeKey) { return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String objectType, final String... attributes) { return null; } @Override public QueryResultsIterator getPrivateDataQueryResult(final String collection, final String query) { return null; } @Override public void setEvent(final String name, final byte[] payload) {} @Override public ChaincodeEvent getEvent() { return null; } @Override public SignedProposal getSignedProposal() { return null; } @Override public Instant getTxTimestamp() { return null; } @Override public byte[] getCreator() { return buildSerializedIdentity(); } @Override public Map getTransient() { return new HashMap<>(); } @Override public byte[] getBinding() { return new byte[0]; } void setStringArgs(final List args) { this.args = args; this.argsAsByte = args.stream().map(String::getBytes).collect(Collectors.toList()); } public byte[] buildSerializedIdentity() { final SerializedIdentity.Builder identity = SerializedIdentity.newBuilder(); identity.setMspid("testMSPID"); final byte[] decodedCert = Base64.getDecoder().decode(this.certificate); identity.setIdBytes(ByteString.copyFrom(decodedCert)); final SerializedIdentity builtIdentity = identity.build(); return builtIdentity.toByteArray(); } // Used by tests to control which serialized identity is returned by // buildSerializedIdentity public void setCertificate(final String certificateToTest) { this.certificate = certificateToTest; } @Override public String getMspId() { return "fakemspid"; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ClientIdentityTest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.math.BigInteger; import org.hyperledger.fabric.TestUtil; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Test; final class ClientIdentityTest { /** Test client identity can be created using certificate without attributes */ @Test void clientIdentityWithoutAttributes() throws Exception { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); final ClientIdentity identity = new ClientIdentity(stub); assertEquals(identity.getMSPID(), "testMSPID"); assertEquals( identity.getId(), "x509::CN=admin, OU=Fabric, O=Hyperledger, ST=North Carolina, C=US::CN=example.com," + " OU=WWW, O=Internet Widgets, L=San Francisco, ST=California, C=US"); assertEquals(identity.getAttributeValue("attr1"), null); assertEquals(identity.getAttributeValue("val1"), null); assertEquals( identity.getX509Certificate().getSubjectX500Principal().toString(), "CN=admin, OU=Fabric, O=Hyperledger, ST=North Carolina, C=US"); assertEquals( identity.getX509Certificate().getSerialNumber(), new BigInteger("689287698446788666856807436918134903862142510628")); } /** Test client identity can be created using certificate with attributes */ @Test void clientIdentityWithAttributes() throws Exception { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); ((ChaincodeStubNaiveImpl) stub).setCertificate(TestUtil.CERT_WITH_ATTRS); final ClientIdentity identity = new ClientIdentity(stub); assertEquals(identity.getMSPID(), "testMSPID"); assertEquals(identity.getId(), "x509::CN=MyTestUserWithAttrs::CN=fabric-ca-server"); assertEquals(identity.getAttributeValue("attr1"), "val1"); assertEquals(identity.getAttributeValue("val1"), null); assertEquals(identity.assertAttributeValue("attr1", "val1"), true); assertEquals(identity.assertAttributeValue("attr1", "val2"), false); assertEquals(identity.assertAttributeValue("attr2", "val1"), false); assertEquals(identity.getX509Certificate().getSubjectX500Principal().toString(), "CN=MyTestUserWithAttrs"); assertEquals( identity.getX509Certificate().getSerialNumber(), new BigInteger("172910998202207082780622887076293058980152824437")); } /** Test client identity can be created using certificate with multiple attributes */ @Test void clientIdentityWithMultipleAttributes() throws Exception { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); ((ChaincodeStubNaiveImpl) stub).setCertificate(TestUtil.CERT_MULTIPLE_ATTRIBUTES); final ClientIdentity identity = new ClientIdentity(stub); assertEquals(identity.getMSPID(), "testMSPID"); assertEquals( identity.getId(), "x509::CN=test, OU=client::CN=ca.org1.example.com, O=org1.example.com, L=San Francisco, ST=California, C=US"); assertEquals(identity.getAttributeValue("hello"), "world"); assertEquals(identity.getAttributeValue("foo"), "bar"); assertEquals(identity.getAttributeValue("attr1"), "val1"); assertEquals(identity.getAttributeValue("val1"), null); assertEquals(identity.assertAttributeValue("hello", "world"), true); assertEquals(identity.assertAttributeValue("attr1", "val2"), false); assertEquals(identity.assertAttributeValue("hello", "val1"), false); assertEquals(identity.getX509Certificate().getSubjectX500Principal().toString(), "CN=test, OU=client"); assertEquals( identity.getX509Certificate().getSerialNumber(), new BigInteger("400549269877250942864348502164024974865235124098")); } /** Test client identity can be created using certificate with long distinguished name */ @Test void clientIdentityWithLongDNs() throws Exception { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); ((ChaincodeStubNaiveImpl) stub).setCertificate(TestUtil.CERT_WITH_DNS); final ClientIdentity identity = new ClientIdentity(stub); assertEquals(identity.getMSPID(), "testMSPID"); assertEquals( identity.getId(), "x509::CN=User1@org2.example.com, L=San Francisco, ST=California," + " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US"); assertEquals( identity.getX509Certificate().getSubjectX500Principal().toString(), "CN=User1@org2.example.com, L=San Francisco, ST=California, C=US"); assertEquals( identity.getX509Certificate().getSerialNumber(), new BigInteger("175217963267961225716341475631843075227")); } /** Test client identity throws a ContractRuntimeException when creating a serialized identity fails */ @Test void catchInvalidProtocolBufferException() { final ChaincodeStub stub = mock(ChaincodeStub.class); when(stub.getCreator()).thenReturn("somethingInvalid".getBytes()); assertThatThrownBy(() -> ContextFactory.getInstance().createContext(stub)) .isInstanceOf(ContractRuntimeException.class) .hasMessage("Could not create new client identity"); } /** Test client identity attributes are empty when using a certificate with dummy attributes */ @Test void createClientIdentityWithDummyAttributesCert() throws Exception { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); // Create a certificate with rubbish attributes final String certWithDummyAttrs = TestUtil.createCertWithIdentityAttributes("{gsdhrlxhvcilgwoueglfs,djhzxo;vjs.dcx }"); ((ChaincodeStubNaiveImpl) stub).setCertificate(certWithDummyAttrs); final ClientIdentity identity = new ClientIdentity(stub); assertEquals(identity.getMSPID(), "testMSPID"); assertEquals(identity.getAttributeValue("attr1"), null); assertEquals(identity.getAttributeValue("val1"), null); assertEquals(identity.getX509Certificate().getSubjectX500Principal().toString(), "CN=test, OU=client"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ContextFactoryTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import java.util.Collections; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Test; final class ContextFactoryTest { @Test void getInstance() { final ContextFactory f1 = ContextFactory.getInstance(); final ContextFactory f2 = ContextFactory.getInstance(); assertThat(f1, sameInstance(f2)); } @Test void createContext() { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); final Context ctx = ContextFactory.getInstance().createContext(stub); assertThat(stub.getArgs(), is(equalTo(ctx.getStub().getArgs()))); assertThat(stub.getStringArgs(), is(equalTo(ctx.getStub().getStringArgs()))); assertThat(stub.getFunction(), is(equalTo(ctx.getStub().getFunction()))); assertThat(stub.getParameters(), is(equalTo(ctx.getStub().getParameters()))); assertThat(stub.getTxId(), is(equalTo(ctx.getStub().getTxId()))); assertThat(stub.getChannelId(), is(equalTo(ctx.getStub().getChannelId()))); assertThat( stub.invokeChaincode("cc", Collections.emptyList(), "ch0"), is(equalTo(ctx.getStub().invokeChaincode("cc", Collections.emptyList(), "ch0")))); assertThat(stub.getState("a"), is(equalTo(ctx.getStub().getState("a")))); ctx.getStub().putState("b", "sdfg".getBytes()); assertThat(stub.getStringState("b"), is(equalTo(ctx.getStub().getStringState("b")))); assertThat(ctx.clientIdentity.getMSPID(), is(equalTo("testMSPID"))); assertThat( ctx.clientIdentity.getId(), is(equalTo("x509::CN=admin, OU=Fabric, O=Hyperledger, ST=North Carolina," + " C=US::CN=example.com, OU=WWW, O=Internet Widgets, L=San Francisco, ST=California, C=US"))); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ContextTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Test; final class ContextTest { /** Test creating a new context returns what we expect */ @Test void getInstance() { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); final Context context1 = new Context(stub); final Context context2 = new Context(stub); assertThat(context1.getStub(), sameInstance(context2.getStub())); } /** Test identity created in Context constructor matches getClientIdentity */ @Test void getSetClientIdentity() { final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); final Context context = ContextFactory.getInstance().createContext(stub); assertThat(context.getClientIdentity(), sameInstance(context.clientIdentity)); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ContractInterfaceTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import org.hyperledger.fabric.shim.ChaincodeException; import org.junit.jupiter.api.Test; final class ContractInterfaceTest { @Test void createContext() { assertThat( new ContractInterface() {}.createContext(new ChaincodeStubNaiveImpl()), is(instanceOf(Context.class))); } @Test void unknownTransaction() { final ContractInterface c = new ContractInterface() {}; assertThatThrownBy(() -> c.unknownTransaction(c.createContext(new ChaincodeStubNaiveImpl()))) .isInstanceOf(ChaincodeException.class) .hasMessage("Undefined contract method called"); } @Test void beforeTransaction() { final ContractInterface c = new ContractInterface() {}; c.beforeTransaction(c.createContext(new ChaincodeStubNaiveImpl())); } @Test void afterTransaction() { final ContractInterface c = new ContractInterface() {}; c.afterTransaction(c.createContext(new ChaincodeStubNaiveImpl()), "ReturnValue"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/ContractRouterTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import contract.SampleContract; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.execution.ExecutionFactory; import org.hyperledger.fabric.contract.execution.InvocationRequest; import org.hyperledger.fabric.shim.Chaincode; import org.hyperledger.fabric.shim.ChaincodeServer; import org.hyperledger.fabric.shim.ChaincodeServerProperties; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.NettyChaincodeServer; import org.junit.jupiter.api.Test; final class ContractRouterTest { @Test void testCreateFailsWithoutValidOptions() { assertThatThrownBy(() -> new ContractRouter(new String[] {})) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("The chaincode id must be specified using either the -i or --i command " + "line options or the CORE_CHAINCODE_ID_NAME environment variable."); } @Test void testCreateAndScan() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); // Test Transaction routing final List args = new ArrayList<>(); args.add("samplecontract:t1"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); final InvocationRequest request = ExecutionFactory.getInstance().createRequest(s); assertThat( request.getNamespace(), is(equalTo(SampleContract.class.getAnnotation(Contract.class).name()))); assertThat(request.getMethod(), is(equalTo("t1"))); assertThat(request.getRequestName(), is(equalTo("samplecontract:t1"))); assertThat(request.getArgs(), is(contains(s.getArgs().get(1)))); } @Test void testInit() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t1"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.init(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(equalTo("asdf"))); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(1)); } /** Test invoking two transaction functions in a contract via fully qualified name */ @Test void testInvokeTwoTxnsThatExist() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t1"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(equalTo("asdf"))); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(1)); args.clear(); args.add("samplecontract:t5"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response secondResponse = r.invoke(s); assertThat(secondResponse, is(notNullValue())); assertThat(secondResponse.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(secondResponse.getMessage(), is(nullValue())); assertThat(secondResponse.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeTxnWithDefinedName() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t4"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(equalTo("Transaction 4"))); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); assertThat(SampleContract.getT1Invoked(), is(0)); } /** Test invoking two transaction functions in a contract via default name name */ @Test void testInvokeTwoTxnsWithDefaultNamespace() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("t1"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(equalTo("asdf"))); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(1)); args.clear(); args.add("t5"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response secondResponse = r.invoke(s); assertThat(secondResponse, is(notNullValue())); assertThat(secondResponse.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(secondResponse.getMessage(), is(nullValue())); assertThat(secondResponse.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeTxnWithDefinedNameUsingMethodName() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:tFour"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.INTERNAL_SERVER_ERROR)); assertThat(response.getMessage(), is(equalTo("Undefined contract method called"))); assertThat(response.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(0)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeContractThatDoesNotExist() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("thereisnocontract:t1"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.INTERNAL_SERVER_ERROR)); assertThat(response.getMessage(), is(equalTo("Undefined contract called"))); assertThat(response.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(0)); assertThat(SampleContract.getAfterInvoked(), is(0)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeTxnThatDoesNotExist() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:notsupposedtoexist"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.INTERNAL_SERVER_ERROR)); assertThat(response.getMessage(), is(equalTo("Undefined contract method called"))); assertThat(response.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(0)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeTxnThatReturnsNullString() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t5"); args.add("asdf"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(0)); } @Test void testInvokeTxnThatThrowsAnException() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t3"); args.add("RuntimeException"); args.add("T3 fail!"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.INTERNAL_SERVER_ERROR)); assertThat(response.getMessage(), is(equalTo("Error during contract method execution"))); assertThat(response.getStringPayload(), is(nullValue())); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(0)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); } @Test void testInvokeTxnThatThrowsAChaincodeException() { final ContractRouter r = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); r.findAllContracts(); final ChaincodeStub s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t3"); args.add("TransactionException"); args.add("T3 fail!"); ((ChaincodeStubNaiveImpl) s).setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); final Chaincode.Response response = r.invoke(s); assertThat(response, is(notNullValue())); assertThat(response.getStatus(), is(Chaincode.Response.Status.INTERNAL_SERVER_ERROR)); assertThat(response.getMessage(), is(equalTo("T3 fail!"))); assertThat(response.getStringPayload(), is("T3ERR1")); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(0)); assertThat(SampleContract.getDoWorkInvoked(), is(0)); } /** Test confirming ContractRuntimeExceptions can be created. */ @Test void createContractRuntimeExceptions() { final ContractRuntimeException cre1 = new ContractRuntimeException("failure"); new ContractRuntimeException("another failure", cre1); new ContractRuntimeException(new Exception("cause")); } @Test void testStartingContractRouterWithStartingAChaincodeServer() throws IOException { ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setServerAddress(new InetSocketAddress("0.0.0.0", 9999)); final ContractRouter r = new ContractRouter(new String[] {"-i", "testId"}); ChaincodeServer chaincodeServer = new NettyChaincodeServer(r, chaincodeServerProperties); new Thread(() -> { try { r.startRouterWithChaincodeServer(chaincodeServer); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } final ChaincodeStubNaiveImpl s = new ChaincodeStubNaiveImpl(); final List args = new ArrayList<>(); args.add("samplecontract:t1"); args.add("asdf"); s.setStringArgs(args); SampleContract.setBeforeInvoked(0); SampleContract.setAfterInvoked(0); SampleContract.setDoWorkInvoked(0); SampleContract.setT1Invoked(0); final Chaincode.Response response = r.init(s); assertThat(response, is(notNullValue())); assertThat( response.getMessage() + " " + response.getStringPayload() + response.toString(), response.getStatus(), is(Chaincode.Response.Status.SUCCESS)); assertThat(response.getMessage(), is(nullValue())); assertThat(response.getStringPayload(), is(equalTo("asdf"))); assertThat(SampleContract.getBeforeInvoked(), is(1)); assertThat(SampleContract.getAfterInvoked(), is(1)); assertThat(SampleContract.getDoWorkInvoked(), is(1)); assertThat(SampleContract.getT1Invoked(), is(1)); chaincodeServer.stop(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/MyType.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; import org.json.JSONPropertyIgnore; @DataType public final class MyType { @Property() private String value; private String state = ""; public static final String STARTED = "STARTED"; public static final String STOPPED = "STOPPED"; public void setState(final String state) { this.state = state; } @JSONPropertyIgnore() public boolean isStarted() { return STARTED.equals(state); } @JSONPropertyIgnore() public boolean isStopped() { return STOPPED.equals(state); } public MyType setValue(final String value) { this.value = value; return this; } public String getValue() { return this.value; } @Override public String toString() { return "++++ MyType: " + value; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/MyType2.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.annotation.Property; @DataType public final class MyType2 { @Property() private String value; @Property( schema = { "title", "MrProperty", "Pattern", "[a-z]", "uniqueItems", "false", "required", "true,false", "enum", "a,bee,cee,dee", "minimum", "42" }) private String constrainedValue; public MyType2 setValue(final String value) { this.value = value; return this; } public String getValue() { return this.value; } @Override public String toString() { return "++++ MyType: " + value; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/TransactionExceptionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import org.hyperledger.fabric.shim.ChaincodeException; import org.junit.jupiter.api.Test; final class TransactionExceptionTest { class MyTransactionException extends ChaincodeException { private static final long serialVersionUID = 1L; private final int errorCode; MyTransactionException(final int errorCode) { super("MyTransactionException"); this.errorCode = errorCode; } @Override public byte[] getPayload() { final String payload = String.format("E%03d", errorCode); return payload.getBytes(); } } @Test void testNoArgConstructor() { final ChaincodeException e = new ChaincodeException(); assertThat(e.getMessage(), is(nullValue())); assertThat(e.getPayload(), is(nullValue())); } @Test void testMessageArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure"); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(nullValue())); } @Test void testCauseArgConstructor() { final ChaincodeException e = new ChaincodeException(new Error("Cause")); assertThat(e.getMessage(), is("java.lang.Error: Cause")); assertThat(e.getPayload(), is(nullValue())); assertThat(e.getCause().getMessage(), is("Cause")); } @Test void testMessageAndCauseArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure", new Error("Cause")); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(nullValue())); assertThat(e.getCause().getMessage(), is("Cause")); } @Test void testMessageAndPayloadArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure", new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'}); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'})); } @Test void testMessagePayloadAndCauseArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure", new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'}, new Error("Cause")); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'})); assertThat(e.getCause().getMessage(), is("Cause")); } @Test void testMessageAndStringPayloadArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure", "Payload"); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'})); } @Test void testMessageStringPayloadAndCauseArgConstructor() { final ChaincodeException e = new ChaincodeException("Failure", "Payload", new Error("Cause")); assertThat(e.getMessage(), is("Failure")); assertThat(e.getPayload(), is(new byte[] {'P', 'a', 'y', 'l', 'o', 'a', 'd'})); assertThat(e.getCause().getMessage(), is("Cause")); } @Test void testSubclass() { final ChaincodeException e = new MyTransactionException(1); assertThat(e.getMessage(), is("MyTransactionException")); assertThat(e.getPayload(), is(new byte[] {'E', '0', '0', '1'})); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/execution/ContractExecutionServiceTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import contract.SampleContract; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Collections; import org.hyperledger.fabric.contract.ChaincodeStubNaiveImpl; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Serializer; import org.hyperledger.fabric.contract.execution.impl.ContractExecutionService; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.ParameterDefinition; import org.hyperledger.fabric.contract.routing.TxFunction; import org.hyperledger.fabric.contract.routing.impl.ParameterDefinitionImpl; import org.hyperledger.fabric.contract.routing.impl.SerializerRegistryImpl; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Test; final class ContractExecutionServiceTest { @Test void noReturnValue() throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, SecurityException { JSONTransactionSerializer jts = new JSONTransactionSerializer(); SerializerRegistryImpl serializerRegistry = spy(new SerializerRegistryImpl()); ContractExecutionService ces = new ContractExecutionService(serializerRegistry); ContractInterface contract = spy(new SampleContract()); TxFunction txFn = mock(TxFunction.class); InvocationRequest req = mock(InvocationRequest.class); TxFunction.Routing routing = mock(TxFunction.Routing.class); ChaincodeStub stub = new ChaincodeStubNaiveImpl(); when(txFn.getRouting()).thenReturn(routing); when(req.getArgs()).thenReturn(new ArrayList<>()); when(routing.getMethod()) .thenReturn(SampleContract.class.getMethod("noReturn", new Class[] {Context.class})); when(routing.getContractInstance()).thenReturn(contract); when(serializerRegistry.getSerializer(any(), any())).thenReturn(jts); ces.executeRequest(txFn, req, stub); verify(contract).beforeTransaction(any()); } @Test() void failureToInvoke() throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, SecurityException { JSONTransactionSerializer jts = new JSONTransactionSerializer(); SerializerRegistryImpl serializerRegistry = spy(new SerializerRegistryImpl()); ContractExecutionService ces = new ContractExecutionService(serializerRegistry); spy(new SampleContract()); TxFunction txFn = mock(TxFunction.class); InvocationRequest req = mock(InvocationRequest.class); TxFunction.Routing routing = mock(TxFunction.Routing.class); ChaincodeStub stub = mock(ChaincodeStub.class); when(txFn.getRouting()).thenReturn(routing); when(req.getArgs()).thenReturn(new ArrayList<>() {}); when(routing.getContractInstance()).thenThrow(IllegalAccessException.class); when(routing.toString()).thenReturn("MockMethodName:MockClassName"); when(serializerRegistry.getSerializer(any(), any())).thenReturn(jts); assertThatThrownBy(() -> ces.executeRequest(txFn, req, stub)) .isInstanceOf(ContractRuntimeException.class) .hasMessage("Could not execute contract method: MockMethodName:MockClassName"); } @Test() void invokeWithDifferentSerializers() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { JSONTransactionSerializer defaultSerializer = spy(new JSONTransactionSerializer()); SerializerInterface customSerializer = mock(SerializerInterface.class); SerializerRegistryImpl serializerRegistry = spy(new SerializerRegistryImpl()); ExecutionService executionService = ExecutionFactory.getInstance().createExecutionService(serializerRegistry); TxFunction txFn = mock(TxFunction.class); InvocationRequest req = mock(InvocationRequest.class); TxFunction.Routing routing = mock(TxFunction.Routing.class); TypeSchema ts = TypeSchema.typeConvert(String.class); Method method = SampleContract.class.getMethod("t1", Context.class, String.class); Parameter[] params = method.getParameters(); ParameterDefinition pd = new ParameterDefinitionImpl("arg1", String.class, ts, params[1]); byte[] arg = "asdf".getBytes(); ChaincodeStub stub = new ChaincodeStubNaiveImpl(); ContractInterface contract = spy(new SampleContract()); when(req.getArgs()).thenReturn(Collections.singletonList(arg)); when(txFn.getRouting()).thenReturn(routing); when(txFn.getParamsList()).thenReturn(Collections.singletonList(pd)); when(txFn.getReturnSchema()).thenReturn(ts); when(routing.getMethod()).thenReturn(method); when(routing.getContractInstance()).thenReturn(contract); String defaultSerializerName = defaultSerializer.getClass().getCanonicalName(); String customSerializerName = "customSerializer"; // execute transaction with the default serializer when(routing.getSerializerName()).thenReturn(defaultSerializerName); when(serializerRegistry.getSerializer(defaultSerializerName, Serializer.TARGET.TRANSACTION)) .thenReturn(defaultSerializer); executionService.executeRequest(txFn, req, stub); // execute transaction with the custom serializer when(routing.getSerializerName()).thenReturn(customSerializerName); when(serializerRegistry.getSerializer(customSerializerName, Serializer.TARGET.TRANSACTION)) .thenReturn(customSerializer); executionService.executeRequest(txFn, req, stub); verify(defaultSerializer).fromBuffer(arg, ts); verify(customSerializer).fromBuffer(arg, ts); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/execution/JSONTransactionSerializerTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.execution; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.charset.StandardCharsets; import org.hyperledger.fabric.contract.AllTypesAsset; import org.hyperledger.fabric.contract.MyType; import org.hyperledger.fabric.contract.metadata.MetadataBuilder; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; final class JSONTransactionSerializerTest { @Test void toBuffer() { final TypeRegistry tr = TypeRegistry.getRegistry(); tr.addDataType(MyType.class); MetadataBuilder.addComponent(tr.getDataType("MyType")); final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); byte[] bytes = serializer.toBuffer("hello world", TypeSchema.typeConvert(String.class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("hello world")); bytes = serializer.toBuffer(42, TypeSchema.typeConvert(Integer.class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("42")); bytes = serializer.toBuffer(true, TypeSchema.typeConvert(Boolean.class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("true")); bytes = serializer.toBuffer(new MyType(), TypeSchema.typeConvert(MyType.class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("{}")); bytes = serializer.toBuffer(new MyType().setValue("Hello"), TypeSchema.typeConvert(MyType.class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("{\"value\":\"Hello\"}")); final MyType[] array = new MyType[2]; array[0] = new MyType().setValue("hello"); array[1] = new MyType().setValue("world"); bytes = serializer.toBuffer(array, TypeSchema.typeConvert(MyType[].class)); final byte[] buffer = "[{\"value\":\"hello\"},{\"value\":\"world\"}]".getBytes(StandardCharsets.UTF_8); assertThat(bytes, equalTo(buffer)); } @Nested @DisplayName("Complex Data types") final class ComplexDataTypes { @Test public void alltypes() { final TypeRegistry tr = TypeRegistry.getRegistry(); tr.addDataType(AllTypesAsset.class); tr.addDataType(MyType.class); MetadataBuilder.addComponent(tr.getDataType("MyType")); MetadataBuilder.addComponent(tr.getDataType("AllTypesAsset")); final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final AllTypesAsset all = new AllTypesAsset(); final TypeSchema ts = TypeSchema.typeConvert(AllTypesAsset.class); final byte[] bytes = serializer.toBuffer(all, ts); final AllTypesAsset returned = (AllTypesAsset) serializer.fromBuffer(bytes, ts); assertTrue(all.equals(returned)); } } @Nested @DisplayName("Primitive Arrays") final class PrimitiveArrays { @Test void ints() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); // convert array of primitive final int[] intarray = new int[] {42, 83}; final byte[] bytes = serializer.toBuffer(intarray, TypeSchema.typeConvert(int[].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[42,83]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(int[].class)); assertThat(returned, equalTo(intarray)); } @Test void bytes() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); // convert array of primitive final byte[] array = new byte[] {42, 83}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(byte[].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[42,83]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(byte[].class)); assertThat(returned, equalTo(array)); } @Test void floats() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); // convert array of primitive final float[] array = new float[] {42.5F, 83.5F}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(float[].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[42.5,83.5]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(float[].class)); assertThat(returned, equalTo(array)); } @Test void booleans() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); // convert array of primitive final boolean[] array = new boolean[] {true, false, true}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(boolean[].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[true,false,true]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(boolean[].class)); assertThat(returned, equalTo(array)); } @Test void chars() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); // convert array of primitive final char[] array = new char[] {'a', 'b', 'c'}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(char[].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[\"a\",\"b\",\"c\"]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(char[].class)); assertThat(returned, equalTo(array)); } } @Nested @DisplayName("Nested Arrays") final class NestedArrays { @Test void ints() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final int[][] array = new int[][] {{42, 83}, {83, 42}}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(int[][].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[[42,83],[83,42]]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(int[][].class)); assertThat(returned, equalTo(array)); } @Test void longs() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final long[][] array = new long[][] {{42L, 83L}, {83L, 42L}}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(long[][].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[[42,83],[83,42]]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(long[][].class)); assertThat(returned, equalTo(array)); } @Test void doubles() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final double[][] array = new double[][] {{42.42d, 83.83d}, {83.23d, 42.33d}}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(double[][].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[[42.42,83.83],[83.23,42.33]]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(double[][].class)); assertThat(returned, equalTo(array)); } @Test void bytes() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final byte[][] array = new byte[][] {{42, 83}, {83, 42}}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(byte[][].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[[42,83],[83,42]]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(byte[][].class)); assertThat(returned, equalTo(array)); } @Test void shorts() { final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final short[][] array = new short[][] {{42, 83}, {83, 42}}; final byte[] bytes = serializer.toBuffer(array, TypeSchema.typeConvert(short[][].class)); assertThat(new String(bytes, StandardCharsets.UTF_8), equalTo("[[42,83],[83,42]]")); final Object returned = serializer.fromBuffer(bytes, TypeSchema.typeConvert(short[][].class)); assertThat(returned, equalTo(array)); } } @Test void fromBufferObject() { final byte[] buffer = "[{\"value\":\"hello\"},{\"value\":\"world\"}]".getBytes(StandardCharsets.UTF_8); final TypeRegistry tr = TypeRegistry.getRegistry(); tr.addDataType(MyType.class); MetadataBuilder.addComponent(tr.getDataType("MyType")); final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final TypeSchema ts = TypeSchema.typeConvert(MyType[].class); final MyType[] o = (MyType[]) serializer.fromBuffer(buffer, ts); assertThat(o[0].toString(), equalTo("++++ MyType: hello")); assertThat(o[1].toString(), equalTo("++++ MyType: world")); } @Test void toBufferPrimitive() { final TypeRegistry tr = TypeRegistry.getRegistry(); final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); TypeSchema ts; Object value; byte[] buffer; ts = TypeSchema.typeConvert(boolean.class); value = false; buffer = serializer.toBuffer(value, ts); assertThat(buffer, equalTo(new byte[] {102, 97, 108, 115, 101})); assertThat(serializer.fromBuffer(buffer, ts), equalTo(false)); ts = TypeSchema.typeConvert(int.class); value = 1; buffer = serializer.toBuffer(value, ts); assertThat(buffer, equalTo(new byte[] {49})); assertThat(serializer.fromBuffer(buffer, ts), equalTo(1)); ts = TypeSchema.typeConvert(long.class); value = 9192631770L; buffer = serializer.toBuffer(value, ts); assertThat(buffer, equalTo(new byte[] {57, 49, 57, 50, 54, 51, 49, 55, 55, 48})); assertThat(serializer.fromBuffer(buffer, ts), equalTo(9192631770L)); ts = TypeSchema.typeConvert(float.class); final float f = 3.1415927F; buffer = serializer.toBuffer(f, ts); assertThat(buffer, equalTo(new byte[] {51, 46, 49, 52, 49, 53, 57, 50, 55})); assertThat(serializer.fromBuffer(buffer, ts), equalTo(3.1415927F)); ts = TypeSchema.typeConvert(double.class); final double d = 2.7182818284590452353602874713527; buffer = serializer.toBuffer(d, ts); assertThat(buffer, equalTo(new byte[] {50, 46, 55, 49, 56, 50, 56, 49, 56, 50, 56, 52, 53, 57, 48, 52, 53})); assertThat(serializer.fromBuffer(buffer, ts), equalTo(2.7182818284590452353602874713527)); } @Test void fromBufferErrors() { final TypeRegistry tr = new TypeRegistryImpl(); tr.addDataType(MyType.class); MetadataBuilder.addComponent(tr.getDataType("MyType")); final JSONTransactionSerializer serializer = new JSONTransactionSerializer(); final TypeSchema ts = TypeSchema.typeConvert(MyType[].class); serializer.toBuffer(null, ts); } class MyTestObject {} } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/metadata/MetadataBuilderTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.metadata; import contract.SampleContract; import java.io.InputStream; import java.lang.reflect.Field; import java.util.HashMap; import org.everit.json.schema.loader.SchemaClient; import org.everit.json.schema.loader.internal.DefaultSchemaClient; import org.hyperledger.fabric.contract.ChaincodeStubNaiveImpl; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.routing.ContractDefinition; import org.hyperledger.fabric.contract.routing.impl.ContractDefinitionImpl; import org.hyperledger.fabric.contract.systemcontract.SystemContract; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; final class MetadataBuilderTest { // fields are private, so use reflection to bypass this for unit testing private void setMetadataBuilderField(final String name, final Object value) throws NoSuchFieldException, IllegalAccessException { final Field f = MetadataBuilder.class.getDeclaredField(name); f.setAccessible(true); f.set(null, value); } @BeforeEach @AfterEach void beforeAndAfterEach() throws NoSuchFieldException, IllegalAccessException { setMetadataBuilderField("componentMap", new HashMap<>()); setMetadataBuilderField("contractMap", new HashMap<>()); setMetadataBuilderField("overallInfoMap", new HashMap<>()); setMetadataBuilderField("schemaClient", new DefaultSchemaClient()); } @Test void systemContract() { final SystemContract system = new SystemContract(); final ChaincodeStub stub = new ChaincodeStubNaiveImpl(); system.getMetadata(new Context(stub)); } @Test void defaultSchemasNotLoadedFromNetwork() throws NoSuchFieldException, IllegalAccessException { final ContractDefinition contractDefinition = new ContractDefinitionImpl(SampleContract.class); MetadataBuilder.addContract(contractDefinition); setMetadataBuilderField("schemaClient", new SchemaClient() { @Override public InputStream get(final String uri) { throw new IllegalStateException("Refusing to load schema: " + uri); } }); MetadataBuilder.validate(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/metadata/TypeSchemaTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.metadata; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; import org.hyperledger.fabric.contract.annotation.DataType; import org.hyperledger.fabric.contract.routing.DataTypeDefinition; import org.hyperledger.fabric.contract.routing.TypeRegistry; import org.hyperledger.fabric.contract.routing.impl.DataTypeDefinitionImpl; import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; final class TypeSchemaTest { @BeforeEach void beforeEach() {} @Test void putIfNotNull() { final TypeSchema ts = new TypeSchema(); ts.putIfNotNull("Key", "value"); final String nullstr = null; ts.putIfNotNull("Key", nullstr); assertThat(ts.get("Key"), equalTo("value")); ts.putIfNotNull("Key", ""); assertThat(ts.get("Key"), equalTo("value")); } @Test void getType() { final TypeSchema ts = new TypeSchema(); ts.put("type", "MyType"); assertThat(ts.getType(), equalTo("MyType")); final TypeSchema wrapper = new TypeSchema(); wrapper.put("schema", ts); assertThat(wrapper.getType(), equalTo("MyType")); } @Test void getFormat() { final TypeSchema ts = new TypeSchema(); ts.put("format", "MyFormat"); assertThat(ts.getFormat(), equalTo("MyFormat")); final TypeSchema wrapper = new TypeSchema(); wrapper.put("schema", ts); assertThat(wrapper.getFormat(), equalTo("MyFormat")); } @Test void getRef() { final TypeSchema ts = new TypeSchema(); ts.put("$ref", "#/ref/to/MyType"); assertThat(ts.getRef(), equalTo("#/ref/to/MyType")); final TypeSchema wrapper = new TypeSchema(); wrapper.put("schema", ts); assertThat(wrapper.getRef(), equalTo("#/ref/to/MyType")); } @Test void getItems() { final TypeSchema ts1 = new TypeSchema(); final TypeSchema ts = new TypeSchema(); ts.put("items", ts1); assertThat(ts.getItems(), equalTo(ts1)); final TypeSchema wrapper = new TypeSchema(); wrapper.put("schema", ts); assertThat(wrapper.getItems(), equalTo(ts1)); } @DataType class MyType {} @Test void getTypeClass() { final TypeSchema ts = new TypeSchema(); ts.put("type", "string"); final TypeRegistry mockRegistry = new TypeRegistryImpl(); assertThat(ts.getTypeClass(mockRegistry), equalTo(String.class)); ts.put("type", "integer"); ts.put("format", "int8"); assertThat(ts.getTypeClass(mockRegistry), equalTo(byte.class)); ts.put("type", "integer"); ts.put("format", "int16"); assertThat(ts.getTypeClass(mockRegistry), equalTo(short.class)); ts.put("type", "integer"); ts.put("format", "int32"); assertThat(ts.getTypeClass(mockRegistry), equalTo(int.class)); ts.put("type", "integer"); ts.put("format", "int64"); assertThat(ts.getTypeClass(mockRegistry), equalTo(long.class)); ts.put("type", "number"); ts.put("format", "double"); assertThat(ts.getTypeClass(mockRegistry), equalTo(double.class)); ts.put("type", "number"); ts.put("format", "float"); assertThat(ts.getTypeClass(mockRegistry), equalTo(float.class)); ts.put("type", "boolean"); assertThat(ts.getTypeClass(mockRegistry), equalTo(boolean.class)); ts.put("type", null); ts.put("$ref", "#/ref/to/MyType"); mockRegistry.addDataType(MyType.class); assertThat(ts.getTypeClass(mockRegistry), equalTo(MyType.class)); final TypeSchema array = new TypeSchema(); array.put("type", "array"); array.put("items", ts); assertThat(array.getTypeClass(mockRegistry), equalTo(MyType[].class)); } @Test void unknownConversions() { assertThrows(RuntimeException.class, () -> { final TypeSchema ts = new TypeSchema(); final TypeRegistry mockRegistry = new TypeRegistryImpl(); ts.put("type", "integer"); ts.put("format", "int63"); ts.getTypeClass(mockRegistry); }); assertThrows(RuntimeException.class, () -> { final TypeSchema ts = new TypeSchema(); final TypeRegistry mockRegistry = new TypeRegistryImpl(); ts.put("type", "number"); ts.put("format", "approximate"); ts.getTypeClass(mockRegistry); }); } @Test void typeConvertPrimitives() { TypeSchema rts; final String[] array = new String[] {}; rts = TypeSchema.typeConvert(array.getClass()); assertThat(rts.getType(), equalTo("array")); rts = TypeSchema.typeConvert(int.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(long.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(float.class); assertThat(rts.getType(), equalTo("number")); rts = TypeSchema.typeConvert(double.class); assertThat(rts.getType(), equalTo("number")); rts = TypeSchema.typeConvert(byte.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(short.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(boolean.class); assertThat(rts.getType(), equalTo("boolean")); } @Test void typeConvertObjects() { TypeSchema rts; rts = TypeSchema.typeConvert(String.class); assertThat(rts.getType(), equalTo("string")); final String[] array = new String[] {}; rts = TypeSchema.typeConvert(array.getClass()); assertThat(rts.getType(), equalTo("array")); rts = TypeSchema.typeConvert(Integer.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(Long.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(Float.class); assertThat(rts.getType(), equalTo("number")); rts = TypeSchema.typeConvert(Double.class); assertThat(rts.getType(), equalTo("number")); rts = TypeSchema.typeConvert(Byte.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(Short.class); assertThat(rts.getType(), equalTo("integer")); rts = TypeSchema.typeConvert(Boolean.class); assertThat(rts.getType(), equalTo("boolean")); rts = TypeSchema.typeConvert(MyType.class); assertThat(rts.getRef(), equalTo("#/components/schemas/TypeSchemaTest$MyType")); } @Test void validate() { final TypeSchema ts = TypeSchema.typeConvert(org.hyperledger.fabric.contract.MyType.class); final DataTypeDefinition dtd = new DataTypeDefinitionImpl(org.hyperledger.fabric.contract.MyType.class); MetadataBuilder.addComponent(dtd); final JSONObject json = new JSONObject(); ts.validate(json); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/ContractDefinitionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import contract.SampleContract; import java.lang.reflect.Method; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.routing.impl.ContractDefinitionImpl; import org.junit.jupiter.api.Test; final class ContractDefinitionTest { @Test void constructor() throws SecurityException { final ContractDefinition cf = new ContractDefinitionImpl(SampleContract.class); assertThat(cf.toString()).startsWith("samplecontract:"); } @Test void duplicateTransaction() throws NoSuchMethodException, SecurityException { final ContractDefinition cf = new ContractDefinitionImpl(SampleContract.class); final ContractInterface contract = new SampleContract(); final Method m = contract.getClass().getMethod("t2", new Class[] {Context.class}); cf.addTxFunction(m); assertThatThrownBy(() -> cf.addTxFunction(m)) .isInstanceOf(ContractRuntimeException.class) .hasMessage("Duplicate transaction method t2"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/DataTypeDefinitionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.util.Map; import org.hyperledger.fabric.contract.MyType2; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.impl.DataTypeDefinitionImpl; import org.junit.jupiter.api.Test; final class DataTypeDefinitionTest { @Test void constructor() { final DataTypeDefinitionImpl dtd = new DataTypeDefinitionImpl(MyType2.class); assertThat(dtd.getTypeClass()).isEqualTo(MyType2.class); assertThat(dtd.getName()).isEqualTo("org.hyperledger.fabric.contract.MyType2"); assertThat(dtd.getSimpleName()).isEqualTo("MyType2"); final Map properties = dtd.getProperties(); assertThat(properties.size()).isEqualTo(2); assertThat(properties).containsKey("value"); assertThat(properties).containsKey("constrainedValue"); final PropertyDefinition pd = properties.get("constrainedValue"); final TypeSchema ts = pd.getSchema(); assertThat(ts) .contains( entry("title", "MrProperty"), entry("Pattern", "[a-z]"), entry("uniqueItems", false), entry("required", new String[] {"true", "false"}), entry("enum", new String[] {"a", "bee", "cee", "dee"}), entry("minimum", 42)); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/ParameterDefinitionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.lang.reflect.Parameter; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.impl.ParameterDefinitionImpl; import org.junit.jupiter.api.Test; final class ParameterDefinitionTest { @Test void constructor() throws NoSuchMethodException, SecurityException { final Parameter[] params = String.class.getMethod("concat", String.class).getParameters(); final ParameterDefinition pd = new ParameterDefinitionImpl("test", String.class, new TypeSchema(), params[0]); assertThat(pd.toString(), equalTo("test-class java.lang.String-{}-java.lang.String arg0")); assertThat(pd.getTypeClass(), equalTo(String.class)); assertThat(pd.getParameter(), equalTo(params[0])); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/PropertyDefinitionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.lang.reflect.Field; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.impl.PropertyDefinitionImpl; import org.junit.jupiter.api.Test; final class PropertyDefinitionTest { @Test void constructor() throws NoSuchMethodException, SecurityException { final Field[] props = String.class.getFields(); final TypeSchema ts = new TypeSchema(); final PropertyDefinition pd = new PropertyDefinitionImpl("test", String.class, ts, props[0]); assertThat(pd.getTypeClass(), equalTo(String.class)); assertThat(pd.getField(), equalTo(props[0])); assertThat(pd.getSchema(), equalTo(ts)); assertThat(pd.getName(), equalTo("test")); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/TxFunctionTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractRuntimeException; import org.hyperledger.fabric.contract.annotation.Contract; import org.hyperledger.fabric.contract.annotation.Property; import org.hyperledger.fabric.contract.annotation.Transaction; import org.hyperledger.fabric.contract.metadata.TypeSchema; import org.hyperledger.fabric.contract.routing.impl.TxFunctionImpl; import org.junit.jupiter.api.Test; import org.mockito.Mockito; final class TxFunctionTest { @Contract() class TestObject implements ContractInterface { @Transaction() public void testMethod1(final Context ctx) {} @Transaction() public void testMethod2(final Context ctx, @Property(schema = {"a", "b"}) final int arg) {} @Transaction() public void wibble(final String arg1) {} } @Test void constructor() throws NoSuchMethodException, SecurityException { final TestObject test = new TestObject(); final ContractDefinition cd = mock(ContractDefinition.class); Mockito.when(cd.getAnnotation()).thenReturn(test.getClass().getAnnotation(Contract.class)); final TxFunction txfn = new TxFunctionImpl(test.getClass().getMethod("testMethod1", new Class[] {Context.class}), cd); final String name = txfn.getName(); assertEquals(name, "testMethod1"); assertThat(txfn.toString(), startsWith("testMethod1")); } @Test void property() throws NoSuchMethodException, SecurityException { final TestObject test = new TestObject(); final ContractDefinition cd = mock(ContractDefinition.class); Mockito.when(cd.getAnnotation()).thenReturn(test.getClass().getAnnotation(Contract.class)); final TxFunction txfn = new TxFunctionImpl( test.getClass().getMethod("testMethod2", new Class[] {Context.class, int.class}), cd); final String name = txfn.getName(); assertEquals(name, "testMethod2"); assertThat(txfn.toString(), startsWith("testMethod2")); assertFalse(txfn.isUnknownTx()); txfn.setUnknownTx(true); assertTrue(txfn.isUnknownTx()); final TypeSchema ts = new TypeSchema(); txfn.setReturnSchema(ts); final TypeSchema rts = txfn.getReturnSchema(); assertEquals(ts, rts); } @Test void invaldtxfn() throws NoSuchMethodException, SecurityException { final TestObject test = new TestObject(); final ContractDefinition cd = mock(ContractDefinition.class); Mockito.when(cd.getAnnotation()).thenReturn(test.getClass().getAnnotation(Contract.class)); assertThatThrownBy(() -> new TxFunctionImpl(test.getClass().getMethod("wibble", String.class), cd)) .isInstanceOf(ContractRuntimeException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/routing/TypeRegistryTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.routing; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.util.Collection; import org.hyperledger.fabric.contract.routing.impl.DataTypeDefinitionImpl; import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl; import org.junit.jupiter.api.Test; final class TypeRegistryTest { @Test void addDataType() { final TypeRegistryImpl tr = new TypeRegistryImpl(); tr.addDataType(String.class); final DataTypeDefinition drt = tr.getDataType("String"); assertThat(drt.getName(), equalTo("java.lang.String")); } @Test void addDataTypeDefinition() { final DataTypeDefinitionImpl dtd = new DataTypeDefinitionImpl(String.class); final TypeRegistryImpl tr = new TypeRegistryImpl(); tr.addDataType(dtd); final DataTypeDefinition drt = tr.getDataType("java.lang.String"); assertThat(drt.getName(), equalTo("java.lang.String")); } @Test void getAllDataTypes() { final TypeRegistryImpl tr = new TypeRegistryImpl(); tr.addDataType(String.class); tr.addDataType(Integer.class); tr.addDataType(Float.class); final Collection c = tr.getAllDataTypes(); assertThat(c.size(), equalTo(3)); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/simplepath/ContractSimplePathTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.contract.simplepath; import static org.assertj.core.api.Assertions.assertThat; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.READY; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.REGISTER; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.TRANSACTION; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.hyperledger.fabric.contract.ContractRouter; import org.hyperledger.fabric.protos.peer.ChaincodeInput; import org.hyperledger.fabric.protos.peer.ChaincodeInput.Builder; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.Response; import org.hyperledger.fabric.shim.mock.peer.ChaincodeMockPeer; import org.hyperledger.fabric.shim.mock.peer.RegisterStep; import org.hyperledger.fabric.shim.mock.peer.ScenarioStep; import org.hyperledger.fabric.shim.utils.MessageUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ContractSimplePathTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); private ChaincodeMockPeer server; @AfterEach void afterTest() throws Exception { if (server != null) { server.stop(); server = null; } } /** * Test starting the contract logic * * @throws Exception */ @Test void testContract() throws Exception { final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); ContractRouter.main(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType()).isEqualTo(READY); assertThat(server.getLastMessageRcvd().getType()).isEqualTo(REGISTER); setLogLevel("INFO"); } private ChaincodeMessage newInvokeFn(final String[] args) { final Builder invokePayload = ChaincodeInput.newBuilder(); for (final String arg : args) { invokePayload.addArgs(ByteString.copyFromUtf8(arg)); } return MessageUtil.newEventMessage( TRANSACTION, "testChannel", "0", invokePayload.build().toByteString(), null); } private String getLastReturnString() throws Exception { final Response resp = Response.parseFrom(server.getLastMessageRcvd().getPayload()); return resp.getPayload().toStringUtf8(); } private void setLogLevel(final String logLevel) { environmentVariables.set("CORE_CHAINCODE_LOGGING_SHIM", logLevel); environmentVariables.set("CORE_CHAINCODE_LOGGING_LEVEL", logLevel); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/ledger/LedgerTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.ledger; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import org.hyperledger.fabric.contract.Context; import org.junit.jupiter.api.Test; final class LedgerTest { @Test void getLedger() { final Context ctx = mock(Context.class); final Ledger ledger = Ledger.getLedger(ctx); assertThat(ledger).isNotNull(); assertThat(ledger).isInstanceOf(Ledger.class); // assert that the ledger instance is new final Ledger ledger2 = Ledger.getLedger(ctx); assertThat(ledger2).isNotSameAs(ledger); } @Test void getCollection() { final Context ctx = mock(Context.class); final Ledger ledger = Ledger.getLedger(ctx); final Collection collection = ledger.getDefaultCollection(); assertThat(collection).isNotNull(); assertThat(collection).isInstanceOf(Collection.class); collection.placeholder(); final Collection collection2 = ledger.getDefaultCollection(); assertThat(collection2).isNotSameAs(collection); } @Test void getNamedCollection() { final Context ctx = mock(Context.class); final Ledger ledger = Ledger.getLedger(ctx); final Collection collection = ledger.getCollection("myPrivateCollection"); assertThat(collection).isNotNull(); assertThat(collection).isInstanceOf(Collection.class); final Collection collection2 = ledger.getCollection("myPrivateCollection"); assertThat(collection2).isNotSameAs(collection); } @Test void getOrganizationCollection() { final Context ctx = mock(Context.class); final Ledger ledger = Ledger.getLedger(ctx); final Collection collection = ledger.getOrganizationCollection("org1"); assertThat(collection).isNotNull(); assertThat(collection).isInstanceOf(Collection.class); final Collection collection2 = ledger.getOrganizationCollection("org1"); assertThat(collection2).isNotSameAs(collection); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/metrics/MetricsTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Properties; import org.hyperledger.fabric.metrics.impl.DefaultProvider; import org.hyperledger.fabric.metrics.impl.NullProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; final class MetricsTest { private static final class TestProvider implements MetricsProvider { public TestProvider() {} @Override public void setTaskMetricsCollector(final TaskMetricsCollector taskService) {} @Override public void initialize(final Properties props) {} } @Nested @DisplayName("Metrics initialize") final class Initialize { @Test void metricsDisabled() { final MetricsProvider provider = Metrics.initialize(new Properties()); assertThat(provider).isExactlyInstanceOf(NullProvider.class); } @Test void metricsEnabledUnknownProvider() { final Properties props = new Properties(); props.put("CHAINCODE_METRICS_PROVIDER", "org.example.metrics.provider"); props.put("CHAINCODE_METRICS_ENABLED", "true"); assertThrows( RuntimeException.class, () -> { final MetricsProvider provider = Metrics.initialize(props); }, "Unable to start metrics"); } @Test void metricsNoProvider() { final Properties props = new Properties(); props.put("CHAINCODE_METRICS_ENABLED", "true"); final MetricsProvider provider = Metrics.initialize(props); assertTrue(provider instanceof DefaultProvider); } @Test void metricsValid() { final Properties props = new Properties(); props.put("CHAINCODE_METRICS_PROVIDER", MetricsTest.TestProvider.class.getName()); props.put("CHAINCODE_METRICS_ENABLED", "true"); final MetricsProvider provider = Metrics.initialize(props); assertThat(provider).isExactlyInstanceOf(MetricsTest.TestProvider.class); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/metrics/impl/DefaultProviderTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.metrics.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Properties; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import org.hyperledger.fabric.metrics.TaskMetricsCollector; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; final class DefaultProviderTest { @Test void allMethods() throws InterruptedException { DefaultProvider provider = new DefaultProvider(); provider.setTaskMetricsCollector(new TaskMetricsCollector() { @Override public int getPoolSize() { return 0; } @Override public int getMaximumPoolSize() { return 0; } @Override public int getLargestPoolSize() { return 0; } @Override public int getCurrentTaskCount() { return 0; } @Override public int getCurrentQueueCount() { return 0; } @Override public int getCorePoolSize() { return 0; } @Override public int getActiveCount() { // TODO Auto-generated method stub return 0; } }); Logger perfLogger = LogManager.getLogManager().getLogger("org.hyperledger.Performance"); Level original = perfLogger.getLevel(); try { perfLogger.setLevel(Level.ALL); Handler mockHandler = Mockito.mock(Handler.class); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(LogRecord.class); perfLogger.addHandler(mockHandler); provider.initialize(new Properties()); provider.logMetrics(); Thread.sleep(6000); Mockito.verify(mockHandler, Mockito.atLeast(1)).publish(argumentCaptor.capture()); LogRecord lr = argumentCaptor.getValue(); String msg = lr.getMessage(); assertThat(msg) .contains( "{ \"active_count\":0 , \"pool_size\":0 , \"core_pool_size\":0 , \"current_task_count\":0 , \"current_queue_depth\":0 "); } finally { perfLogger.setLevel(original); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ChaincodeBaseTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.nio.charset.Charset; import java.util.Properties; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ChaincodeBaseTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test void testNewSuccessResponseEmpty() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newSuccessResponse(); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.SUCCESS); assertThat(response.getMessage()).as("Response message").isNull(); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testNewSuccessResponseWithMessage() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newSuccessResponse("Simple message"); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.SUCCESS); assertThat(response.getMessage()).as("Response message").isEqualTo("Simple message"); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testNewSuccessResponseWithPayload() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newSuccessResponse("Simple payload".getBytes(Charset.defaultCharset())); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.SUCCESS); assertThat(response.getMessage()).as("Response message").isNull(); assertThat(response.getPayload()) .as("Response payload") .isEqualTo("Simple payload".getBytes(Charset.defaultCharset())); } @Test void testNewSuccessResponseWithMessageAndPayload() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newSuccessResponse("Simple message", "Simple payload".getBytes(Charset.defaultCharset())); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.SUCCESS); assertThat(response.getMessage()).as("Response message").isEqualTo("Simple message"); assertThat(response.getPayload()) .as("Response payload") .isEqualTo("Simple payload".getBytes(Charset.defaultCharset())); } @Test void testNewErrorResponseEmpty() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse(); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isNull(); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testNewErrorResponseWithMessage() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse("Simple message"); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isEqualTo("Simple message"); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testNewErrorResponseWithPayload() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse("Simple payload".getBytes(Charset.defaultCharset())); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isNull(); assertThat(response.getPayload()) .as("Response payload") .isEqualTo("Simple payload".getBytes(Charset.defaultCharset())); } @Test void testNewErrorResponseWithMessageAndPayload() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse("Simple message", "Simple payload".getBytes(Charset.defaultCharset())); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isEqualTo("Simple message"); assertThat(response.getPayload()) .as("Response payload") .isEqualTo("Simple payload".getBytes(Charset.defaultCharset())); } @Test void testNewErrorResponseWithException() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse(new Exception("Simple exception")); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isEqualTo("Unexpected error"); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testNewErrorResponseWithChaincodeException() { final org.hyperledger.fabric.shim.Chaincode.Response response = ResponseUtils.newErrorResponse(new ChaincodeException("Chaincode exception")); assertThat(response.getStatus()) .as("Response status") .isEqualTo(org.hyperledger.fabric.shim.Chaincode.Response.Status.INTERNAL_SERVER_ERROR); assertThat(response.getMessage()).as("Response message").isEqualTo("Chaincode exception"); assertThat(response.getPayload()).as("Response payload").isNull(); } @Test void testOptions() throws Exception { final ChaincodeBase cb = new EmptyChaincode(); assertThat(cb.getHost()).as("Host incorrect").isEqualTo(ChaincodeBase.DEFAULT_HOST); assertThat(cb.getPort()).as("Port incorrect").isEqualTo(ChaincodeBase.DEFAULT_PORT); assertThat(cb.isTlsEnabled()).as("TLS should not be enabled").isFalse(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "true"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "non_exist_path3"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "non_exist_path2"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "non_exist_path1"); cb.processEnvironmentOptions(); assertThat(cb.getId()).as("CCId incorrect").isEqualTo("mycc"); assertThat(cb.getHost()).as("Host incorrect").isEqualTo("localhost"); assertThat(cb.getPort()).as("Port incorrect").isEqualTo(7052); assertThat(cb.isTlsEnabled()).as("TLS should be enabled").isTrue(); assertThat(cb.getTlsClientRootCertPath()).as("Root certificate file").isEqualTo("non_exist_path1"); assertThat(cb.getTlsClientKeyPath()).as("Client key file").isEqualTo("non_exist_path2"); assertThat(cb.getTlsClientCertPath()).as("Client certificate file").isEqualTo("non_exist_path3"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost1"); cb.processEnvironmentOptions(); assertThat(cb.getHost()).as("Host incorrect").isEqualTo("localhost"); assertThat(cb.getPort()).as("Port incorrect").isEqualTo(7052); assertThatCode(cb::validateOptions).as("Wrong arguments").doesNotThrowAnyException(); cb.processCommandLineOptions(new String[] {"-i", "mycc1", "--peerAddress", "localhost.org:7053"}); assertThat(cb.getId()).as("CCId incorrect").isEqualTo("mycc1"); assertThat(cb.getHost()).as("Host incorrect").isEqualTo("localhost.org"); assertThat(cb.getPort()).as("Port incorrect").isEqualTo(7053); assertThatCode(cb::validateOptions).as("Wrong arguments").doesNotThrowAnyException(); cb.processCommandLineOptions(new String[] {"-i", "mycc1", "--peerAddress", "localhost1.org.7054"}); assertThat(cb.getHost()).as("Host incorrect").isEqualTo("localhost.org"); assertThat(cb.getPort()).as("Port incorrect").isEqualTo(7053); } @Test void testUnsetOptionId() { final ChaincodeBase cb = new EmptyChaincode(); assertThatThrownBy(cb::validateOptions) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("The chaincode id must be specified"); } @Test void testUnsetOptionClientCertPath() { final ChaincodeBase cb = new EmptyChaincode(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "true"); cb.processEnvironmentOptions(); assertThatThrownBy(cb::validateOptions) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Client key certificate chain"); } @Test void testUnsetOptionClientKeyPath() { final ChaincodeBase cb = new EmptyChaincode(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "true"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "non_exist_path3"); cb.processEnvironmentOptions(); assertThatThrownBy(cb::validateOptions) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Client key ("); } @Test @Disabled void testNewChannelBuilder() throws Exception { final ChaincodeBase cb = new EmptyChaincode(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "true"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "src/test/resources/ca.crt"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "src/test/resources/client.key.enc"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "src/test/resources/client.crt.enc"); cb.processEnvironmentOptions(); cb.validateOptions(); assertThat(cb.newChannelBuilder()).isInstanceOf(ManagedChannelBuilder.class); } @Test void testInitializeLogging() { final ChaincodeBase cb = new EmptyChaincode(); cb.processEnvironmentOptions(); cb.initializeLogging(); assertThat(Logger.getLogger("org.hyperledger.fabric.shim").getLevel()) .as("Wrong log level for org.hyperledger.fabric.shim") .isEqualTo(Level.INFO); assertThat(Logger.getLogger(cb.getClass().getPackage().getName()).getLevel()) .as("Wrong log level for " + cb.getClass().getPackage().getName()) .isEqualTo(Level.INFO); setLogLevelForChaincode(environmentVariables, cb, "WRONG", "WRONG"); assertThat(Logger.getLogger("org.hyperledger.fabric.shim").getLevel()) .as("Wrong log level for org.hyperledger.fabric.shim") .isEqualTo(Level.INFO); assertThat(Logger.getLogger(cb.getClass().getPackage().getName()).getLevel()) .as("Wrong log level for " + cb.getClass().getPackage().getName()) .isEqualTo(Level.INFO); setLogLevelForChaincode(environmentVariables, cb, "DEBUG", "NOTICE"); assertThat(Logger.getLogger("org.hyperledger.fabric.shim").getLevel()) .as("Wrong log level for org.hyperledger.fabric.shim") .isEqualTo(Level.FINEST); assertThat(Logger.getLogger(cb.getClass().getPackage().getName()).getLevel()) .as("Wrong log level for " + cb.getClass().getPackage().getName()) .isEqualTo(Level.CONFIG); setLogLevelForChaincode(environmentVariables, cb, "INFO", "WARNING"); assertThat(Logger.getLogger("org.hyperledger.fabric.shim").getLevel()) .as("Wrong log level for org.hyperledger.fabric.shim") .isEqualTo(Level.INFO); assertThat(Logger.getLogger(cb.getClass().getPackage().getName()).getLevel()) .as("Wrong log level for " + cb.getClass().getPackage().getName()) .isEqualTo(Level.WARNING); setLogLevelForChaincode(environmentVariables, cb, "CRITICAL", "ERROR"); assertThat(Logger.getLogger("org.hyperledger.fabric.shim").getLevel()) .as("Wrong log level for org.hyperledger.fabric.shim") .isEqualTo(Level.SEVERE); assertThat(Logger.getLogger(cb.getClass().getPackage().getName()).getLevel()) .as("Wrong log level for " + cb.getClass().getPackage().getName()) .isEqualTo(Level.SEVERE); } @Test void testStartFailsWithoutValidOptions() { final String[] args = new String[0]; final ChaincodeBase cb = new EmptyChaincode(); Handler mockHandler = Mockito.mock(Handler.class); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(LogRecord.class); Logger logger = Logger.getLogger("org.hyperledger.fabric.shim.ChaincodeBase"); logger.addHandler(mockHandler); cb.start(args); Mockito.verify(mockHandler, Mockito.atLeast(1)).publish(argumentCaptor.capture()); LogRecord lr = argumentCaptor.getValue(); String msg = lr.getMessage(); assertThat(msg).doesNotContain("java.lang.NullPointerException"); assertThat(msg) .contains( "The chaincode id must be specified using either the -i or --i command line options or the CORE_CHAINCODE_ID_NAME environment variable."); } private static void setLogLevelForChaincode( final EnvironmentVariables environmentVariables, final ChaincodeBase cb, final String shimLevel, final String chaincodeLevel) { environmentVariables.set(ChaincodeBase.CORE_CHAINCODE_LOGGING_SHIM, shimLevel); environmentVariables.set(ChaincodeBase.CORE_CHAINCODE_LOGGING_LEVEL, chaincodeLevel); cb.processEnvironmentOptions(); cb.initializeLogging(); } @Test void connectChaincodeBase() throws IOException { final ChaincodeBase cb = new EmptyChaincode(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "false"); cb.processEnvironmentOptions(); cb.validateOptions(); final Properties props = cb.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); cb.connectToPeer(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) {} @Override public void onCompleted() {} }); environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); environmentVariables.remove("CORE_PEER_ADDRESS"); environmentVariables.remove("CORE_PEER_TLS_ENABLED"); } @Test void connectChaincodeBaseNull() { Assertions.assertThrows(IllegalArgumentException.class, () -> { final ChaincodeBase cb = new EmptyChaincode(); cb.connectToPeer(null); }); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ChaincodeServerImplTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.io.IOException; import java.net.URISyntaxException; import org.hyperledger.fabric.contract.ContractRouter; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ChaincodeServerImplTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @BeforeEach void setEnv() { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CHAINCODE_SERVER_ADDRESS", "0.0.0.0:9999"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "false"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "src/test/resources/ca.crt"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "src/test/resources/client.key.enc"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "src/test/resources/client.crt.enc"); } @AfterEach void clearEnv() { environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); environmentVariables.remove("CORE_PEER_ADDRESS"); environmentVariables.remove("CORE_PEER_TLS_ENABLED"); environmentVariables.remove("CORE_PEER_TLS_ROOTCERT_FILE"); environmentVariables.remove("CORE_TLS_CLIENT_KEY_PATH"); environmentVariables.remove("CORE_TLS_CLIENT_CERT_PATH"); environmentVariables.remove("CHAINCODE_SERVER_ADDRESS"); } @Test void init() { try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); } catch (Exception e) { e.printStackTrace(); } } @Test void initEnvNotSet() { clearEnv(); try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); } catch (Exception e) { e.printStackTrace(); } } @Test void startAndStop() { try { final ChaincodeBase chaincodeBase = new ContractRouter(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); new Thread(() -> { try { chaincodeServer.start(); } catch (Exception e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } chaincodeServer.stop(); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ChaincodeStubTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.SignedProposal; import org.hyperledger.fabric.shim.Chaincode.Response; import org.hyperledger.fabric.shim.ledger.CompositeKey; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata; import org.junit.jupiter.api.Test; final class ChaincodeStubTest { @SuppressWarnings("PMD.ReturnEmptyCollectionRatherThanNull") private static final class FakeStub implements ChaincodeStub { @Override public List getArgs() { // TODO Auto-generated method stub return null; } @Override public List getStringArgs() { // TODO Auto-generated method stub return null; } @Override public String getFunction() { // TODO Auto-generated method stub return null; } @Override public List getParameters() { // TODO Auto-generated method stub return null; } @Override public String getTxId() { // TODO Auto-generated method stub return null; } @Override public String getChannelId() { // TODO Auto-generated method stub return null; } @Override public Response invokeChaincode(final String chaincodeName, final List args, final String channel) { // TODO Auto-generated method stub return null; } @Override public byte[] getState(final String key) { return key.getBytes(); } @Override public byte[] getStateValidationParameter(final String key) { // TODO Auto-generated method stub return null; } @Override public void putState(final String key, final byte[] value) { // TODO Auto-generated method stub } @Override public void setStateValidationParameter(final String key, final byte[] value) { // TODO Auto-generated method stub } @Override public void delState(final String key) { // TODO Auto-generated method stub } @Override public QueryResultsIterator getStateByRange(final String startKey, final String endKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIteratorWithMetadata getStateByRangeWithPagination( final String startKey, final String endKey, final int pageSize, final String bookmark) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey(final String compositeKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey( final String objectType, final String... attributes) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getStateByPartialCompositeKey(final CompositeKey compositeKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIteratorWithMetadata getStateByPartialCompositeKeyWithPagination( final CompositeKey compositeKey, final int pageSize, final String bookmark) { // TODO Auto-generated method stub return null; } @Override public CompositeKey createCompositeKey(final String objectType, final String... attributes) { // TODO Auto-generated method stub return null; } @Override public CompositeKey splitCompositeKey(final String compositeKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getQueryResult(final String query) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIteratorWithMetadata getQueryResultWithPagination( final String query, final int pageSize, final String bookmark) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getHistoryForKey(final String key) { // TODO Auto-generated method stub return null; } @Override public byte[] getPrivateData(final String collection, final String key) { return collection.getBytes(); } @Override public byte[] getPrivateDataHash(final String collection, final String key) { // TODO Auto-generated method stub return null; } @Override public byte[] getPrivateDataValidationParameter(final String collection, final String key) { // TODO Auto-generated method stub return null; } @Override public void putPrivateData(final String collection, final String key, final byte[] value) { // TODO Auto-generated method stub } @Override public void setPrivateDataValidationParameter(final String collection, final String key, final byte[] value) { // TODO Auto-generated method stub } @Override public void delPrivateData(final String collection, final String key) { // TODO Auto-generated method stub } @Override public void purgePrivateData(final String collection, final String key) { // TODO Auto-generated method stub } @Override public QueryResultsIterator getPrivateDataByRange( final String collection, final String startKey, final String endKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String compositeKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final CompositeKey compositeKey) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getPrivateDataByPartialCompositeKey( final String collection, final String objectType, final String... attributes) { // TODO Auto-generated method stub return null; } @Override public QueryResultsIterator getPrivateDataQueryResult(final String collection, final String query) { // TODO Auto-generated method stub return null; } @Override public void setEvent(final String name, final byte[] payload) { // TODO Auto-generated method stub } @Override public ChaincodeEvent getEvent() { // TODO Auto-generated method stub return null; } @Override public SignedProposal getSignedProposal() { // TODO Auto-generated method stub return null; } @Override public Instant getTxTimestamp() { // TODO Auto-generated method stub return null; } @Override public byte[] getCreator() { // TODO Auto-generated method stub return null; } @Override public Map getTransient() { // TODO Auto-generated method stub return null; } @Override public byte[] getBinding() { // TODO Auto-generated method stub return null; } @Override public String getMspId() { // TODO Auto-generated method stub return null; } } @Test void testDefaultMethods() { final ChaincodeStub stub = new FakeStub(); final String chaincodeName = "ACME_SHIPPING"; stub.invokeChaincode(chaincodeName, new ArrayList<>()); stub.invokeChaincodeWithStringArgs(chaincodeName, new ArrayList<>(), "channel"); stub.invokeChaincodeWithStringArgs(chaincodeName, new ArrayList<>()); stub.invokeChaincodeWithStringArgs(chaincodeName, "anvil", "tnt"); stub.getStringState("key"); stub.putPrivateData("collection", "key", "value"); stub.getPrivateDataUTF8("collection", "key"); stub.putStringState("key", "value"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ChaincodeTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; final class ChaincodeTest { @Test void testResponse() { final Chaincode.Response resp = new Chaincode.Response( Chaincode.Response.Status.SUCCESS, "No message", "no payload".getBytes(StandardCharsets.UTF_8)); assertThat(Chaincode.Response.Status.SUCCESS).as("Incorrect status").isEqualTo(resp.getStatus()); assertThat("No message").as("Incorrect message").isEqualTo(resp.getMessage()); assertThat("no payload").as("Incorrect payload").isEqualTo(resp.getStringPayload()); } @Test void testResponseWithCode() { Chaincode.Response resp = new Chaincode.Response(200, "No message", "no payload".getBytes(StandardCharsets.UTF_8)); assertThat(Chaincode.Response.Status.SUCCESS).as("Incorrect status").isEqualTo(resp.getStatus()); assertThat(200).as("Incorrect status").isEqualTo(resp.getStatusCode()); assertThat("No message").as("Incorrect message").isEqualTo(resp.getMessage()); assertThat("no payload").as("Incorrect payload").isEqualTo(resp.getStringPayload()); resp = new Chaincode.Response(404, "No message", "no payload".getBytes(StandardCharsets.UTF_8)); assertThat(404).as("Incorrect status").isEqualTo(resp.getStatusCode()); assertThat("No message").as("Incorrect message").isEqualTo(resp.getMessage()); assertThat("no payload").as("Incorrect payload").isEqualTo(resp.getStringPayload()); resp = new Chaincode.Response( Chaincode.Response.Status.ERROR_THRESHOLD, "No message", "no payload".getBytes(StandardCharsets.UTF_8)); assertThat(Chaincode.Response.Status.ERROR_THRESHOLD) .as("Incorrect status") .isEqualTo(resp.getStatus()); assertThat(400).as("Incorrect status").isEqualTo(resp.getStatusCode()); assertThat("No message").as("Incorrect message").isEqualTo(resp.getMessage()); assertThat("no payload").as("Incorrect payload").isEqualTo(resp.getStringPayload()); } @Test void testStatus() { assertThat(Chaincode.Response.Status.SUCCESS) .as("Wrong status") .isEqualTo(Chaincode.Response.Status.forCode(200)); assertThat(Chaincode.Response.Status.ERROR_THRESHOLD) .as("Wrong status") .isEqualTo(Chaincode.Response.Status.forCode(400)); assertThat(Chaincode.Response.Status.INTERNAL_SERVER_ERROR) .as("Wrong status") .isEqualTo(Chaincode.Response.Status.forCode(500)); assertThatThrownBy(() -> Chaincode.Response.Status.forCode(501)).isInstanceOf(IllegalArgumentException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ChatChaincodeWithPeerTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.INIT; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.INVOKE_CHAINCODE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.protobuf.ByteString; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.List; import java.util.Properties; import java.util.stream.Stream; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeInput; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeSpec; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.shim.utils.MessageUtil; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ChatChaincodeWithPeerTest { private static final String TEST_CHANNEL = "testChannel"; @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @BeforeEach void setEnv() { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "false"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "src/test/resources/ca.crt"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "src/test/resources/client.key.enc"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "src/test/resources/client.crt.enc"); } @AfterEach void clearEnv() { environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); environmentVariables.remove("CORE_PEER_ADDRESS"); environmentVariables.remove("CORE_PEER_TLS_ENABLED"); environmentVariables.remove("CORE_PEER_TLS_ROOTCERT_FILE"); environmentVariables.remove("CORE_TLS_CLIENT_KEY_PATH"); environmentVariables.remove("CORE_TLS_CLIENT_CERT_PATH"); } @Test void initNull() throws IOException { Assertions.assertThrows( IOException.class, () -> { ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(null); }, "chaincodeBase can't be null"); } @Test void init() { ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = null; try { chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); } catch (IOException e) { e.printStackTrace(); } assertNotNull(chatChaincodeWithPeer); } @Test void initEmptyId() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", ""); Assertions.assertThrows( IllegalArgumentException.class, () -> { ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); }, "chaincode id not set, set env 'CORE_CHAINCODE_ID_NAME', for example 'CORE_CHAINCODE_ID_NAME=mycc'"); } @Test void connectEnvNotSet() throws IOException { clearEnv(); Assertions.assertThrows(IllegalArgumentException.class, () -> { ChaincodeBase chaincodeBase = new EmptyChaincode(); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); }); } @Test void connectNull() throws IOException { ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); assertNull(chatChaincodeWithPeer.connect(null)); } @Test @SuppressWarnings("PMD.SystemPrintln") void connectAndReceiveRegister() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Traces.initialize(props); Metrics.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); final StreamObserver connect = chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) { assertEquals(ChaincodeMessage.Type.REGISTER, value.getType()); assertEquals("\u0012\u0004mycc", value.getPayload().toStringUtf8()); } @Override public void onError(final Throwable t) { assertNull(t); } @Override public void onCompleted() {} }); assertNotNull(connect); final ByteString payload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, TEST_CHANNEL, "0", payload, null); connect.onNext(initMsg); { final List args = Stream.of("invoke", "a", "1").map(x -> x.getBytes(UTF_8)).collect(toList()); final ByteString invocationSpecPayload = ChaincodeSpec.newBuilder() .setChaincodeId(ChaincodeID.newBuilder() .setName(chaincodeBase.getId()) .build()) .setInput(ChaincodeInput.newBuilder() .addAllArgs(args.stream().map(ByteString::copyFrom).collect(toList())) .build()) .build() .toByteString(); final ChaincodeMessage invokeChaincodeMessage = ChaincodeMessage.newBuilder() .setType(INVOKE_CHAINCODE) .setChannelId(TEST_CHANNEL) .setTxid("1") .setPayload(invocationSpecPayload) .build(); connect.onNext(invokeChaincodeMessage); System.out.println(invokeChaincodeMessage.getPayload().toStringUtf8()); } { final List args = Stream.of("invoke", "a", "1").map(x -> x.getBytes(UTF_8)).collect(toList()); final ByteString invocationSpecPayload = ChaincodeSpec.newBuilder() .setChaincodeId(ChaincodeID.newBuilder() .setName(chaincodeBase.getId()) .build()) .setInput(ChaincodeInput.newBuilder() .addAllArgs(args.stream().map(ByteString::copyFrom).collect(toList())) .build()) .build() .toByteString(); final ChaincodeMessage invokeChaincodeMessage = ChaincodeMessage.newBuilder() .setType(INVOKE_CHAINCODE) .setChannelId(TEST_CHANNEL) .setTxid("2") .setPayload(invocationSpecPayload) .build(); connect.onNext(invokeChaincodeMessage); System.out.println(invokeChaincodeMessage.getPayload().toStringUtf8()); } } @Test void connectAndReceiveRegisterComplete() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Traces.initialize(props); Metrics.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); final StreamObserver connect = chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) { assertEquals(ChaincodeMessage.Type.REGISTER, value.getType()); assertEquals("\u0012\u0004mycc", value.getPayload().toStringUtf8()); } @Override public void onError(final Throwable t) { assertNull(t); } @Override public void onCompleted() {} }); connect.onCompleted(); } @Test void connectAndReceiveRegisterException() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Traces.initialize(props); Metrics.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); final StreamObserver connect = chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) {} @Override public void onCompleted() {} }); connect.onError(new Exception("some_error")); } @Test @SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes") void connectOnCompletedException() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(chaincodeBase); Assertions.assertDoesNotThrow( () -> { chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) {} @Override public void onCompleted() { throw new RuntimeException("some_error"); } }); }, "some_error"); } @Test void testMockChaincodeBase() throws IOException { final ChaincodeBase mockChaincodeBase = mock(ChaincodeBase.class); when(mockChaincodeBase.getId()).thenReturn("ccid_1234"); when(mockChaincodeBase.connectToPeer(any())).thenReturn(null); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(mockChaincodeBase); assertNotNull(chatChaincodeWithPeer); assertNull(chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) {} @Override public void onCompleted() {} })); } @Test void testMockChaincodeBaseThrowIOException() throws IOException { final ChaincodeBase mockChaincodeBase = mock(ChaincodeBase.class); when(mockChaincodeBase.getId()).thenReturn("ccid_1234"); final IOException expectedException = new IOException("some_error"); when(mockChaincodeBase.connectToPeer(any())).thenThrow(expectedException); doNothing().when(mockChaincodeBase).validateOptions(); ChatChaincodeWithPeer chatChaincodeWithPeer = new ChatChaincodeWithPeer(mockChaincodeBase); assertNotNull(chatChaincodeWithPeer); assertNull(chatChaincodeWithPeer.connect(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) { assertEquals(expectedException, t); } @Override public void onCompleted() {} })); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/NettyGrpcServerTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim; import java.io.IOException; import java.net.URISyntaxException; import java.util.Properties; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class NettyGrpcServerTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @BeforeEach void setEnv() { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "false"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "src/test/resources/ca.crt"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "src/test/resources/client.key.enc"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "src/test/resources/client.crt.enc"); environmentVariables.set("CHAINCODE_SERVER_ADDRESS", "0.0.0.0:9999"); } @AfterEach void clearEnv() { environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); environmentVariables.remove("CORE_PEER_ADDRESS"); environmentVariables.remove("CORE_PEER_TLS_ENABLED"); environmentVariables.remove("CORE_PEER_TLS_ROOTCERT_FILE"); environmentVariables.remove("CORE_TLS_CLIENT_KEY_PATH"); environmentVariables.remove("CORE_TLS_CLIENT_CERT_PATH"); environmentVariables.remove("CHAINCODE_SERVER_ADDRESS"); } @Test void initNoTls() { try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } @Test void validationNoChaincodeServerPropertiesg() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, null); }, "ChaincodeServerProperties must be specified"); } @Test void validationPortChaincodeServer() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setServerAddress(null); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getServerAddress() must be set"); } @Test void validationKeepAliveTimeMinutes() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setKeepAliveTimeMinutes(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getKeepAliveTimeMinutes() must be more then 0"); } @Test void validationKeepAliveTimeoutSeconds() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setKeepAliveTimeoutSeconds(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getKeepAliveTimeoutSeconds() must be more then 0"); } @Test void validationPermitKeepAliveTimeMinutes() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setPermitKeepAliveTimeMinutes(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getPermitKeepAliveTimeMinutes() must be more then 0"); } @Test void validationMaxConnectionAgeSeconds() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setMaxConnectionAgeSeconds(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getMaxConnectionAgeSeconds() must be more then 0"); } @Test void validationMaxInboundMetadataSize() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setMaxInboundMetadataSize(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getMaxInboundMetadataSize() must be more then 0"); } @Test void validationMaxInboundMessageSize() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setMaxInboundMessageSize(-1); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getMaxInboundMessageSize() must be more then 0"); } @Test void validationTlsEnabledButKeyNotSet() { Assertions.assertThrows( IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); final ChaincodeServerProperties chaincodeServerProperties = new ChaincodeServerProperties(); chaincodeServerProperties.setTlsEnabled(true); chaincodeServerProperties.setKeyFile(null); chaincodeServerProperties.setKeyCertChainFile(null); chaincodeServerProperties.setKeyPassword(null); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); }, "ChaincodeServerProperties.getMaxInboundMessageSize() must be more then 0"); } @Test void initNull() { Assertions.assertThrows( IllegalArgumentException.class, () -> { ChaincodeServer chaincodeServer = new NettyChaincodeServer(null, new ChaincodeServerProperties()); }, "chaincode must be specified"); } @Test void initNullEnvNotSet() { clearEnv(); Assertions.assertThrows(IllegalArgumentException.class, () -> { ChaincodeServer chaincodeServer = new NettyChaincodeServer(null, new ChaincodeServerProperties()); }); } @Test void initEnvNotSet() { clearEnv(); Assertions.assertThrows(IllegalArgumentException.class, () -> { final ChaincodeBase chaincodeBase = new EmptyChaincode(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, new ChaincodeServerProperties()); }); } @Test void initEnvSetPortChaincodeServerAndCoreChaincodeIdName() throws IOException, URISyntaxException { clearEnv(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CHAINCODE_SERVER_ADDRESS", "0.0.0.0:9999"); ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); } @Test void startAndStopSetCoreChaincodeIdName() { clearEnv(); environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CHAINCODE_SERVER_ADDRESS", "0.0.0.0:9999"); try { ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); new Thread(() -> { try { chaincodeServer.start(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } chaincodeServer.stop(); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } @Test void startAndStop() { try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeBase.getChaincodeServerConfig()); new Thread(() -> { try { chaincodeServer.start(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } chaincodeServer.stop(); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } @Test void startAndStopTlsPassword() { try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); final ChaincodeServerProperties chaincodeServerProperties = chaincodeBase.getChaincodeServerConfig(); chaincodeServerProperties.setTlsEnabled(true); chaincodeServerProperties.setKeyFile("src/test/resources/client.key.password-protected"); chaincodeServerProperties.setKeyCertChainFile("src/test/resources/client.crt"); chaincodeServerProperties.setKeyPassword("test"); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); new Thread(() -> { try { chaincodeServer.start(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } chaincodeServer.stop(); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } @Test void startAndStopTlsWithoutPassword() { try { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); final ChaincodeServerProperties chaincodeServerProperties = chaincodeBase.getChaincodeServerConfig(); chaincodeServerProperties.setTlsEnabled(true); chaincodeServerProperties.setKeyFile("src/test/resources/client.key"); chaincodeServerProperties.setKeyCertChainFile("src/test/resources/client.crt"); ChaincodeServer chaincodeServer = new NettyChaincodeServer(chaincodeBase, chaincodeServerProperties); new Thread(() -> { try { chaincodeServer.start(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }) .start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } chaincodeServer.stop(); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/chaincode/EmptyChaincode.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.chaincode; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ResponseUtils; public final class EmptyChaincode extends ChaincodeBase { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ext/sbe/StateBasedEndorsementTest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.Test; final class StateBasedEndorsementTest { @Test void testRoleType() { assertThat(StateBasedEndorsement.RoleType.forVal("MEMBER")) .isEqualTo(StateBasedEndorsement.RoleType.RoleTypeMember); assertThat(StateBasedEndorsement.RoleType.forVal("PEER")) .isEqualTo(StateBasedEndorsement.RoleType.RoleTypePeer); assertThatThrownBy(() -> StateBasedEndorsement.RoleType.forVal("NONEXIST")) .isInstanceOf(IllegalArgumentException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementFactoryTest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; final class StateBasedEndorsementFactoryTest { @Test void getInstance() { assertNotNull(StateBasedEndorsementFactory.getInstance()); assertInstanceOf(StateBasedEndorsementFactory.class, StateBasedEndorsementFactory.getInstance()); } @Test void newStateBasedEndorsement() { assertNotNull(StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(new byte[] {})); assertThatThrownBy(() -> StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(new byte[] {0})) .isInstanceOf(IllegalArgumentException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ext/sbe/impl/StateBasedEndorsementImplTest.java ================================================ /* * Copyright 2019 IBM DTCC All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ext.sbe.impl; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import org.hyperledger.fabric.protos.common.MSPRole.MSPRoleType; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement.RoleType; import org.junit.jupiter.api.Test; final class StateBasedEndorsementImplTest { @Test void addOrgs() { // add an org final StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(null); ep.addOrgs(RoleType.RoleTypePeer, "Org1"); final byte[] epBytes = ep.policy(); assertThat(epBytes, is(not(nullValue()))); assertTrue(epBytes.length > 0); final byte[] expectedEPBytes = StateBasedEndorsementUtils.signedByFabricEntity("Org1", MSPRoleType.PEER) .toByteString() .toByteArray(); assertArrayEquals(expectedEPBytes, epBytes); } @Test void delOrgs() { final byte[] initEPBytes = StateBasedEndorsementUtils.signedByFabricEntity("Org1", MSPRoleType.PEER) .toByteString() .toByteArray(); final StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(initEPBytes); final List listOrgs = ep.listOrgs(); assertThat(listOrgs, is(not(nullValue()))); assertThat(listOrgs, contains("Org1")); assertThat(listOrgs, hasSize(1)); ep.addOrgs(RoleType.RoleTypeMember, "Org2"); ep.delOrgs("Org1"); final byte[] epBytes = ep.policy(); assertThat(epBytes, is(not(nullValue()))); assertTrue(epBytes.length > 0); final byte[] expectedEPBytes = StateBasedEndorsementUtils.signedByFabricEntity("Org2", MSPRoleType.MEMBER) .toByteString() .toByteArray(); assertArrayEquals(expectedEPBytes, epBytes); } @Test void listOrgs() { final byte[] initEPBytes = StateBasedEndorsementUtils.signedByFabricEntity("Org1", MSPRoleType.PEER) .toByteString() .toByteArray(); final StateBasedEndorsement ep = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(initEPBytes); final List listOrgs = ep.listOrgs(); assertThat(listOrgs, is(not(nullValue()))); assertThat(listOrgs, hasSize(1)); assertThat(listOrgs, contains("Org1")); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/fvt/ChaincodeFVTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.fvt; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.COMPLETED; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.INIT; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.READY; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.REGISTER; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.RESPONSE; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.TRANSACTION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import org.hyperledger.fabric.protos.peer.ChaincodeInput; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.Response; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ResponseUtils; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; import org.hyperledger.fabric.shim.ext.sbe.impl.StateBasedEndorsementFactory; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.mock.peer.ChaincodeMockPeer; import org.hyperledger.fabric.shim.mock.peer.CompleteStep; import org.hyperledger.fabric.shim.mock.peer.DelValueStep; import org.hyperledger.fabric.shim.mock.peer.ErrorResponseStep; import org.hyperledger.fabric.shim.mock.peer.GetHistoryForKeyStep; import org.hyperledger.fabric.shim.mock.peer.GetQueryResultStep; import org.hyperledger.fabric.shim.mock.peer.GetStateByRangeStep; import org.hyperledger.fabric.shim.mock.peer.GetStateMetadata; import org.hyperledger.fabric.shim.mock.peer.GetValueStep; import org.hyperledger.fabric.shim.mock.peer.InvokeChaincodeStep; import org.hyperledger.fabric.shim.mock.peer.PutStateMetadata; import org.hyperledger.fabric.shim.mock.peer.PutValueStep; import org.hyperledger.fabric.shim.mock.peer.QueryCloseStep; import org.hyperledger.fabric.shim.mock.peer.QueryNextStep; import org.hyperledger.fabric.shim.mock.peer.RegisterStep; import org.hyperledger.fabric.shim.mock.peer.ScenarioStep; import org.hyperledger.fabric.shim.utils.MessageUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ChaincodeFVTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); private ChaincodeMockPeer server; @AfterEach void afterTest() throws Exception { if (server != null) { server.stop(); server = null; } } @Test void testRegister() throws Exception { final ChaincodeBase cb = new EmptyChaincode(); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(READY)); assertThat(server.getLastMessageRcvd().getType(), is(REGISTER)); } @Test void testRegisterAndEmptyInit() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } }; final ByteString payload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", payload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(INIT)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); } @Test void testInitAndInvoke() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { assertThat(stub.getFunction(), is("init")); assertThat(stub.getArgs().size(), is(3)); stub.putState("a", ByteString.copyFromUtf8("100").toByteArray()); return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { assertThat(stub.getFunction(), is("invoke")); assertThat(stub.getArgs().size(), is(3)); final String aKey = stub.getStringArgs().get(1); assertThat(aKey, is("a")); stub.getStringState(aKey); stub.putState(aKey, ByteString.copyFromUtf8("120").toByteArray()); stub.delState("delKey"); return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("init")) .addArgs(ByteString.copyFromUtf8("a")) .addArgs(ByteString.copyFromUtf8("100")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new PutValueStep("100")); scenario.add(new CompleteStep()); scenario.add(new GetValueStep("100")); scenario.add(new PutValueStep("120")); scenario.add(new DelValueStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 3, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(INIT)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response1")); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .addArgs(ByteString.copyFromUtf8("a")) .addArgs(ByteString.copyFromUtf8("10")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 7, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(TRANSACTION)); // assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); // assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), // is("OK response2")); } @Test void testStateValidationParameter() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { final String aKey = stub.getStringArgs().get(1); final byte[] epBytes = stub.getStateValidationParameter(aKey); final StateBasedEndorsement stateBasedEndorsement = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(epBytes); assertThat(stateBasedEndorsement.listOrgs().size(), is(2)); stub.setStateValidationParameter(aKey, stateBasedEndorsement.policy()); return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("init")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final StateBasedEndorsement sbe = StateBasedEndorsementFactory.getInstance().newStateBasedEndorsement(null); sbe.addOrgs(StateBasedEndorsement.RoleType.RoleTypePeer, "Org1"); sbe.addOrgs(StateBasedEndorsement.RoleType.RoleTypeMember, "Org2"); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); scenario.add(new GetStateMetadata(sbe)); scenario.add(new PutStateMetadata(sbe)); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(INIT)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response1")); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .addArgs(ByteString.copyFromUtf8("a")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 5, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); } @Test void testInvokeRangeQ() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { assertThat(stub.getFunction(), is("invoke")); assertThat(stub.getArgs().size(), is(3)); final String aKey = stub.getStringArgs().get(1); final String bKey = stub.getStringArgs().get(2); final QueryResultsIterator stateByRange = stub.getStateByRange(aKey, bKey); final Iterator iter = stateByRange.iterator(); while (iter.hasNext()) { iter.next(); } try { stateByRange.close(); } catch (final Exception e) { fail("No exception expected"); } return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .addArgs(ByteString.copyFromUtf8("a")) .addArgs(ByteString.copyFromUtf8("b")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); scenario.add(new GetStateByRangeStep(false, "a", "b")); scenario.add(new QueryCloseStep()); scenario.add(new CompleteStep()); scenario.add(new GetStateByRangeStep(true, "a", "b")); scenario.add(new QueryNextStep(false, "c")); scenario.add(new QueryCloseStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 5, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 9, 30000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); } @Test void testGetQueryResult() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { final String query = stub.getStringArgs().get(1); final QueryResultsIterator queryResult = stub.getQueryResult(query); final Iterator iter = queryResult.iterator(); while (iter.hasNext()) { iter.next(); } try { queryResult.close(); } catch (final Exception e) { fail("No exception expected"); } return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .addArgs(ByteString.copyFromUtf8("query")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); scenario.add(new GetQueryResultStep(false, "a", "b")); scenario.add(new QueryCloseStep()); scenario.add(new CompleteStep()); scenario.add(new GetQueryResultStep(true, "a", "b")); scenario.add(new QueryNextStep(false, "c")); scenario.add(new QueryCloseStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 5, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 9, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); } @Test void testGetHistoryForKey() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { final String key = stub.getStringArgs().get(1); final QueryResultsIterator queryResult = stub.getHistoryForKey(key); final Iterator iter = queryResult.iterator(); while (iter.hasNext()) { iter.next(); } try { queryResult.close(); } catch (final Exception e) { fail("No exception expected"); } return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .addArgs(ByteString.copyFromUtf8("key1")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); scenario.add(new GetHistoryForKeyStep(false, "1", "2")); scenario.add(new QueryCloseStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 5, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); assertThat(Response.parseFrom(server.getLastMessageRcvd().getPayload()).getMessage(), is("OK response2")); } @Test void testInvokeChaincode() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response1"); } @Override public Response invoke(final ChaincodeStub stub) { stub.invokeChaincode("anotherChaincode", Collections.emptyList()); return ResponseUtils.newSuccessResponse("OK response2"); } }; final ByteString initPayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", initPayload, null); final ByteString invokePayload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("invoke")) .build() .toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); scenario.add(new InvokeChaincodeStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 4, 10000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(RESPONSE)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); } @Test void testErrorInitInvoke() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newErrorResponse("Wrong response1".getBytes()); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newErrorResponse("Wrong response2"); } }; final ByteString payload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", payload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new ErrorResponseStep()); scenario.add(new ErrorResponseStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 2, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(INIT)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); String resp1 = Response.parseFrom(server.getLastMessageRcvd().getPayload()) .getPayload() .toStringUtf8(); assertThat(resp1, is("Wrong response1")); final ByteString invokePayload = ChaincodeInput.newBuilder().build().toByteString(); final ChaincodeMessage invokeMsg = MessageUtil.newEventMessage(TRANSACTION, "testChannel", "0", invokePayload, null); server.send(invokeMsg); ChaincodeMockPeer.checkScenarioStepEnded(server, 3, 5000, TimeUnit.MILLISECONDS); assertThat(server.getLastMessageSend().getType(), is(TRANSACTION)); assertThat(server.getLastMessageRcvd().getType(), is(COMPLETED)); String resp2 = Response.parseFrom(server.getLastMessageRcvd().getPayload()) .getMessage() .toString(); assertThat(resp2, is("Wrong response2")); } @Test void testStreamShutdown() throws Exception { final ChaincodeBase cb = new ChaincodeBase() { @Override public Response init(final ChaincodeStub stub) { try { Thread.sleep(10); } catch (final InterruptedException ignored) { } return ResponseUtils.newSuccessResponse(); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } }; final ByteString payload = ChaincodeInput.newBuilder() .addArgs(ByteString.copyFromUtf8("")) .build() .toByteString(); final ChaincodeMessage initMsg = MessageUtil.newEventMessage(INIT, "testChannel", "0", payload, null); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); ChaincodeMockPeer.checkScenarioStepEnded(server, 1, 5000, TimeUnit.MILLISECONDS); server.send(initMsg); server.stop(); server = null; } @Test void testChaincodeLogLevel() throws Exception { final ChaincodeBase cb = new EmptyChaincode(); final List scenario = new ArrayList<>(); scenario.add(new RegisterStep()); scenario.add(new CompleteStep()); setLogLevel("DEBUG"); server = ChaincodeMockPeer.startServer(scenario); cb.start(new String[] {"-a", "127.0.0.1:7052", "-i", "testId"}); assertEquals( Level.FINEST, Logger.getLogger(cb.getClass().getPackage().getName()).getLevel(), "Wrong debug level for " + cb.getClass().getPackage().getName()); } private void setLogLevel(final String logLevel) { environmentVariables.set("CORE_CHAINCODE_LOGGING_SHIM", logLevel); environmentVariables.set("CORE_CHAINCODE_LOGGING_LEVEL", logLevel); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/ChaincodeMessageFactoryTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import com.google.protobuf.ByteString; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type; import org.hyperledger.fabric.shim.Chaincode.Response; import org.hyperledger.fabric.shim.ResponseUtils; import org.junit.jupiter.api.Test; final class ChaincodeMessageFactoryTest { private final String txId = "txid"; private final String key = "key"; private final String channelId = "channelid"; private final String collection = "collectionId"; private final ByteString value = ByteString.copyFromUtf8("Hello"); private final String metakey = "metakey"; private final Throwable throwable = new Throwable(); private final String message = "message"; private ChaincodeEvent event; private final Response response = ResponseUtils.newSuccessResponse(); private final ByteString payload = ByteString.copyFromUtf8("Hello"); private final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("test").build(); private final Type type = ChaincodeMessage.Type.COMPLETED; @Test void testNewGetPrivateDataHashEventMessage() { ChaincodeMessageFactory.newGetPrivateDataHashEventMessage(channelId, txId, collection, key); } @Test void testNewGetStateEventMessage() { ChaincodeMessageFactory.newGetStateEventMessage(channelId, txId, collection, key); } @Test void testNewGetStateMetadataEventMessage() { ChaincodeMessageFactory.newGetStateMetadataEventMessage(channelId, txId, collection, key); } @Test void testNewPutStateEventMessage() { ChaincodeMessageFactory.newPutStateEventMessage(channelId, txId, collection, key, value); } @Test void testNewPutStateMetadataEventMessage() { ChaincodeMessageFactory.newPutStateMetadataEventMessage(channelId, txId, collection, key, metakey, value); } @Test void testNewDeleteStateEventMessage() { ChaincodeMessageFactory.newDeleteStateEventMessage(channelId, txId, collection, key); } @Test void testNewErrorEventMessage() { ChaincodeMessageFactory.newErrorEventMessage(channelId, txId, message); ChaincodeMessageFactory.newErrorEventMessage(channelId, txId, throwable); ChaincodeMessageFactory.newErrorEventMessage(channelId, txId, message, event); } @Test void testNewCompletedEventMessage() { ChaincodeMessageFactory.newCompletedEventMessage(channelId, txId, response, event); } @Test void testNewInvokeChaincodeMessage() { ChaincodeMessageFactory.newInvokeChaincodeMessage(channelId, txId, payload); } @Test void testNewRegisterChaincodeMessage() { ChaincodeMessageFactory.newRegisterChaincodeMessage(chaincodeId); } @Test void testNewEventMessageTypeStringStringByteString() { ChaincodeMessageFactory.newEventMessage(type, channelId, txId, payload); ChaincodeMessageFactory.newEventMessage(type, channelId, txId, payload, event); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/ChaincodeSupportClientTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.Properties; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class ChaincodeSupportClientTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test void testStartInvocationTaskManagerAndRequestObserverNull() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ManagedChannelBuilder managedChannelBuilder = chaincodeBase.newChannelBuilder(); ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(managedChannelBuilder); assertThatThrownBy( () -> { final ChaincodeID chaincodeId = ChaincodeID.newBuilder() .setName("chaincodeIdNumber12345") .build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); final StreamObserver requestObserver = null; chaincodeSupportClient.start(itm, requestObserver); }, "StreamObserver 'requestObserver' for chat with peer can't be null") .isInstanceOf(IOException.class); environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); } @Test void testStartInvocationTaskManagerNullAndRequestObserver() throws IOException { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ManagedChannelBuilder managedChannelBuilder = chaincodeBase.newChannelBuilder(); ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(managedChannelBuilder); assertThatThrownBy( () -> { chaincodeSupportClient.start(null, new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage value) {} @Override public void onError(final Throwable t) {} @Override public void onCompleted() {} }); }, "InvocationTaskManager 'itm' can't be null") .isInstanceOf(IOException.class); environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/InnvocationTaskManagerTest.java ================================================ /* * Copyright 2020 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import com.google.protobuf.ByteString; import io.grpc.ManagedChannelBuilder; import java.io.IOException; import java.util.Properties; import java.util.function.Consumer; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.chaincode.EmptyChaincode; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @ExtendWith(SystemStubsExtension.class) final class InnvocationTaskManagerTest { @SystemStub private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @BeforeEach void setEnv() { environmentVariables.set("CORE_CHAINCODE_ID_NAME", "mycc"); environmentVariables.set("CORE_PEER_ADDRESS", "localhost:7052"); environmentVariables.set("CORE_PEER_TLS_ENABLED", "false"); environmentVariables.set("CORE_PEER_TLS_ROOTCERT_FILE", "src/test/resources/ca.crt"); environmentVariables.set("CORE_TLS_CLIENT_KEY_PATH", "src/test/resources/client.key.enc"); environmentVariables.set("CORE_TLS_CLIENT_CERT_PATH", "src/test/resources/client.crt.enc"); } @AfterEach void clearEnv() { environmentVariables.remove("CORE_CHAINCODE_ID_NAME"); environmentVariables.remove("CORE_PEER_ADDRESS"); environmentVariables.remove("CORE_PEER_TLS_ENABLED"); environmentVariables.remove("CORE_PEER_TLS_ROOTCERT_FILE"); environmentVariables.remove("CORE_TLS_CLIENT_KEY_PATH"); environmentVariables.remove("CORE_TLS_CLIENT_CERT_PATH"); } @Test void getManager() throws IOException { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Traces.initialize(props); Metrics.initialize(props); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); } @Test void getManagerChaincodeIDNull() throws IOException { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); Assertions.assertThrows( IllegalArgumentException.class, () -> { final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, null); }, "chaincodeId can't be null"); } @Test void getManagerChaincodeBaseNull() throws IOException { final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); Assertions.assertThrows( IllegalArgumentException.class, () -> { final InvocationTaskManager itm = InvocationTaskManager.getManager(null, chaincodeId); }, "chaincode is null"); } @Test void onChaincodeMessage() throws IOException { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); Assertions.assertThrows( IllegalArgumentException.class, () -> itm.onChaincodeMessage(null), "chaincodeMessage is null"); } @Test void setResponseConsumer() throws IOException { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); itm.setResponseConsumer(null); } @Test void registerException() { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); Assertions.assertThrows(IllegalArgumentException.class, itm::register, "outgoingMessage is null"); } @Test void onChaincodeMessageREGISTER() { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); final Consumer consumer = t -> { assertEquals(ChaincodeMessageFactory.newRegisterChaincodeMessage(chaincodeId), t); }; itm.setResponseConsumer(consumer); final ChaincodeMessage chaincodeMessage = ChaincodeMessageFactory.newRegisterChaincodeMessage(chaincodeId); itm.onChaincodeMessage(chaincodeMessage); } @Test void onChaincodeMessageInvokeChaincode() { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final String chaincodeIdNumber = "chaincodeIdNumber12345"; final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName(chaincodeIdNumber).build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); final Consumer consumer = t -> { assertEquals(ChaincodeMessageFactory.newRegisterChaincodeMessage(chaincodeId), t); }; itm.setResponseConsumer(consumer); final ChaincodeMessage chaincodeMessage = ChaincodeMessageFactory.newInvokeChaincodeMessage( chaincodeIdNumber, "txid", ByteString.copyFromUtf8("")); itm.onChaincodeMessage(chaincodeMessage); } @Test void onChaincodeMessagePutState() { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Metrics.initialize(props); Traces.initialize(props); final String chaincodeIdNumber = "chaincodeIdNumber12345"; final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName(chaincodeIdNumber).build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); final Consumer consumer = t -> { assertEquals(ChaincodeMessageFactory.newRegisterChaincodeMessage(chaincodeId), t); }; itm.setResponseConsumer(consumer); final ChaincodeMessage chaincodeMessage = ChaincodeMessageFactory.newPutStateEventMessage( chaincodeIdNumber, "txid", "collection", "key", ByteString.copyFromUtf8("value")); itm.onChaincodeMessage(chaincodeMessage); } @Test void shutdown() throws IOException { final ChaincodeBase chaincodeBase = new EmptyChaincode(); chaincodeBase.processEnvironmentOptions(); chaincodeBase.validateOptions(); Properties props = chaincodeBase.getChaincodeConfig(); Traces.initialize(props); Metrics.initialize(props); final ManagedChannelBuilder managedChannelBuilder = chaincodeBase.newChannelBuilder(); ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(managedChannelBuilder); final ChaincodeID chaincodeId = ChaincodeID.newBuilder().setName("chaincodeIdNumber12345").build(); final InvocationTaskManager itm = InvocationTaskManager.getManager(chaincodeBase, chaincodeId); itm.shutdown(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/InvocationStubImplTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.GET_STATE_BY_RANGE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.GetStateByRange; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; final class InvocationStubImplTest { private final String channelId = "mychannel"; private final String txId = "0xCAFEBABE"; private final String simpleKeyStartNamespace = new String(Character.toChars(0x000001)); @Nested final class GetStateByRangeTests { private InvocationStubImpl stubImpl; private ArgumentCaptor chaincodeMessageCaptor; private ChaincodeInvocationTask mockHandler; @BeforeEach void beforeEach() throws Exception { final ChaincodeMessage mockMessage = ChaincodeMessageFactory.newGetStateEventMessage(channelId, txId, "", "key"); mockHandler = mock(ChaincodeInvocationTask.class); final ByteString mockString = QueryResponse.newBuilder().build().toByteString(); chaincodeMessageCaptor = ArgumentCaptor.forClass(ChaincodeMessage.class); when(mockHandler.invoke(any())).thenReturn(mockString); stubImpl = new InvocationStubImpl(mockMessage, mockHandler); } @Test void regular() throws InvalidProtocolBufferException { final QueryResultsIterator qri = stubImpl.getStateByRange("Aardvark", "Zebra"); verify(mockHandler).invoke(chaincodeMessageCaptor.capture()); assertThat(qri).isNotNull(); final ChaincodeMessage msg = chaincodeMessageCaptor.getValue(); assertThat(msg.getTxid()).isEqualTo("0xCAFEBABE"); assertThat(msg.getType()).isEqualTo(GET_STATE_BY_RANGE); final GetStateByRange range = GetStateByRange.parseFrom(msg.getPayload()); assertThat(range.getStartKey()).isEqualTo("Aardvark"); assertThat(range.getEndKey()).isEqualTo("Zebra"); } @Test void nullvalues() throws InvalidProtocolBufferException { final QueryResultsIterator qri = stubImpl.getStateByRange(null, null); verify(mockHandler).invoke(chaincodeMessageCaptor.capture()); assertThat(qri).isNotNull(); final ChaincodeMessage msg = chaincodeMessageCaptor.getValue(); assertThat(msg.getTxid()).isEqualTo("0xCAFEBABE"); assertThat(msg.getType()).isEqualTo(GET_STATE_BY_RANGE); final GetStateByRange range = GetStateByRange.parseFrom(msg.getPayload()); assertThat(range.getStartKey()).isEqualTo(simpleKeyStartNamespace); assertThat(range.getEndKey()).isEqualTo(""); } @Test void unbounded() throws InvalidProtocolBufferException { final QueryResultsIterator qri = stubImpl.getStateByRange("", ""); verify(mockHandler).invoke(chaincodeMessageCaptor.capture()); assertThat(qri).isNotNull(); final ChaincodeMessage msg = chaincodeMessageCaptor.getValue(); assertThat(msg.getTxid()).isEqualTo("0xCAFEBABE"); assertThat(msg.getType()).isEqualTo(GET_STATE_BY_RANGE); final GetStateByRange range = GetStateByRange.parseFrom(msg.getPayload()); assertThat(range.getStartKey()).isEqualTo(simpleKeyStartNamespace); assertThat(range.getEndKey()).isEqualTo(""); } @Test void simplekeys() { assertThatThrownBy(() -> { final QueryResultsIterator qri = stubImpl.getStateByRange(new String(Character.toChars(Character.MIN_CODE_POINT)), ""); }) .hasMessageContaining("not allowed"); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/InvocationTaskManagerTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.mockito.Mockito.when; import com.google.protobuf.ByteString; import java.io.UnsupportedEncodingException; import java.util.Properties; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.Logger; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; final class InvocationTaskManagerTest { private InvocationTaskManager itm; private ChaincodeBase chaincode; private Logger perfLogger; @BeforeEach void setup() { Metrics.initialize(new Properties()); Traces.initialize(new Properties()); chaincode = Mockito.mock(ChaincodeBase.class); final ChaincodeID id = ChaincodeID.newBuilder().setName("randomname").build(); when(chaincode.getChaincodeConfig()).thenReturn(new Properties()); this.itm = InvocationTaskManager.getManager(chaincode, id); perfLogger = LogManager.getLogManager().getLogger("org.hyperledger.Performance"); perfLogger.setLevel(Level.ALL); this.itm.setResponseConsumer((value) -> {}); } @AfterEach void teardown() { itm.shutdown(); perfLogger.setLevel(Level.INFO); } @Test void register() throws UnsupportedEncodingException { itm.register(); } @Test void onMessageTestTx() throws UnsupportedEncodingException { final ChaincodeMessage msg = ChaincodeMessageFactory.newEventMessage( ChaincodeMessage.Type.TRANSACTION, "mychannel", "txid", ByteString.copyFrom("Hello", "UTF-8")); when(chaincode.getState()).thenReturn(ChaincodeBase.CCState.READY); itm.onChaincodeMessage(msg); } @Test void onWrongCreatedState() throws UnsupportedEncodingException { perfLogger.setLevel(Level.ALL); final ChaincodeMessage msg = ChaincodeMessageFactory.newEventMessage( ChaincodeMessage.Type.TRANSACTION, "mychannel", "txid", ByteString.copyFrom("Hello", "UTF-8")); when(chaincode.getState()).thenReturn(ChaincodeBase.CCState.CREATED); itm.onChaincodeMessage(msg); } @Test void onWrongEstablishedState() throws UnsupportedEncodingException { final ChaincodeMessage msg = ChaincodeMessageFactory.newEventMessage( ChaincodeMessage.Type.TRANSACTION, "mychannel", "txid", ByteString.copyFrom("Hello", "UTF-8")); when(chaincode.getState()).thenReturn(ChaincodeBase.CCState.ESTABLISHED); // final InvocationTaskManager itm = // InvocationTaskManager.getManager(chaincode, id); itm.onChaincodeMessage(msg); } @Test void onErrorResponse() throws UnsupportedEncodingException { final ChaincodeMessage msg = ChaincodeMessageFactory.newEventMessage( ChaincodeMessage.Type.ERROR, "mychannel", "txid", ByteString.copyFrom("Hello", "UTF-8")); when(chaincode.getState()).thenReturn(ChaincodeBase.CCState.READY); itm.onChaincodeMessage(msg); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/KeyModificationImplTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import com.google.protobuf.ByteString; import com.google.protobuf.Timestamp; import java.util.stream.Stream; import org.hyperledger.fabric.shim.ledger.KeyModification; import org.junit.jupiter.api.Test; final class KeyModificationImplTest { @Test void testKeyModificationImpl() { new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setTxId("txid") .setValue(ByteString.copyFromUtf8("value")) .setTimestamp(Timestamp.newBuilder().setSeconds(1234567890).setNanos(123456789)) .setIsDelete(true) .build()); } @Test void testGetTxId() { final KeyModification km = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setTxId("txid") .build()); assertThat(km.getTxId()).isEqualTo("txid"); } @Test void testGetValue() { final KeyModification km = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setValue(ByteString.copyFromUtf8("value")) .build()); assertThat(km.getValue()).isEqualTo("value".getBytes(UTF_8)); } @Test void testGetStringValue() { final KeyModification km = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setValue(ByteString.copyFromUtf8("value")) .build()); assertThat(km.getStringValue()).isEqualTo("value"); } @Test void testGetTimestamp() { final KeyModification km = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setTimestamp( Timestamp.newBuilder().setSeconds(1234567890L).setNanos(123456789)) .build()); assertThat(km.getTimestamp().getEpochSecond()).isEqualTo(1234567890L); assertThat(km.getTimestamp().getNano()).isEqualTo(123456789); } @Test void testIsDeleted() { Stream.of(true, false).forEach(b -> { final KeyModification km = new KeyModificationImpl( org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(b) .build()); assertThat(km.isDeleted()).isEqualTo(b); }); } @Test void testHashCode() { final KeyModification km1 = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(false) .build()); final KeyModification km2 = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(false) .build()); assertThat(km1.hashCode()).isEqualTo(km2.hashCode()); } @Test void testEquals() { final KeyModification km1 = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(false) .build()); final KeyModification km2 = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(true) .build()); final KeyModification km3 = new KeyModificationImpl(org.hyperledger.fabric.protos.ledger.queryresult.KeyModification.newBuilder() .setIsDelete(false) .build()); assertThat(km1).isNotEqualTo(km2); assertThat(km1).isEqualTo(km3); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/KeyValueImplTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import com.google.protobuf.ByteString; import org.hyperledger.fabric.protos.ledger.queryresult.KV; import org.junit.jupiter.api.Test; final class KeyValueImplTest { @Test void testKeyValueImpl() { new KeyValueImpl(KV.newBuilder() .setKey("key") .setValue(ByteString.copyFromUtf8("value")) .build()); } @Test void testGetKey() { final KeyValueImpl kv = new KeyValueImpl(KV.newBuilder() .setKey("key") .setValue(ByteString.copyFromUtf8("value")) .build()); assertThat(kv.getKey()).isEqualTo("key"); } @Test void testGetValue() { final KeyValueImpl kv = new KeyValueImpl(KV.newBuilder() .setKey("key") .setValue(ByteString.copyFromUtf8("value")) .build()); assertThat(kv.getValue()).isEqualTo("value".getBytes(UTF_8)); } @Test void testGetStringValue() { final KeyValueImpl kv = new KeyValueImpl(KV.newBuilder() .setKey("key") .setValue(ByteString.copyFromUtf8("value")) .build()); assertThat(kv.getStringValue()).isEqualTo("value"); } @Test void testHashCode() { final KeyValueImpl kv1 = new KeyValueImpl(KV.newBuilder().build()); final KeyValueImpl kv2 = new KeyValueImpl(KV.newBuilder().build()); assertThat(kv1.hashCode()).isEqualTo(kv2.hashCode()); } @Test void testEquals() { final KeyValueImpl kv1 = new KeyValueImpl(KV.newBuilder() .setKey("a") .setValue(ByteString.copyFromUtf8("valueA")) .build()); final KeyValueImpl kv2 = new KeyValueImpl(KV.newBuilder() .setKey("a") .setValue(ByteString.copyFromUtf8("valueB")) .build()); final KeyValueImpl kv3 = new KeyValueImpl(KV.newBuilder() .setKey("b") .setValue(ByteString.copyFromUtf8("valueA")) .build()); final KeyValueImpl kv4 = new KeyValueImpl(KV.newBuilder() .setKey("a") .setValue(ByteString.copyFromUtf8("valueA")) .build()); assertThat(kv1).isNotEqualTo(kv2); assertThat(kv1).isNotEqualTo(kv3); assertThat(kv1).isEqualTo(kv4); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/impl/QueryResultsIteratorWithMetadataImplTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.protobuf.ByteString; import java.util.function.Function; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.protos.peer.QueryResponseMetadata; import org.hyperledger.fabric.protos.peer.QueryResultBytes; import org.junit.jupiter.api.Test; final class QueryResultsIteratorWithMetadataImplTest { private static final Function QUERY_RESULT_BYTES_TO_KV = queryResultBytes -> 0; @Test void getMetadata() { final QueryResultsIteratorWithMetadataImpl testIter = new QueryResultsIteratorWithMetadataImpl<>( null, "", "", prepareQueryResponse().toByteString(), QUERY_RESULT_BYTES_TO_KV); assertThat(testIter.getMetadata().getBookmark()).isEqualTo("asdf"); assertThat(testIter.getMetadata().getFetchedRecordsCount()).isEqualTo(2); } @Test void getInvalidMetadata() { assertThatThrownBy(() -> new QueryResultsIteratorWithMetadataImpl<>( null, "", "", prepareQueryResponseWrongMeta().toByteString(), QUERY_RESULT_BYTES_TO_KV)) .isInstanceOf(RuntimeException.class); } private QueryResponse prepareQueryResponse() { final QueryResponseMetadata qrm = QueryResponseMetadata.newBuilder() .setBookmark("asdf") .setFetchedRecordsCount(2) .build(); return QueryResponse.newBuilder() .setHasMore(false) .setMetadata(qrm.toByteString()) .build(); } private QueryResponse prepareQueryResponseWrongMeta() { final ByteString bs = ByteString.copyFrom(new byte[] {0, 0}); return QueryResponse.newBuilder().setHasMore(false).setMetadata(bs).build(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/ledger/CompositeKeyTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.ledger; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import java.util.Arrays; import org.junit.jupiter.api.Test; final class CompositeKeyTest { @Test void testValidateSimpleKeys() { CompositeKey.validateSimpleKeys("abc", "def", "ghi"); } @Test void testValidateSimpleKeysException() { assertThatThrownBy(() -> CompositeKey.validateSimpleKeys("\u0000abc")) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testCompositeKeyStringStringArray() { final CompositeKey key = new CompositeKey("abc", "def", "ghi", "jkl", "mno"); assertThat(key.getObjectType(), is(equalTo("abc"))); assertThat(key.getAttributes(), hasSize(4)); assertThat(key.toString(), is(equalTo("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"))); } @Test void testCompositeKeyStringListOfString() { final CompositeKey key = new CompositeKey("abc", Arrays.asList("def", "ghi", "jkl", "mno")); assertThat(key.getObjectType(), is(equalTo("abc"))); assertThat(key.getAttributes(), hasSize(4)); assertThat(key.toString(), is(equalTo("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"))); } @Test void testEmptyAttributes() { final CompositeKey key = new CompositeKey("abc"); assertThat(key.getObjectType(), is(equalTo("abc"))); assertThat(key.getAttributes(), hasSize(0)); assertThat(key.toString(), is(equalTo("\u0000abc\u0000"))); } @Test void testCompositeKeyWithInvalidObjectTypeDelimiter() { assertThatThrownBy(() -> new CompositeKey("ab\u0000c", Arrays.asList("def", "ghi", "jkl", "mno"))) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testCompositeKeyWithInvalidAttributeDelimiter() { assertThatThrownBy(() -> new CompositeKey("abc", Arrays.asList("def", "ghi", "j\u0000kl", "mno"))) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testCompositeKeyWithInvalidObjectTypeMaxCodePoint() { assertThatThrownBy(() -> new CompositeKey("ab\udbff\udfffc", Arrays.asList("def", "ghi", "jkl", "mno"))) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testCompositeKeyWithInvalidAttributeMaxCodePoint() { assertThatThrownBy(() -> new CompositeKey("abc", Arrays.asList("def", "ghi", "jk\udbff\udfffl", "mno"))) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testGetObjectType() { final CompositeKey key = new CompositeKey("abc", Arrays.asList("def", "ghi", "jkl", "mno")); assertThat(key.getObjectType(), is(equalTo("abc"))); } @Test void testGetAttributes() { final CompositeKey key = new CompositeKey("abc", Arrays.asList("def", "ghi", "jkl", "mno")); assertThat(key.getObjectType(), is(equalTo("abc"))); assertThat(key.getAttributes(), hasSize(4)); assertThat(key.getAttributes(), contains("def", "ghi", "jkl", "mno")); } @Test void testToString() { final CompositeKey key = new CompositeKey("abc", Arrays.asList("def", "ghi", "jkl", "mno")); assertThat(key.toString(), is(equalTo("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"))); } @Test void testParseCompositeKey() { final CompositeKey key = CompositeKey.parseCompositeKey("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"); assertThat(key.getObjectType(), is(equalTo("abc"))); assertThat(key.getAttributes(), hasSize(4)); assertThat(key.getAttributes(), contains("def", "ghi", "jkl", "mno")); assertThat(key.toString(), is(equalTo("\u0000abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000"))); } @Test void testParseCompositeKeyInvalidObjectType() { assertThatThrownBy(() -> CompositeKey.parseCompositeKey("ab\udbff\udfffc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000")) .isInstanceOf(CompositeKeyFormatException.class); } @Test void testParseCompositeKeyInvalidAttribute() { assertThatThrownBy(() -> CompositeKey.parseCompositeKey("abc\u0000def\u0000ghi\u0000jk\udbff\udfffl\u0000mno\u0000")) .isInstanceOf(CompositeKeyFormatException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/ChaincodeMockPeer.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import static org.hyperledger.fabric.protos.peer.ChaincodeMessage.Type.PUT_STATE; import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc; import org.hyperledger.fabric.shim.utils.TimeoutUtil; /** Mock peer implementation */ public final class ChaincodeMockPeer { private static final Logger LOGGER = Logger.getLogger(ChaincodeMockPeer.class.getName()); private final int port; private final Server server; private final ChaincodeMockPeerService service; /** * Constructor * * @param scenario list of scenario steps * @param port mock peer communication port * @throws IOException */ public ChaincodeMockPeer(final List scenario, final int port) { this.port = port; this.service = new ChaincodeMockPeerService(scenario); final ServerBuilder sb = ServerBuilder.forPort(port); this.server = sb.addService(this.service).build(); } /** Start serving requests. */ @SuppressWarnings("PMD.SystemPrintln") public void start() throws IOException { server.start(); LOGGER.info(() -> "Server started, listening on " + port); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { // Use stderr here since the logger may have been reset by its JVM shutdown hook. System.err.println("*** shutting down gRPC server since JVM is shutting down"); ChaincodeMockPeer.this.stop(); System.err.println("*** server shut down"); } }); } /** Stop serving requests and shutdown resources. */ public void stop() { if (server != null) { server.shutdownNow(); try { server.awaitTermination(); } catch (final InterruptedException ignored) { } } } /** * Send message from mock peer to chaincode (to start init, invoke, etc) * * @param msg */ public void send(final ChaincodeMessage msg) { this.service.lastMessageSend = msg; LOGGER.info(() -> "Mock peer => Sending message: " + msg); this.service.send(msg); } /** * Check last executed step number, to check where in scenario we stopped * * @return */ public int getLastExecutedStep() { return this.service.lastExecutedStepNumber; } /** @return last received message from chaincode */ public ChaincodeMessage getLastMessageRcvd() { return this.service.lastMessageRcvd; } public List getAllReceivedMessages() { return this.service.allMessages; } /** @return last message sent by peer to chaincode */ public ChaincodeMessage getLastMessageSend() { return this.service.lastMessageSend; } /** * Creates new instance of mock peer server, starts it and returns * * @param scenario * @return * @throws Exception */ public static ChaincodeMockPeer startServer(final List scenario) throws Exception { final ChaincodeMockPeer server = new ChaincodeMockPeer(scenario, 7052); server.start(); return server; } private static class ChaincodeMockPeerService extends ChaincodeSupportGrpc.ChaincodeSupportImplBase { private final List scenario; private int lastExecutedStepNumber; private ChaincodeMessage lastMessageRcvd; private ChaincodeMessage lastMessageSend; private final List allMessages = new ArrayList<>(); private StreamObserver observer; // create a lock, with fair property private final ReentrantLock lock = new ReentrantLock(true); ChaincodeMockPeerService(final List scenario) { this.scenario = scenario; this.lastExecutedStepNumber = 0; } public void send(final ChaincodeMessage msg) { lock.lock(); observer.onNext(msg); lock.unlock(); } /** * Attaching observer to steams * * @param responseObserver * @return */ @Override public StreamObserver register(final StreamObserver responseObserver) { observer = responseObserver; return new StreamObserver<>() { /** * Handling incoming messages * * @param chaincodeMessage */ @Override @SuppressWarnings("PMD.AvoidCatchingThrowable") public void onNext(final ChaincodeMessage chaincodeMessage) { try { LOGGER.info(() -> "Mock peer => Got message: " + chaincodeMessage); ChaincodeMockPeerService.this.lastMessageRcvd = chaincodeMessage; ChaincodeMockPeerService.this.allMessages.add(chaincodeMessage); if (chaincodeMessage.getType().equals(PUT_STATE)) { final ChaincodeMessage m = ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(chaincodeMessage.getChannelId()) .setTxid(chaincodeMessage.getTxid()) .build(); Thread.sleep(500); ChaincodeMockPeerService.this.send(m); } else if (ChaincodeMockPeerService.this.scenario.size() > 0) { final ScenarioStep step = ChaincodeMockPeerService.this.scenario.get(0); ChaincodeMockPeerService.this.scenario.remove(0); if (step.expected(chaincodeMessage)) { final List nextSteps = step.next(); for (final ChaincodeMessage m : nextSteps) { ChaincodeMockPeerService.this.lastMessageSend = m; LOGGER.info(() -> "Mock peer => Sending response message: " + m); ChaincodeMockPeerService.this.send(m); } } else { LOGGER.warning(() -> "Non expected message rcvd in step " + step.getClass().getSimpleName()); } ChaincodeMockPeerService.this.lastExecutedStepNumber++; } } catch (final Throwable t) { t.printStackTrace(); } } @Override public void onError(final Throwable throwable) { throwable.printStackTrace(); } @Override public void onCompleted() {} }; } } @SuppressWarnings("PMD.SystemPrintln") public static void checkScenarioStepEnded( final ChaincodeMockPeer s, final int step, final int timeout, final TimeUnit units) throws Exception { try { TimeoutUtil.runWithTimeout( new Thread(() -> { while (true) { if (s.getLastExecutedStep() == step) { return; } try { Thread.sleep(500); } catch (final InterruptedException ignored) { } } }), timeout, units); } catch (final TimeoutException e) { System.out.println("Got timeout, step " + step + " not finished"); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/CompleteStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.Collections; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** Waits for COMPLETED message, sends nothing back */ public final class CompleteStep implements ScenarioStep { @Override public boolean expected(final ChaincodeMessage msg) { return msg.getType() == ChaincodeMessage.Type.COMPLETED; } @Override public List next() { return Collections.emptyList(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/DelValueStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates delState() invocation in chaincode Waits for DEL_STATE message from chaincode and sends back response with * empty payload */ public final class DelValueStep implements ScenarioStep { private ChaincodeMessage orgMsg; @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.DEL_STATE; } @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/ErrorResponseStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.Collections; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** Error message from chaincode side, no response sent */ public final class ErrorResponseStep implements ScenarioStep { @Override public boolean expected(final ChaincodeMessage msg) { return msg.getType() == ChaincodeMessage.Type.ERROR; } @Override public List next() { return Collections.emptyList(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/GetHistoryForKeyStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import static java.util.stream.Collectors.toList; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.hyperledger.fabric.protos.ledger.queryresult.KeyModification; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.protos.peer.QueryResultBytes; public final class GetHistoryForKeyStep implements ScenarioStep { private ChaincodeMessage orgMsg; private final String[] values; private final boolean hasNext; /** * Initiate step * * @param hasNext is response message QueryResponse hasMore field set * @param vals list of keys to generate ("key" => "key Value") pairs */ public GetHistoryForKeyStep(final boolean hasNext, final String... vals) { this.values = Arrays.copyOf(vals, vals.length); this.hasNext = hasNext; } @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.GET_HISTORY_FOR_KEY; } @Override public List next() { final List keyModifications = Arrays.asList(values).stream() .map(x -> KeyModification.newBuilder() .setTxId(x) .setValue(ByteString.copyFromUtf8(x + " Value")) .build()) .collect(toList()); final QueryResponse.Builder builder = QueryResponse.newBuilder(); builder.setHasMore(hasNext); keyModifications.stream() .forEach(kv -> builder.addResults(QueryResultBytes.newBuilder().setResultBytes(kv.toByteString()))); final ByteString historyPayload = builder.build().toByteString(); final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(historyPayload) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/GetQueryResultStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates query invocation. Waits for GET_QUERY_RESULT Returns message that contains list of results in form ("key" * => "key Value")* */ public final class GetQueryResultStep extends QueryResultStep { /** * Initiate step * * @param hasNext is response message QueryResponse hasMore field set * @param vals list of keys to generate ("key" => "key Value") pairs */ public GetQueryResultStep(final boolean hasNext, final String... vals) { super(hasNext, vals); } @Override public boolean expected(final ChaincodeMessage msg) { super.orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.GET_QUERY_RESULT; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/GetStateByRangeStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates getStateByRange Waits for GET_STATE_BY_RANGE message Returns message that contains list of results in form * ("key" => "key Value")* */ public final class GetStateByRangeStep extends QueryResultStep { /** * Initiate step * * @param hasNext is response message QueryResponse hasMore field set * @param vals list of keys to generate ("key" => "key Value") pairs */ public GetStateByRangeStep(final boolean hasNext, final String... vals) { super(hasNext, vals); } @Override public boolean expected(final ChaincodeMessage msg) { super.orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.GET_STATE_BY_RANGE; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/GetStateMetadata.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.MetaDataKeys; import org.hyperledger.fabric.protos.peer.StateMetadata; import org.hyperledger.fabric.protos.peer.StateMetadataResult; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; /** * simulates Handler.getStateMetadata Waits for GET_STATE_METADATA message Returns response message with stored metadata */ public final class GetStateMetadata implements ScenarioStep { private ChaincodeMessage orgMsg; private final byte[] val; /** @param sbe StateBasedEndorsement to return as one and only one metadata entry */ public GetStateMetadata(final StateBasedEndorsement sbe) { val = sbe.policy(); } @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.GET_STATE_METADATA; } @Override public List next() { final List entriesList = new ArrayList<>(); final StateMetadata validationValue = StateMetadata.newBuilder() .setMetakey(MetaDataKeys.VALIDATION_PARAMETER.toString()) .setValue(ByteString.copyFrom(val)) .build(); entriesList.add(validationValue); final StateMetadataResult stateMetadataResult = StateMetadataResult.newBuilder().addAllEntries(entriesList).build(); final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(stateMetadataResult.toByteString()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/GetValueStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** Simulates getState Waits for GET_STATE message Returns response message with value as payload */ public final class GetValueStep implements ScenarioStep { private ChaincodeMessage orgMsg; private final String val; /** @param val value to return */ public GetValueStep(final String val) { this.val = val; } @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.GET_STATE; } @Override public List next() { final ByteString getPayload = ByteString.copyFromUtf8(val); final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(getPayload) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/InvokeChaincodeStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.Response; import org.hyperledger.fabric.shim.Chaincode; /** * Simulates another chaincode invocation Waits for INVOKE_CHAINCODE Sends back RESPONSE message with chaincode response * inside */ public final class InvokeChaincodeStep implements ScenarioStep { private ChaincodeMessage orgMsg; @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.INVOKE_CHAINCODE; } /** * @return Chaincode response packed as payload inside COMPLETE message packed as payload inside RESPONSE message */ @Override public List next() { final ByteString chaincodeResponse = Response.newBuilder() .setStatus(Chaincode.Response.Status.SUCCESS.getCode()) .setMessage("OK") .build() .toByteString(); final ByteString completePayload = ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.COMPLETED) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(chaincodeResponse) .build() .toByteString(); final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(completePayload) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/PurgeValueStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates purgePrivateData() invocation in chaincode Waits for PURGE_PRIVATE_DATA message from chaincode and sends * back response with empty payload */ public final class PurgeValueStep implements ScenarioStep { private ChaincodeMessage orgMsg; @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.PURGE_PRIVATE_DATA; } @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/PutStateMetadata.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.MetaDataKeys; import org.hyperledger.fabric.shim.ext.sbe.StateBasedEndorsement; import org.hyperledger.fabric.shim.ext.sbe.impl.StateBasedEndorsementFactory; /** * * Simulates Handler.putStateMetadata() invocation from chaincode side * Waits for PUT_STATE_METADATA message from * chaincode, including metadata entry with validation metadata and sends back response with empty payload */ public final class PutStateMetadata implements ScenarioStep { private ChaincodeMessage orgMsg; private final StateBasedEndorsement val; public PutStateMetadata(final StateBasedEndorsement sbe) { val = sbe; } /** * Check incoming message If message type is PUT_STATE_METADATA and payload match to passed in constructor * * @param msg message from chaincode * @return */ @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; org.hyperledger.fabric.protos.peer.PutStateMetadata psm; try { psm = org.hyperledger.fabric.protos.peer.PutStateMetadata.parseFrom(msg.getPayload()); } catch (final InvalidProtocolBufferException e) { return false; } final StateBasedEndorsement msgSbe = StateBasedEndorsementFactory.getInstance() .newStateBasedEndorsement(psm.getMetadata().getValue().toByteArray()); return msg.getType() == ChaincodeMessage.Type.PUT_STATE_METADATA && MetaDataKeys.VALIDATION_PARAMETER .toString() .equals(psm.getMetadata().getMetakey()) && (msgSbe.listOrgs().size() == val.listOrgs().size()); } @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/PutValueStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import com.google.protobuf.InvalidProtocolBufferException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.PutState; /** * Simulates putState() invocation in chaincode Waits for PUT_STATE message from chaincode, including value and sends * back response with empty payload */ public final class PutValueStep implements ScenarioStep { private ChaincodeMessage orgMsg; private final String val; /** * Initiate step * * @param val */ public PutValueStep(final String val) { this.val = val; } /** * Check incoming message If message type is PUT_STATE and payload equal to passed in constructor * * @param msg message from chaincode * @return */ @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; PutState putMsg = null; try { putMsg = PutState.parseFrom(msg.getPayload()); } catch (final InvalidProtocolBufferException e) { return false; } return val.equals(new String(putMsg.getValue().toByteArray(), StandardCharsets.UTF_8)) && msg.getType() == ChaincodeMessage.Type.PUT_STATE; } @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/QueryCloseStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulate last query (close) step. Happens after passing over all query result Waits for QUERY_STATE_CLOSE Sends back * response with empty payload */ public final class QueryCloseStep implements ScenarioStep { private ChaincodeMessage orgMsg; @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.QUERY_STATE_CLOSE; } /** @return RESPONSE message with empty payload */ @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/QueryNextStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates requesting/receiving next set of results for query Waits for QUERY_STATE_NEXT Returns message that contains * list of results in form ("key" => "key Value")* */ public final class QueryNextStep extends QueryResultStep { /** * Initiate step * * @param hasNext is response message QueryResponse hasMore field set * @param vals list of keys to generate ("key" => "key Value") pairs */ public QueryNextStep(final boolean hasNext, final String... vals) { super(hasNext, vals); } @Override public boolean expected(final ChaincodeMessage msg) { super.orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.QUERY_STATE_NEXT; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/QueryResultStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import static java.util.stream.Collectors.toList; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.hyperledger.fabric.protos.ledger.queryresult.KV; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.QueryResponse; import org.hyperledger.fabric.protos.peer.QueryResultBytes; /** Base class for multi result query steps/messages */ public abstract class QueryResultStep implements ScenarioStep { protected ChaincodeMessage orgMsg; protected final String[] values; protected final boolean hasNext; /** * Initiate step * * @param hasNext is response message QueryResponse hasMore field set * @param vals list of keys to generate ("key" => "key Value") pairs */ QueryResultStep(final boolean hasNext, final String... vals) { this.values = Arrays.copyOf(vals, vals.length); this.hasNext = hasNext; } /** * Generate response message that list of (key => value) pairs * * @return */ @Override public List next() { final List keyValues = Arrays.asList(values).stream() .map(x -> KV.newBuilder() .setKey(x) .setValue(ByteString.copyFromUtf8(x + " Value")) .build()) .collect(toList()); final QueryResponse.Builder builder = QueryResponse.newBuilder(); builder.setHasMore(hasNext); keyValues.stream() .forEach(kv -> builder.addResults(QueryResultBytes.newBuilder().setResultBytes(kv.toByteString()))); final ByteString rangePayload = builder.build().toByteString(); final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.RESPONSE) .setChannelId(orgMsg.getChannelId()) .setTxid(orgMsg.getTxid()) .setPayload(rangePayload) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/RegisterStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.ArrayList; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; /** * Simulates chaincode registration after start Waits for REGISTER message from chaincode Sends back pair of messages: * REGISTERED and READY */ public final class RegisterStep implements ScenarioStep { private ChaincodeMessage orgMsg; @Override public boolean expected(final ChaincodeMessage msg) { orgMsg = msg; return msg.getType() == ChaincodeMessage.Type.REGISTER; } @Override public List next() { final List list = new ArrayList<>(); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.REGISTERED) .build()); list.add(ChaincodeMessage.newBuilder() .setType(ChaincodeMessage.Type.READY) .build()); return list; } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/mock/peer/ScenarioStep.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.mock.peer; import java.util.List; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; public interface ScenarioStep { /** * Validate incoming message from chaincode side * * @param msg message from chaincode * @return is incoming message was expected */ boolean expected(ChaincodeMessage msg); /** * List of messages send from peer to chaincode as response(s) * * @return */ List next(); } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/utils/MessageUtil.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.utils; import com.google.protobuf.ByteString; import org.hyperledger.fabric.protos.peer.ChaincodeEvent; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; public final class MessageUtil { private MessageUtil() {} /** * Generate chaincode messages * * @param type * @param channelId * @param txId * @param payload * @param event * @return */ public static ChaincodeMessage newEventMessage( final ChaincodeMessage.Type type, final String channelId, final String txId, final ByteString payload, final ChaincodeEvent event) { final ChaincodeMessage.Builder builder = ChaincodeMessage.newBuilder() .setType(type) .setChannelId(channelId) .setTxid(txId) .setPayload(payload); if (event != null) { builder.setChaincodeEvent(event); } return builder.build(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/shim/utils/TimeoutUtil.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.shim.utils; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** Give possibility to stop runnable execution after specific time, if not ended */ public final class TimeoutUtil { private TimeoutUtil() {} public static void runWithTimeout(final Runnable callable, final long timeout, final TimeUnit timeUnit) throws Exception { final ExecutorService executor = Executors.newSingleThreadExecutor(); final CountDownLatch latch = new CountDownLatch(1); final Thread t = new Thread(() -> { try { callable.run(); } finally { latch.countDown(); } }); try { executor.execute(t); if (!latch.await(timeout, timeUnit)) { throw new TimeoutException(); } } finally { executor.shutdown(); t.interrupt(); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/traces/TracesTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import io.opentelemetry.api.trace.Span; import java.util.Properties; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.traces.impl.DefaultTracesProvider; import org.hyperledger.fabric.traces.impl.NullProvider; import org.hyperledger.fabric.traces.impl.OpenTelemetryTracesProvider; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; final class TracesTest { private static final class TestProvider implements TracesProvider { public TestProvider() {} @Override public void initialize(final Properties props) {} @Override public Span createSpan(final ChaincodeStub stub) { return null; } } @Nested @DisplayName("Traces initialize") final class Initialize { @Test void tracesDisabled() { final TracesProvider provider = Traces.initialize(new Properties()); assertThat(provider).isExactlyInstanceOf(NullProvider.class); } @Test void tracesEnabledUnknownProvider() { final Properties props = new Properties(); props.put("CHAINCODE_TRACES_PROVIDER", "org.example.traces.provider"); props.put("CHAINCODE_TRACES_ENABLED", "true"); assertThrows( RuntimeException.class, () -> { final TracesProvider provider = Traces.initialize(props); }, "Unable to start traces"); } @Test void tracesNoProvider() { final Properties props = new Properties(); props.put("CHAINCODE_TRACES_ENABLED", "true"); final TracesProvider provider = Traces.initialize(props); assertTrue(provider instanceof DefaultTracesProvider); } @Test void tracesOpenTelemetryProvider() { final Properties props = new Properties(); props.put("CHAINCODE_TRACES_PROVIDER", "org.hyperledger.fabric.traces.impl.OpenTelemetryTracesProvider"); props.put("CHAINCODE_TRACES_ENABLED", "true"); final TracesProvider provider = Traces.initialize(props); assertTrue(provider instanceof OpenTelemetryTracesProvider); } @Test void tracesValid() { final Properties props = new Properties(); props.put("CHAINCODE_TRACES_PROVIDER", TracesTest.TestProvider.class.getName()); props.put("CHAINCODE_TRACES_ENABLED", "true"); final TracesProvider provider = Traces.initialize(props); assertThat(provider).isExactlyInstanceOf(TracesTest.TestProvider.class); } } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/traces/impl/DefaultProviderTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.trace.Span; import org.hyperledger.fabric.contract.ChaincodeStubNaiveImpl; import org.hyperledger.fabric.shim.ChaincodeStub; import org.junit.jupiter.api.Test; final class DefaultProviderTest { @Test void testDefaultProvider() { DefaultTracesProvider provider = new DefaultTracesProvider(); ChaincodeStub stub = new ChaincodeStubNaiveImpl(); Span span = provider.createSpan(stub); assertThat(span).isNull(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/traces/impl/OpenTelemetryPropertiesTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.HOURS; import static java.time.temporal.ChronoUnit.MILLIS; import static java.time.temporal.ChronoUnit.MINUTES; import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.Test; final class OpenTelemetryPropertiesTest { @Test void testOverrideValue() { OpenTelemetryProperties props = new OpenTelemetryProperties( Collections.singletonMap("foo", "bar"), Collections.singletonMap("foo", "foobar")); assertThat(props.getString("foo")).isEqualTo("foobar"); } @Test void testCanGetDurationDays() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5d")); assertThat(props.getDuration("foo")).isEqualTo(Duration.of(5, DAYS)); } @Test void testCanGetDurationHours() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5h")); assertThat(props.getDuration("foo")).isEqualTo(Duration.of(5, HOURS)); } @Test void testCanGetDurationMinutes() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5m")); assertThat(props.getDuration("foo")).isEqualTo(Duration.of(5, MINUTES)); } @Test void testCanGetDurationSeconds() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5s")); assertThat(props.getDuration("foo")).isEqualTo(Duration.of(5, SECONDS)); } @Test void testCanGetDurationMilliSeconds() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5ms")); assertThat(props.getDuration("foo")).isEqualTo(Duration.of(5, MILLIS)); } @Test void testCanGetDurationInvalid() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5foo")); assertThatThrownBy(() -> props.getDuration("foo")).isInstanceOf(ConfigurationException.class); } @Test void testGetDouble() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5.23")); assertThat(props.getDouble("foo")).isEqualTo(5.23d); assertThat(props.getDouble("bar")).isNull(); } @Test void testGetDoubleInvalid() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "5foo")); assertThatThrownBy(() -> props.getDouble("foo")).isInstanceOf(ConfigurationException.class); } @Test void testGetLong() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "500003")); assertThat(props.getLong("foo")).isEqualTo(500003L); assertThat(props.getLong("bar")).isNull(); } @Test void testGetInt() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "500003")); assertThat(props.getInt("foo")).isEqualTo(500003); assertThat(props.getInt("bar")).isNull(); } @Test void testGetBoolean() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "true")); assertThat(props.getBoolean("foo")).isTrue(); assertThat(props.getBoolean("bar")).isNull(); } @Test void testGetList() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "foo,bar,foobar")); assertThat(props.getList("foo")).isEqualTo(Arrays.asList("foo", "bar", "foobar")); assertThat(props.getList("bar")).isEqualTo(Collections.emptyList()); } @Test void testGetMap() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "foo=bar,foobar=noes")); Map expected = new HashMap<>(); expected.put("foo", "bar"); expected.put("foobar", "noes"); assertThat(props.getMap("foo")).isEqualTo(expected); } @Test void testGetMapInvalid() { OpenTelemetryProperties props = new OpenTelemetryProperties(Collections.singletonMap("foo", "foo/bar,foobar/noes")); Map expected = new HashMap<>(); assertThatThrownBy(() -> props.getMap("foo")).isInstanceOf(ConfigurationException.class); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/traces/impl/OpenTelemetryTracesProviderTest.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import static org.assertj.core.api.Assertions.assertThat; import io.grpc.ManagedChannelBuilder; import io.grpc.Server; import io.grpc.ServerCall; import io.grpc.ServerCallHandler; import io.grpc.ServerMethodDefinition; import io.grpc.ServerServiceDefinition; import io.grpc.Status; import io.grpc.inprocess.InProcessChannelBuilder; import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.stub.StreamObserver; import io.opentelemetry.api.trace.Span; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.hyperledger.fabric.contract.ChaincodeStubNaiveImpl; import org.hyperledger.fabric.metrics.Metrics; import org.hyperledger.fabric.protos.peer.ChaincodeGrpc; import org.hyperledger.fabric.protos.peer.ChaincodeID; import org.hyperledger.fabric.protos.peer.ChaincodeMessage; import org.hyperledger.fabric.protos.peer.ChaincodeSupportGrpc; import org.hyperledger.fabric.shim.ChaincodeBase; import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ResponseUtils; import org.hyperledger.fabric.shim.impl.ChaincodeSupportClient; import org.hyperledger.fabric.shim.impl.InvocationTaskManager; import org.hyperledger.fabric.traces.Traces; import org.junit.jupiter.api.Test; final class OpenTelemetryTracesProviderTest { private final class ContextGetterChaincode extends ChaincodeBase { @Override public Response init(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse(); } @Override public Response invoke(final ChaincodeStub stub) { return ResponseUtils.newSuccessResponse("OK response"); } @Override public Properties getChaincodeConfig() { return new Properties(); } } @Test void testProvider() { OpenTelemetryTracesProvider provider = new OpenTelemetryTracesProvider(); provider.initialize(new Properties()); ChaincodeStub stub = new ChaincodeStubNaiveImpl(); Span span = provider.createSpan(stub); assertThat(span.isRecording()).isTrue(); assertThat(provider.createInterceptor()).isNotNull(); } @Test void testTracing() throws Exception { Properties props = new Properties(); props.put("CHAINCODE_TRACES_ENABLED", "true"); props.put("CHAINCODE_TRACES_PROVIDER", OpenTelemetryTracesProvider.class.getName()); props.put("OTEL_TRACES_SAMPLER", "always_on"); props.put("OTEL_BSP_SCHEDULE_DELAY", "100"); props.put("otel.traces.exporter", "TestSpanExporterProvider"); Traces.initialize(props); Metrics.initialize(props); // set up a grpc server in process ServerCallHandler handler = (call, headers) -> { call.close(Status.OK, headers); return new ServerCall.Listener<>() {}; }; ServerServiceDefinition.Builder builder = ServerServiceDefinition.builder(ChaincodeGrpc.getServiceDescriptor()) .addMethod(ServerMethodDefinition.create(ChaincodeGrpc.getConnectMethod(), handler)); ServerServiceDefinition.Builder supportBuilder = ServerServiceDefinition.builder( ChaincodeSupportGrpc.getServiceDescriptor()) .addMethod(ServerMethodDefinition.create(ChaincodeSupportGrpc.getRegisterMethod(), handler)); String uniqueName = InProcessServerBuilder.generateName(); Server server = InProcessServerBuilder.forName(uniqueName) .directExecutor() .addService(builder.build()) .addService(supportBuilder.build()) .build() .start(); // create our client ManagedChannelBuilder channelBuilder = InProcessChannelBuilder.forName(uniqueName); ContextGetterChaincode chaincode = new ContextGetterChaincode(); ChaincodeSupportClient chaincodeSupportClient = new ChaincodeSupportClient(channelBuilder); InvocationTaskManager itm = InvocationTaskManager.getManager( chaincode, ChaincodeID.newBuilder().setName("foo").build()); CompletableFuture wait = new CompletableFuture<>(); StreamObserver requestObserver = chaincodeSupportClient .getStub() .register(new StreamObserver<>() { @Override public void onNext(final ChaincodeMessage chaincodeMessage) { // message off to the ITM... itm.onChaincodeMessage(chaincodeMessage); } @Override public void onError(final Throwable t) { chaincodeSupportClient.shutdown(itm); wait.completeExceptionally(t); } @Override public void onCompleted() { chaincodeSupportClient.shutdown(itm); wait.complete(null); } }); chaincodeSupportClient.start(itm, requestObserver); wait.get(5, TimeUnit.SECONDS); Thread.sleep(5000); List spans = TestSpanExporterProvider.SPANS; assertThat(spans.isEmpty()).isFalse(); chaincodeSupportClient.shutdown(itm); server.shutdown(); } } ================================================ FILE: fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/traces/impl/TestSpanExporterProvider.java ================================================ /* * Copyright 2019 IBM All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ package org.hyperledger.fabric.traces.impl; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.util.ArrayList; import java.util.Collection; import java.util.List; public final class TestSpanExporterProvider implements ConfigurableSpanExporterProvider { public static final List SPANS = new ArrayList<>(); public static final SpanExporter EXPORTER = new SpanExporter() { @Override public CompletableResultCode export(final Collection spans) { SPANS.addAll(spans); return CompletableResultCode.ofSuccess(); } @Override public CompletableResultCode flush() { return CompletableResultCode.ofSuccess(); } @Override public CompletableResultCode shutdown() { return CompletableResultCode.ofSuccess(); } }; @Override public SpanExporter createExporter(final ConfigProperties config) { return EXPORTER; } @Override public String getName() { return "TestSpanExporterProvider"; } } ================================================ FILE: fabric-chaincode-shim/src/test/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider ================================================ org.hyperledger.fabric.traces.impl.TestSpanExporterProvider ================================================ FILE: fabric-chaincode-shim/src/test/resources/ca.crt ================================================ -----BEGIN CERTIFICATE----- MIICSTCCAe+gAwIBAgIQZ97pJjwOf+/15wXlaQhswTAKBggqhkjOPQQDAjB2MQsw CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xODA4MjExNDEyMzhaFw0yODA4MTgxNDEy MzhaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD VQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D AQcDQgAEgClmvqBKTmruqxNluLAL82p/06D58M6sg/5Qa6epl4/pjc7xv2KpDqIl ONITgmKzR8VslccRoOpV97PRQljH8qNfMF0wDgYDVR0PAQH/BAQDAgGmMA8GA1Ud JQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgmGlEDIWlK8zX Hz3tNOuC1jE58I8yNMaaIiz2fLaopMYwCgYIKoZIzj0EAwIDSAAwRQIhAPSWUrs3 n0Lr6gfaYIxxfEopUm8/J8OVL8cdXPWFnkBbAiBFCCbgtxQRdvPUAHfJLtgOTNwM MxxvehamsJdpqCUsNA== -----END CERTIFICATE----- ================================================ FILE: fabric-chaincode-shim/src/test/resources/client.crt ================================================ -----BEGIN CERTIFICATE----- MIICOjCCAeGgAwIBAgIQCSag4gNL7SdBHpN3BtqTeDAKBggqhkjOPQQDAjB2MQsw CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xODA4MjExNDEyMzhaFw0yODA4MTgxNDEy MzhaMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH Ew1TYW4gRnJhbmNpc2NvMR8wHQYDVQQDDBZVc2VyMUBvcmcxLmV4YW1wbGUuY29t MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzh25F4EJhycT7wKoLRYZt/1rgsaW 4yIPb+QnDg17jX/fhKSnFRcWE4U0OOmwESKc0MniMWxrdzUIYh+9W0DHPKNsMGow DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM BgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIJhpRAyFpSvM1x897TTrgtYxOfCPMjTG miIs9ny2qKTGMAoGCCqGSM49BAMCA0cAMEQCICG6fm4B9BKFfWyLDOwpBOk/KRrI MqJdlNIFI6d6924wAiB4drv3HQCleeVOg2z9Mm4xflcgl78BsYduECh1+qcEqg== -----END CERTIFICATE----- ================================================ FILE: fabric-chaincode-shim/src/test/resources/client.crt.enc ================================================ LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNPakNDQWVHZ0F3SUJBZ0lRQ1NhZzRnTkw3U2RCSHBOM0J0cVRlREFLQmdncWhrak9QUVFEQWpCMk1Rc3cKQ1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0JNS1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ4TU5VMkZ1SUVaeQpZVzVqYVhOamJ6RVpNQmNHQTFVRUNoTVFiM0puTVM1bGVHRnRjR3hsTG1OdmJURWZNQjBHQTFVRUF4TVdkR3h6ClkyRXViM0puTVM1bGVHRnRjR3hsTG1OdmJUQWVGdzB4T0RBNE1qRXhOREV5TXpoYUZ3MHlPREE0TVRneE5ERXkKTXpoYU1Gc3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SWXdGQVlEVlFRSApFdzFUWVc0Z1JuSmhibU5wYzJOdk1SOHdIUVlEVlFRRERCWlZjMlZ5TVVCdmNtY3hMbVY0WVcxd2JHVXVZMjl0Ck1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRXpoMjVGNEVKaHljVDd3S29MUlladC8xcmdzYVcKNHlJUGIrUW5EZzE3algvZmhLU25GUmNXRTRVME9PbXdFU0tjME1uaU1XeHJkelVJWWgrOVcwREhQS05zTUdvdwpEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFNCkJnTlZIUk1CQWY4RUFqQUFNQ3NHQTFVZEl3UWtNQ0tBSUpocFJBeUZwU3ZNMXg4OTdUVHJndFl4T2ZDUE1qVEcKbWlJczlueTJxS1RHTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUNHNmZtNEI5QktGZld5TERPd3BCT2svS1JySQpNcUpkbE5JRkk2ZDY5MjR3QWlCNGRydjNIUUNsZWVWT2cyejlNbTR4ZmxjZ2w3OEJzWWR1RUNoMStxY0VxZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K ================================================ FILE: fabric-chaincode-shim/src/test/resources/client.key ================================================ -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwCm9EFOOwAugpN3s leXGaKAzrr1E/0PJbqh8p2/MqnyhRANCAATOHbkXgQmHJxPvAqgtFhm3/WuCxpbj Ig9v5CcODXuNf9+EpKcVFxYThTQ46bARIpzQyeIxbGt3NQhiH71bQMc8 -----END PRIVATE KEY----- ================================================ FILE: fabric-chaincode-shim/src/test/resources/client.key.enc ================================================ LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ3dDbTlFRk9Pd0F1Z3BOM3MKbGVYR2FLQXpycjFFLzBQSmJxaDhwMi9NcW55aFJBTkNBQVRPSGJrWGdRbUhKeFB2QXFndEZobTMvV3VDeHBiagpJZzl2NUNjT0RYdU5mOStFcEtjVkZ4WVRoVFE0NmJBUklwelF5ZUl4Ykd0M05RaGlINzFiUU1jOAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg== ================================================ FILE: fabric-chaincode-shim/src/test/resources/client.key.password-protected ================================================ -----BEGIN ENCRYPTED PRIVATE KEY----- MIGxMBwGCiqGSIb3DQEMAQMwDgQIfzm0IqTm+rACAggABIGQDY1vpaSD+KDuVRyT Gi35536iOYUuVoz01ktV3YCDv03Pm5+8xZ1JXXW8lDM3JP/TcKbocRRk63y/R7O2 dB9kcyV7/gYtYH0B3TMk1/x1WtfHL8JnYRFHQ/OuhYjJ6O04B4aY2waeYByzsIsI YhNVZq5fZ7/bjsy8b54o57WD4DDHH3uRysbv8I5TaDVyJMJq -----END ENCRYPTED PRIVATE KEY----- ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: pmd-ruleset.xml ================================================ Custom PMD ruleset ================================================ FILE: release_notes/v1.3.0.txt ================================================ v1.3.0 September 25, 2018 ------------------------- Release Notes ------------- Initial release of Java chaincode support. baseimage version 0.4.12 Java version 1.8.0_sr5fp21 Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v130-rc1 ================================================ FILE: release_notes/v1.4.0.txt ================================================ v1.4.0-rc1 December 11, 2018 ---------------------------- Release Notes ------------- Java chaincode v1.4.0 adds parity with Go chaincode: FAB-12329 State-based endorsement support FAB-12328 Query result pagination support Increase test coverage, including integration tests based on testcontainers framework. baseimage version: 0.4.14 Java version: openjdk version "1.8.0_181" Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v140-rc1 ================================================ FILE: release_notes/v2.0.0-alpha.txt ================================================ v2.0.0-alpha April 9, 2019 -------------------------- Release Notes ------------- Java chaincode v2.0.0-alpha includes multiple improvements: Javaenv docker images is now based on Alpine to reduce the size. Integration tests have been improved. Java chaincode extra validation during start. Added support for maven projects. openjdk:8-slim is now used instead of baseimage for the basis of javaenv Docker image. Java version: openjdk version "1.8.0_181" Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v200-alpha ================================================ FILE: release_notes/v2.0.0-beta.txt ================================================ v2.0.0-beta December 12, 2019 -------------------------- Release Notes ------------- Java chaincode v2.0.0-alpha includes multiple improvements: Javaenv docker images is now based on Alpine to reduce the size. Integration tests have been improved. Java chaincode extra validation during start. Added support for maven projects. adoptopenjdk/openjdk11:jdk-11.0.4_11-alpine is now used instead of baseimage for the basis of javaenv Docker image. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v200-beta ================================================ FILE: release_notes/v2.0.0.txt ================================================ v2.0.0 20 January 2020 -------------------------- Release Notes ------------- Java chaincode v2.0.0 includes multiple improvements: Javaenv docker images is now based on Alpine to reduce the size. Integration tests have been improved. Java chaincode extra validation during start. Added support for maven projects. adoptopenjdk/openjdk11:jdk-11.0.4_11-alpine is now used instead of baseimage for the basis of javaenv Docker image. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v200 ================================================ FILE: release_notes/v2.0.1.txt ================================================ v2.0.1 4 March 2020 -------------------------- Release Notes ------------- - Provides access to the localmspid adoptopenjdk/openjdk11:jdk-11.0.4_11-alpine is now used for the basis of javaenv Docker image. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/master/CHANGELOG.md#v201 ================================================ FILE: release_notes/v2.1.0.txt ================================================ v2.1.0 4 March 2020 -------------------------- Release Notes ------------- There are minimal changes between v2.0.0 and v2.1.0, please see the change log for a full list of updates. The release-2.0 branch has been renamed to release-2.x; the v2.1.0 release supercedes v2.0.0. The release-1.4 branch is currently LTS, please see the proposed Fabric LTS strategy for more information: https://github.com/hyperledger/fabric-rfcs/pull/23 Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v210 ================================================ FILE: release_notes/v2.1.1.txt ================================================ v2.1.1 18 May 2020 -------------------------- Release Notes ------------- This is a bug fix release with a fix for a performance issue when using query. See this JIRA for more information https://jira.hyperledger.org/browse/FABCJ-285 The release-2.0 branch has been renamed to release-2.x; the v2.1.0 release supercedes v2.0.0. The release-1.4 branch is currently LTS, please see the proposed Fabric LTS strategy for more information: https://github.com/hyperledger/fabric-rfcs/pull/23 Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v211 ================================================ FILE: release_notes/v2.2.0.txt ================================================ v2.2.0 -------------------------- Release Notes ------------- This v2.2.0 Release is the LTS version of the fabric-chaincode-java Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v220 ================================================ FILE: release_notes/v2.2.1.txt ================================================ v2.2.1 -------------------------- Release Notes ------------- This v2.2.1 Release is the LTS version of the fabric-chaincode-java Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v221 ================================================ FILE: release_notes/v2.3.0.txt ================================================ v2.3.0 -------------------------- Release Notes ------------- This v2.3.0 Release is the LTS version of the fabric-chaincode-java Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v221 ================================================ FILE: release_notes/v2.3.1.txt ================================================ v2.3.1 -------------------------- Release Notes ------------- This v2.3.1 Release is a bug fix release of the main branch. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v231 ================================================ FILE: release_notes/v2.4.0-beta.txt ================================================ v2.4.0-beta -------------------------- Release Notes ------------- This v2.4.0-beta Release is a bug fix release of the main branch. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v240 ================================================ FILE: release_notes/v2.4.0.txt ================================================ v2.4.0 -------------------------- Release Notes ------------- This v2.4.0 Release is to support the Fabric v2.4 release. Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v240 ================================================ FILE: release_notes/v2.4.1.txt ================================================ v2.4.1 -------------------------- Release Notes ------------- This release improves support for the chaincode-as-a-service feature. It removes the need to write custom 'bootstrap' scripts See the `examples/fabric-contract-examples-as-service` Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v241 ================================================ FILE: release_notes/v2.5.0.txt ================================================ v2.5.0 LTS ---------- Release Notes ------------- This is the LTS Release of of the v2.5 Fabric Chaincode Java. It replaces the previous v2.2 LTS. - the default `JavaEnv` docker image has been moved to use the Eclipse Temurin Java 11 JDK - the PurgePrivateData feature is exposed via a new `PurgePrivateData` API Known Vulnerabilities --------------------- none Resolved Vulnerabilities ------------------------ none Known Issues & Workarounds -------------------------- none Change Log ---------- https://github.com/hyperledger/fabric-chaincode-java/blob/release-2.x/CHANGELOG.md#v250 ================================================ FILE: settings.gradle ================================================ /* * Copyright IBM Corp. 2017 All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ rootProject.name = 'fabric-chaincode-java' include 'fabric-chaincode-shim' include 'fabric-chaincode-docker' include 'fabric-chaincode-integration-test'