ERC20 token review error fix

Signed-off-by: renjithkn@gmail.com <renjithkn@gmail.com>
This commit is contained in:
renjithkn@gmail.com 2022-01-31 13:28:28 +00:00
parent 590851471b
commit efe232f5a5
29 changed files with 2140 additions and 822 deletions

View file

@ -0,0 +1,43 @@
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html
*/
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
ext {
javaMainClass = "application.java.App"
}
repositories {
mavenCentral()
}
dependencies {
// This dependency is used by the application.
implementation 'org.hyperledger.fabric:fabric-gateway:1.0.0'
implementation 'io.grpc:grpc-netty-shaded:1.42.0'
implementation 'com.google.code.gson:gson:2.8.9'
}
application {
// Define the main class for the application.
mainClassName = 'application.java.App'
}
// task for running the app after building dependencies
task runApp(type: Exec) {
dependsOn build
group = "Execution"
description = "Run the main class with ExecTask"
commandLine "java", "-classpath", sourceSets.main.runtimeClasspath.getAsPath(), javaMainClass
}

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or 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 UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# 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
;;
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"
which java >/dev/null 2>&1 || 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
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

View file

@ -0,0 +1,104 @@
@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=.
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%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="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!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1,10 @@
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html
*/
rootProject.name = 'application-java'

View file

@ -0,0 +1,286 @@
/*
* Copyright IBM Corp. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
// Running TestApp:
// gradle runApp
package application.java;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.hyperledger.fabric.client.CallOption;
import org.hyperledger.fabric.client.CommitException;
import org.hyperledger.fabric.client.CommitStatusException;
import org.hyperledger.fabric.client.Contract;
import org.hyperledger.fabric.client.EndorseException;
import org.hyperledger.fabric.client.Gateway;
import org.hyperledger.fabric.client.GatewayException;
import org.hyperledger.fabric.client.Network;
import org.hyperledger.fabric.client.SubmitException;
import org.hyperledger.fabric.client.SubmittedTransaction;
import org.hyperledger.fabric.client.Status;
import org.hyperledger.fabric.client.identity.Identities;
import org.hyperledger.fabric.client.identity.Identity;
import org.hyperledger.fabric.client.identity.Signer;
import org.hyperledger.fabric.client.identity.Signers;
import org.hyperledger.fabric.client.identity.X509Identity;
import org.hyperledger.fabric.protos.gateway.ErrorDetail;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
public class App {
private static final String mspID = "Org1MSP";
private static final String channelName = "mychannel";
private static final String chaincodeName = "basic";
public static String assetId = "asset" + Instant.now().toEpochMilli();
// Path to crypto materials.
private static final Path cryptoPath = Paths.get("..", "..", "test-network", "organizations", "peerOrganizations",
"org1.example.com");
// Path to user certificate.
private static final Path certPath = cryptoPath
.resolve(Paths.get("users", "User1@org1.example.com", "msp", "signcerts", "cert.pem"));
// Path to user private key directory.
private static final Path keyPath = cryptoPath
.resolve(Paths.get("users", "User1@org1.example.com", "msp", "keystore"));
// Path to peer tls certificate.
private static final Path tlsCertPath = cryptoPath
.resolve(Paths.get("peers", "peer0.org1.example.com", "tls", "ca.crt"));
// Gateway peer end point.
public static String peerEndpoint = "localhost:7051";
public static String overrideAuth = "peer0.org1.example.com";
public static void main(String[] args)
throws Exception {
// The gRPC client connection should be shared by all Gateway connections to
// this endpoint.
ManagedChannel channel = newGrpcConnection();
Gateway.Builder builder = Gateway.newInstance().identity(newIdentity()).signer(newSigner()).connection(channel)
// Default timeouts for different gRPC calls
.evaluateOptions(CallOption.deadlineAfter(5, TimeUnit.SECONDS))
.endorseOptions(CallOption.deadlineAfter(15, TimeUnit.SECONDS))
.submitOptions(CallOption.deadlineAfter(5, TimeUnit.SECONDS))
.commitStatusOptions(CallOption.deadlineAfter(1, TimeUnit.MINUTES));
try (Gateway gateway = builder.connect()) {
// Get a network instance representing the channel where the smart contract is
// deployed.
Network network = gateway.getNetwork(channelName);
// Get the smart contract from the network.
Contract contract = network.getContract(chaincodeName);
// Initialize a set of asset data on the ledger using the chaincode 'InitLedger'
// function.
initLedger(contract);
// Return all the current assets on the ledger.
getAllAssets(contract);
// Create a new asset on the ledger.
createAsset(contract);
// Update an existing asset asynchronously.
transferAssetAsync(contract);
// Get the asset details by assetID.
readAssetById(contract);
// Update an asset which does not exist.
updateNonExistentAsset(contract);
} finally {
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}
}
private static ManagedChannel newGrpcConnection() throws IOException, CertificateException {
Reader tlsCertReader = Files.newBufferedReader(tlsCertPath);
X509Certificate tlsCert = Identities.readX509Certificate(tlsCertReader);
return NettyChannelBuilder.forTarget(peerEndpoint)
.sslContext(GrpcSslContexts.forClient().trustManager(tlsCert).build()).overrideAuthority(overrideAuth)
.build();
}
private static Identity newIdentity() throws IOException, CertificateException {
Reader certReader = Files.newBufferedReader(certPath);
X509Certificate certificate = Identities.readX509Certificate(certReader);
return new X509Identity(mspID, certificate);
}
private static Signer newSigner() throws IOException, InvalidKeyException {
File dir = new File(keyPath.toString());
File[] listOfFiles = dir.listFiles();
Path path = Paths.get(listOfFiles[0].getPath());
Reader keyReader = Files.newBufferedReader(path);
PrivateKey privateKey = Identities.readPrivateKey(keyReader);
return Signers.newPrivateKeySigner(privateKey);
}
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private static String prettyJson(byte[] json) {
return prettyJson(new String(json, StandardCharsets.UTF_8));
}
private static String prettyJson(String json) {
JsonElement parsedJson = JsonParser.parseString(json);
return gson.toJson(parsedJson);
}
/**
* This type of transaction would typically only be run once by an application
* the first time it was started after its initial deployment. A new version of
* the chaincode deployed later would likely not need to run an "init" function.
*/
private static void initLedger(Contract contract) throws GatewayException, CommitException {
System.out.println(
"\n" + "--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger");
contract.submitTransaction("InitLedger");
System.out.println("*** Transaction committed successfully");
}
/**
* Evaluate a transaction to query ledger state.
*/
private static void getAllAssets(Contract contract) throws GatewayException {
System.out.println(
"\n" + "--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger");
byte[] result = contract.evaluateTransaction("GetAllAssets");
System.out.println("*** Result: " + prettyJson(result));
}
/**
* Submit a transaction synchronously, blocking until it has been committed to
* the ledger.
*/
private static void createAsset(Contract contract) throws GatewayException, CommitException {
System.out.println("\n"
+ "--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments");
contract.submitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300");
System.out.println("*** Transaction committed successfully");
}
/**
* Submit transaction asynchronously, allowing the application to process the
* smart contract response (e.g. update a UI) while waiting for the commit
* notification.
*/
private static void transferAssetAsync(Contract contract) throws GatewayException {
System.out.println("\n" + "--> Async Submit Transaction: TransferAsset, updates existing asset owner");
SubmittedTransaction commit = contract.newProposal("TransferAsset")
.addArguments(assetId, "Saptha")
.build()
.endorse()
.submitAsync();
byte[] result = commit.getResult();
String oldOwner = new String(result, StandardCharsets.UTF_8);
System.out.println(
"*** Successfully submitted transaction to transfer ownership from " + oldOwner + " to Saptha");
System.out.println("*** Waiting for transaction commit");
Status status = commit.getStatus();
if (!status.isSuccessful()) {
throw new RuntimeException("Transaction " + status.getTransactionId() + " failed to commit with status code "
+ status.getCode());
}
System.out.println("*** Transaction committed successfully");
}
private static void readAssetById(Contract contract) throws GatewayException {
System.out.println("\n" + "--> Evaluate Transaction: ReadAsset, function returns asset attributes");
byte[] evaluateResult = contract.evaluateTransaction("ReadAsset", assetId);
System.out.println("*** Result:" + prettyJson(evaluateResult));
}
/**
* submitTransaction() will throw an error containing details of any error
* responses from the smart contract.
*/
private static void updateNonExistentAsset(Contract contract) {
try {
System.out.println("\n"
+ "--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error");
contract.submitTransaction("UpdateAsset", "asset70", "blue", "5", "Tomoko", "300");
System.out.println("******** FAILED to return an error");
} catch (EndorseException | SubmitException | CommitStatusException e) {
System.out.println("*** Successfully caught the error: ");
e.printStackTrace(System.out);
if (!e.getDetails().isEmpty()) {
System.out.println("\n" + "Error Details: ");
for (ErrorDetail detail : e.getDetails()) {
System.out.println("address: " + detail.getAddress() + ", mspId: " + detail.getMspId()
+ ", message: " + detail.getMessage());
}
}
System.out.println("Transaction ID: " + e.getTransactionId());
} catch (CommitException e) {
System.out.println("*** Successfully caught the error: " + e);
e.printStackTrace(System.out);
System.out.println("Transaction ID: " + e.getTransactionId() + " status code: " + e.getCode());
}
}
}

View file

@ -18,9 +18,8 @@ ext {
} }
repositories { repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here. // You can declare any Maven/Ivy/file repository here.
jcenter() mavenCentral()
} }
dependencies { dependencies {

View file

@ -24,10 +24,7 @@ dependencies {
} }
repositories { repositories {
maven { mavenCentral()
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
jcenter()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -1,90 +1,71 @@
# Asset Transfer Events Sample # Asset transfer events sample
The asset transfer events sample demonstrates chaincode events send/receive The asset transfer events sample demonstrates:
and the receive of block events. The chaincode events are set by your
chaincode which adds the event data to the transaction and are sent when the - Emitting chaincode events from smart contract transaction functions.
transaction is committed to the ledger. The block events are published when - Receiving chaincode events in a client application.
a block is committed to the ledger, containing all the transaction details - Replaying previous chaincode events in a client application.
within that block.
Events are published when a block is committed to the ledger.
For more information about event services on per-channel basis, visit the For more information about event services on per-channel basis, visit the
[Channel-based event service](https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html) [Channel-based event service](https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html)
page in the Fabric documentation. page in the Fabric documentation.
## About the Sample ## About the sample
This sample includes chaincodes and application code in multiple languages. This sample includes smart contract and application code in multiple languages. In a use-case similar to basic asset transfer (see [asset-transfer-basic](../asset-transfer-basic) folder) this sample shows sending and receiving of events during create / update / delete of an asset, and during transfer of an asset to a new owner.
In a use-case similar to basic asset transfer ( see `../asset-transfer-basic` folder)
this sample shows sending and receiving of events during create/update/delete of an asset
and during transfer of an asset to a new owner.
### Application ### Application
The application demonstrates this, using two types of listeners in subsequent sections of `main` function:
1. Contract Listener: listen for events in a specific Contract
- How to register a contract listener in an application, for chaincode events
- How to get the chaincode event name and value from the chaincode event
- How to retrieve the transaction and block information from the chaincode event
2. Block Listener: listen for block level events and parse private-data events Follow the execution flow in the client application code, and corresponding output on running the application. Pay attention to the sequence of:
- How to register a block listener for full block events
- How to retrieve the transaction and block information from the block event
- How to register to receive private data associated with transactions, when registering a block listener
- How to retrieve the private data collection details from the full block event
- This section also shows how to connect to a Gateway with listener that will not listen for commit events. This may be useful when the application does not want to wait for the peer to commit blocks and notify the application.
- Transaction invocations (console output like "**--> Submit transaction**").
- Events received by the application (console output like "**<-- Chaincode event received**").
Follow the comments in `application-javascript/app.js` file, and corresponding output on running this application. Notice that events will be received by the listener after the application code submits the transaction and it is committed to the ledger, but during other application activity unrelated to the event.
Pay attention to the sequence of
- smart contract calls (console output like `--> Submit Transaction or --> Evaluate`)
- the events received at application end (console output like `<-- Contract Event Received: or <-- Block Event Received`)
The listener will be notified of an event asynchronously. Notice that events will
be posted by the listener after the application code sends the transaction (or after the
change is committed to the ledger), but during other application activity unrelated to the event.
### Smart Contract ### Smart Contract
The smart contract implements (in folder `chaincode-xyz`) following functions to support the application:
The smart contract (in folder `chaincode-xyz`) implements the following functions to support the application:
- CreateAsset - CreateAsset
- ReadAsset - ReadAsset
- UpdateAsset - UpdateAsset
- DeleteAsset - DeleteAsset
- TransferAsset - TransferAsset
Note that the asset transfer implemented by the smart contract is a simplified scenario, without ownership validation, meant only to Note that the asset transfer implemented by the smart contract is a simplified scenario, without ownership validation, meant only to demonstrate the use of sending and receiving events.
demonstrate the use of sending and receiving events.
## Running the sample ## Running the sample
Like other samples, we will use the Fabric test network to deploy and run ths sample. Follow these step in order. Like other samples, the Fabric test network is used to deploy and run this sample. Follow these steps in order:
- Create the test network and a channel
```
cd test-network
./network.sh up createChannel -c mychannel -ca
```
- Deploy the chaincode (smart contract) 1. Create the test network and a channel (from the `test-network` folder).
``` ```
# to deploy javascript version ./network.sh up createChannel -c mychannel -ca
./network.sh deployCC -ccs 1 -ccv 1 -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -ccl javascript -ccp ./../asset-transfer-events/chaincode-javascript/ -ccn asset-transfer-events-javascript ```
# or to deploy java version 1. Deploy one of the smart contract implementations (from the `test-network` folder).
./network.sh deployCC -ccs 1 -ccv 1 -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -ccl java -ccp ./../asset-transfer-events/chaincode-java/ -ccn asset-transfer-events-java ```
``` # To deploy the JavaScript chaincode implementation
./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-javascript/ -ccl javascript -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
- Run the application # To deploy the Java chaincode implementation
``` ./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-java/ -ccl java -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
cd application-javascript ```
npm install
# ensure this line in app.js have correct chaincode deploy name
# const chaincodeName = '...';
node app.js
```
1. Run the application (from the `asset-transfer-events` folder).
```
# To run the Go sample application
cd application-gateway-go
go run .
```
## Clean up ## Clean up
When you are finished, you can bring down the test network. The command will remove all the nodes of the test network, and delete any ledger data that you created:
When you are finished, you can bring down the test network (from the `test-network` folder). The command will remove all the nodes of the test network, and delete any ledger data that you created.
``` ```
./network.sh down ./network.sh down

View file

@ -0,0 +1,169 @@
/*
Copyright 2022 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/hyperledger/fabric-gateway/pkg/client"
)
const (
channelName = "mychannel"
chaincodeName = "events"
)
var now = time.Now()
var assetID = fmt.Sprintf("asset%d", now.Unix()*1e3+int64(now.Nanosecond())/1e6)
func main() {
clientConnection := newGrpcConnection()
defer clientConnection.Close()
id := newIdentity()
sign := newSign()
gateway, err := client.Connect(
id,
client.WithSign(sign),
client.WithClientConnection(clientConnection),
client.WithEvaluateTimeout(5*time.Second),
client.WithEndorseTimeout(15*time.Second),
client.WithSubmitTimeout(5*time.Second),
client.WithCommitStatusTimeout(1*time.Minute),
)
if err != nil {
panic(err)
}
defer gateway.Close()
network := gateway.GetNetwork(channelName)
contract := network.GetContract(chaincodeName)
// Context used for event listening
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Listen for events emitted by subsequent transactions
startChaincodeEventListening(ctx, network)
firstBlockNumber := createAsset(contract)
updateAsset(contract)
transferAsset(contract)
deleteAsset(contract)
// Replay events from the block containing the first transaction
replayChaincodeEvents(ctx, network, firstBlockNumber)
}
func startChaincodeEventListening(ctx context.Context, network *client.Network) {
fmt.Printf("\n*** Start chaincode event listening\n")
events, err := network.ChaincodeEvents(ctx, chaincodeName)
if err != nil {
panic(fmt.Errorf("failed to start chaincode event listening: %w", err))
}
go func() {
for event := range events {
asset := formatJSON(event.Payload)
fmt.Printf("\n<-- Chaincode event received: %s - %s\n", event.EventName, asset)
}
}()
}
func formatJSON(data []byte) string {
var result bytes.Buffer
if err := json.Indent(&result, data, "", " "); err != nil {
panic(fmt.Errorf("failed to parse JSON: %w", err))
}
return result.String()
}
func createAsset(contract *client.Contract) uint64 {
fmt.Printf("\n--> Submit transaction: CreateAsset, %s owned by Sam with appraised value 100\n", assetID)
_, commit, err := contract.SubmitAsync("CreateAsset", client.WithArguments(assetID, "blue", "10", "Sam", "100"))
if err != nil {
panic(fmt.Errorf("failed to submit transaction: %w", err))
}
status, err := commit.Status()
if err != nil {
panic(fmt.Errorf("failed to get transaction commit status: %w", err))
}
if !status.Successful {
panic(fmt.Errorf("failed to commit transaction with status code %v", status.Code))
}
fmt.Printf("\n*** CreateAsset committed successfully\n")
return status.BlockNumber
}
func updateAsset(contract *client.Contract) {
fmt.Printf("\n--> Submit transaction: UpdateAsset, %s update appraised value to 200\n", assetID)
_, err := contract.SubmitTransaction("UpdateAsset", assetID, "blue", "10", "Sam", "200")
if err != nil {
panic(fmt.Errorf("failed to submit transaction: %w", err))
}
fmt.Printf("\n*** UpdateAsset committed successfully\n")
}
func transferAsset(contract *client.Contract) {
fmt.Printf("\n--> Submit transaction: TransferAsset, %s to Mary\n", assetID)
_, err := contract.SubmitTransaction("TransferAsset", assetID, "Mary")
if err != nil {
panic(fmt.Errorf("failed to submit transaction: %w", err))
}
fmt.Printf("\n*** TransferAsset committed successfully\n")
}
func deleteAsset(contract *client.Contract) {
fmt.Printf("\n--> Submit transaction: DeleteAsset, %s\n", assetID)
_, err := contract.SubmitTransaction("DeleteAsset", assetID)
if err != nil {
panic(fmt.Errorf("failed to submit transaction: %w", err))
}
fmt.Printf("\n*** DeleteAsset committed successfully\n")
}
func replayChaincodeEvents(ctx context.Context, network *client.Network, startBlock uint64) {
fmt.Printf("\n*** Start chaincode event replay\n")
events, err := network.ChaincodeEvents(ctx, chaincodeName, client.WithStartBlock(startBlock))
if err != nil {
panic(fmt.Errorf("failed to start chaincode event listening: %w", err))
}
for {
select {
case <-time.After(10 * time.Second):
panic(errors.New("timeout waiting for event replay"))
case event := <-events:
asset := formatJSON(event.Payload)
fmt.Printf("\n<-- Chaincode event replayed: %s - %s\n", event.EventName, asset)
if event.EventName == "DeleteAsset" {
return
}
}
}
}

View file

@ -0,0 +1,95 @@
/*
Copyright 2022 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"crypto/x509"
"fmt"
"io/ioutil"
"path"
"github.com/hyperledger/fabric-gateway/pkg/identity"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
mspID = "Org1MSP"
cryptoPath = "../../test-network/organizations/peerOrganizations/org1.example.com"
certPath = cryptoPath + "/users/User1@org1.example.com/msp/signcerts/cert.pem"
keyPath = cryptoPath + "/users/User1@org1.example.com/msp/keystore/"
tlsCertPath = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt"
peerEndpoint = "localhost:7051"
gatewayPeer = "peer0.org1.example.com"
)
// newGrpcConnection creates a gRPC connection to the Gateway server.
func newGrpcConnection() *grpc.ClientConn {
certificate, err := loadCertificate(tlsCertPath)
if err != nil {
panic(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(certificate)
transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)
connection, err := grpc.Dial(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
if err != nil {
panic(fmt.Errorf("failed to create gRPC connection: %w", err))
}
return connection
}
// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.
func newIdentity() *identity.X509Identity {
certificate, err := loadCertificate(certPath)
if err != nil {
panic(err)
}
id, err := identity.NewX509Identity(mspID, certificate)
if err != nil {
panic(err)
}
return id
}
func loadCertificate(filename string) (*x509.Certificate, error) {
certificatePEM, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read certificate file: %w", err)
}
return identity.CertificateFromPEM(certificatePEM)
}
// newSign creates a function that generates a digital signature from a message digest using a private key.
func newSign() identity.Sign {
files, err := ioutil.ReadDir(keyPath)
if err != nil {
panic(fmt.Errorf("failed to read private key directory: %w", err))
}
privateKeyPEM, err := ioutil.ReadFile(path.Join(keyPath, files[0].Name()))
if err != nil {
panic(fmt.Errorf("failed to read private key file: %w", err))
}
privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}
sign, err := identity.NewPrivateKeySign(privateKey)
if err != nil {
panic(err)
}
return sign
}

View file

@ -0,0 +1,13 @@
module assetTransfer
go 1.16
require (
github.com/hyperledger/fabric-gateway v1.0.0
github.com/hyperledger/fabric-protos-go v0.0.0-20220125190318-19041b215616 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba // indirect
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 // indirect
google.golang.org/grpc v1.44.0
)

View file

@ -0,0 +1,500 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/sarama v1.27.2 h1:1EyY1dsxNDUQEv0O/4TsjosHI2CgB1uo9H/v56xzTxc=
github.com/Shopify/sarama v1.27.2/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
github.com/cucumber/godog v0.12.1/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6Tm9t5pIc=
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hyperledger/fabric v2.1.1+incompatible h1:cYYRv3vVg4kA6DmrixLxwn1nwBEUuYda8DsMwlaMKbY=
github.com/hyperledger/fabric v2.1.1+incompatible/go.mod h1:tGFAOCT696D3rG0Vofd2dyWYLySHlh0aQjf7Q1HAju0=
github.com/hyperledger/fabric-amcl v0.0.0-20200424173818-327c9e2cf77a h1:JAKZdGuUIjVmES0X31YUD7UqMR2rz/kxLluJuGvsXPk=
github.com/hyperledger/fabric-amcl v0.0.0-20200424173818-327c9e2cf77a/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE=
github.com/hyperledger/fabric-gateway v1.0.0 h1:bki1JYYdQzRGHFArxtgG4wyH6sbFNbYn3PzpdeDfjdk=
github.com/hyperledger/fabric-gateway v1.0.0/go.mod h1:uaRZyC+xzfucPqZIJpesdEsugVvChPhDxZiZmDRSFd4=
github.com/hyperledger/fabric-protos-go v0.0.0-20211118165945-23d738fc3553/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/hyperledger/fabric-protos-go v0.0.0-20220125190318-19041b215616 h1:CZrcDuLxBorn/xvbQl/r9kC0pniDEm0GuiBn9GgfY3c=
github.com/hyperledger/fabric-protos-go v0.0.0-20220125190318-19041b215616/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/sykesm/zap-logfmt v0.0.4 h1:U2WzRvmIWG1wDLCFY3sz8UeEmsdHQjHFNlIdmroVFaI=
github.com/sykesm/zap-logfmt v0.0.4/go.mod h1:AuBd9xQjAe3URrWT1BBDk2v2onAZHkZkWRMiYZXiZWA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba h1:6u6sik+bn/y7vILcYkK3iwTBWN7WtBvB0+SZswQnbf8=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5 h1:zzNejm+EgrbLfDZ6lu9Uud2IVvHySPl8vQzf04laR5Q=
google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View file

@ -44,7 +44,7 @@
// approve, and commit the javascript chaincode, all the actions it takes // approve, and commit the javascript chaincode, all the actions it takes
// to deploy a chaincode to a channel. // to deploy a chaincode to a channel.
// ===> from directory test-network // ===> from directory test-network
// ./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-javacript/ -ccl javascript -ccep "OR('Org1MSP.peer','Org2MSP.peer')" // ./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-javascript/ -ccl javascript -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
// //
// - Be sure that node.js is installed // - Be sure that node.js is installed
// ===> from directory asset-transfer-sbe/application-javascript // ===> from directory asset-transfer-sbe/application-javascript

View file

@ -19,10 +19,7 @@ dependencies {
} }
repositories { repositories {
maven { mavenCentral()
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
jcenter()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -18,9 +18,8 @@ ext {
} }
repositories { repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here. // You can declare any Maven/Ivy/file repository here.
jcenter() mavenCentral()
} }
dependencies { dependencies {

View file

@ -24,10 +24,7 @@ dependencies {
} }
repositories { repositories {
maven { mavenCentral()
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
jcenter()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -27,10 +27,7 @@ dependencies {
} }
repositories { repositories {
maven { mavenCentral()
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
jcenter()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -14,11 +14,7 @@ version '1.0-SNAPSHOT'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
maven {
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -22,10 +22,7 @@ dependencies {
} }
repositories { repositories {
maven { mavenCentral()
url "https://hyperledger.jfrog.io/hyperledger/fabric-maven"
}
jcenter()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'
} }

View file

@ -41,6 +41,15 @@ gradle run
popd popd
stopNetwork stopNetwork
# Run Java application using gateway
createNetwork
print "Initializing Java application"
pushd ../asset-transfer-basic/application-gateway-java
print "Executing Gradle Run"
gradle run
popd
stopNetwork
# Run Javascript application # Run Javascript application
createNetwork createNetwork
print "Initializing Javascript application" print "Initializing Javascript application"

View file

@ -34,3 +34,12 @@ popd
stopNetwork stopNetwork
print "Remove wallet storage" print "Remove wallet storage"
rm -R ../asset-transfer-events/application-javascript/wallet rm -R ../asset-transfer-events/application-javascript/wallet
# Run Go gateway application
createNetwork
print "Initializing Go gateway application"
pushd ../asset-transfer-events/application-gateway-go
print "Executing application"
go run .
popd
stopNetwork

View file

@ -7,7 +7,6 @@ version '0.0.1'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'

View file

@ -12,7 +12,6 @@ version '0.0.1'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'

View file

@ -12,7 +12,6 @@ version '0.0.1'
sourceCompatibility = 1.8 sourceCompatibility = 1.8
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
maven { maven {
url 'https://jitpack.io' url 'https://jitpack.io'

View file

@ -1,6 +1,6 @@
# Running Chaincode as Service with the Test Network # Running Chaincode as Service with the Test Network
The chaincode-as-a-service feature is a very useful and practical way to run 'Smart Contracts'. Traditionally the Fabric Peer has taken on the role of orchestrating the complete lifecycle of the chaincode. It required access to the Docker Daemon to create images, and start containers. Java, NodeJS and Go chaincode frameworks were explicitly known to the peer including how they should be built and started. The chaincode-as-a-service feature is a very useful and practical way to run 'Smart Contracts'. Traditionally the Fabric Peer has taken on the role of orchestrating the complete lifecycle of the chaincode. It required access to the Docker Daemon to create images, and start containers. Java, Node.js and Go chaincode frameworks were explicitly known to the peer including how they should be built and started.
As a result this makes it very hard to deploy into Kubernetes (K8S) style environments, or to run in any form of debug mode. Additionally, the code is being rebuilt by the peer therefore there is some degree of uncertainty about what dependencies have been pulled in. As a result this makes it very hard to deploy into Kubernetes (K8S) style environments, or to run in any form of debug mode. Additionally, the code is being rebuilt by the peer therefore there is some degree of uncertainty about what dependencies have been pulled in.
@ -12,7 +12,7 @@ We need to use the latest 2.4.1 release as this contains some improvements to ma
- The docker image for the peer contains a builder for chaincode-as-a-service preconfigured. This is named 'ccaasbuilder'. This removes the need to build your own external builder and repackage and configure the peer - The docker image for the peer contains a builder for chaincode-as-a-service preconfigured. This is named 'ccaasbuilder'. This removes the need to build your own external builder and repackage and configure the peer
- The `ccaasbuilder` applications are included in the binary tgz archive download for use in other circumstances. The `sampleconfig/core.yaml` is updated as well to refer to 'ccaasbuilder' - The `ccaasbuilder` applications are included in the binary tgz archive download for use in other circumstances. The `sampleconfig/core.yaml` is updated as well to refer to 'ccaasbuilder'
- The 2.4.1 Java Chaincode release has been updated to remove the need to write a custom bootstrap main class, similar to the NodeJS Chaincode. It is intended that this will be added to the go chaincode as well. - The 2.4.1 Java Chaincode release has been updated to remove the need to write a custom bootstrap main class, similar to the Node.js Chaincode. It is intended that this will be added to the go chaincode as well.
## End-to-end with the the test-network ## End-to-end with the the test-network
@ -20,7 +20,7 @@ The `test-network` and some of the chaincodes have been updated to support runni
It's useful to have two terminal windows open, one for starting the Fabric Network, and a second for monitoring all the docker containers. It's useful to have two terminal windows open, one for starting the Fabric Network, and a second for monitoring all the docker containers.
In your 'monitoring' window, run this to watch all activity from the all the docker containers on the `fabric_test` network; this will monitor all the docker containers that are added to the `fabric-test` network. The network is usually created by the `./network.sh up` command, so remember to delay running this until at lest the network is created. It is possible to precreate the network with `docker network create fabric-test` if you wish. In your 'monitoring' window, run this to watch all activity from the all the docker containers on the `fabric_test` network; this will monitor all the docker containers that are added to the `fabric-test` network. The network is usually created by the `./network.sh up` command, so remember to delay running this until at least the network is created. It is possible to precreate the network with `docker network create fabric-test` if you wish.
```bash ```bash
# from the fabric-samples repo # from the fabric-samples repo
@ -50,7 +50,7 @@ This sequence can be run as follows
This is very similar to the `deployCC` command, it needs the name, and path. But also needs to have the port the chaincode container is going use. As each container is on the `fabric-test` network, you might wish to alter this so there are no collisions with other chaincode containers. This is very similar to the `deployCC` command, it needs the name, and path. But also needs to have the port the chaincode container is going use. As each container is on the `fabric-test` network, you might wish to alter this so there are no collisions with other chaincode containers.
You should be able to see the contract starting in the monitoring window. There will be two containers running, one for org1 and one for org2. The container names contain the organzation/peer and the name of the chaincode. You should be able to see the contract starting in the monitoring window. There will be two containers running, one for org1 and one for org2. The container names contain the organization/peer and the name of the chaincode.
To test things are working you can invoke the 'Contract Metadata' function. For information on how to work as different organizations see [Interacting with the network](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html#interacting-with-the-network) To test things are working you can invoke the 'Contract Metadata' function. For information on how to work as different organizations see [Interacting with the network](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html#interacting-with-the-network)
@ -62,6 +62,7 @@ export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051 export CORE_PEER_ADDRESS=localhost:7051
export FABRIC_CFG_PATH=${PWD}/../config
# invoke the function # invoke the function
peer chaincode query -C mychannel -n basicts -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' | jq peer chaincode query -C mychannel -n basicts -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' | jq
@ -78,7 +79,7 @@ To run the Java example, change the `deployCCAAS` command as follows, This will
### Troubleshooting ### Troubleshooting
If the JSON structure passed in is badly formatted JSON this error will be in the peer log If the JSON structure passed in is badly formatted JSON this error will be in the peer log:
``` ```
::Error: Failed to unmarshal json: cannot unmarshal string into Go value of type map[string]interface {} command=build ::Error: Failed to unmarshal json: cannot unmarshal string into Go value of type map[string]interface {} command=build
@ -103,9 +104,9 @@ A sample docker run command could be as follows. The two key variables that are
assettx_ccaas_image:latest assettx_ccaas_image:latest
``` ```
### Nodejs ### Node.js
For NodeJS (JavaScript or TypeScript) chaincode, typically the `package.json` has `fabric-chaincode-node start` as the main start command. To run in the '-as-a-service' mode change this to `fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID` For Node.js (JavaScript or TypeScript) chaincode, typically the `package.json` has `fabric-chaincode-node start` as the main start command. To run in the '-as-a-service' mode change this to `fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID`
### Golang ### Golang
@ -131,7 +132,7 @@ Not starting docker containers; these are the commands we would have run
Depending on your directory, and what you need to debug you might need to adjust these commands. Depending on your directory, and what you need to debug you might need to adjust these commands.
### Building the docker image ### Building the docker image
The first thing needed is to build the docker image. Remember that so long as the peer can connect to the hostname:port given in the `connection.json` the actual packaging of the chaincode is not important to the peer. You are at liberty to adjust the dockerfiles given hgere. The first thing needed is to build the docker image. Remember that so long as the peer can connect to the hostname:port given in the `connection.json` the actual packaging of the chaincode is not important to the peer. You are at liberty to adjust the dockerfiles given here.
To manually build the docker image for the `asset-transfer-basic/chaincode-java` To manually build the docker image for the `asset-transfer-basic/chaincode-java`
@ -164,7 +165,7 @@ For all languages please note:
For Node.js please note: For Node.js please note:
- Port 9229 is forwarded however - this is the debug port used by NodeJS - Port 9229 is forwarded however - this is the debug port used by Node.js
- `-e DEBUG=true` will trigger the node runtime to be started in debug mode. This is encoded in the `docker/docker-entrypoint.sh` script - this is an example and you may wish to remove this in production images for security - `-e DEBUG=true` will trigger the node runtime to be started in debug mode. This is encoded in the `docker/docker-entrypoint.sh` script - this is an example and you may wish to remove this in production images for security
- If you are using typescript, ensure that the typescript has been compiled with sourcemaps, otherwise a debugger will struggle matching up the source code. - If you are using typescript, ensure that the typescript has been compiled with sourcemaps, otherwise a debugger will struggle matching up the source code.
@ -199,6 +200,6 @@ CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG="{\"peername\":\"org1peer1\"}"
The external builder will then resolve this address to be `org1peer1_assettransfer_ccaas:9999` for the peer to use. The external builder will then resolve this address to be `org1peer1_assettransfer_ccaas:9999` for the peer to use.
Each peer can have there own separate configuration, and therefore different addresses. The JSON string that is set can have any structure, so long as the templates (in golang template syntax) match. Each peer can have their own separate configuration, and therefore different addresses. The JSON string that is set can have any structure, so long as the templates (in golang template syntax) match.
Any value in the `connection.json` can be templated - but only the values and not the keys. Any value in the `connection.json` can be templated - but only the values and not the keys.

View file

@ -1,10 +1,8 @@
/* /*
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.example; package org.example;
import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.*; import org.hyperledger.fabric.contract.annotation.*;
@ -16,36 +14,42 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import org.json.JSONObject; import org.json.JSONObject;
import com.google.common.base.Strings;
@Contract(name = "TokenERC20Contract", info = @Info(title = "TokenERC20Contract", description = "A java chaincode for erc20 token", version = "0.0.1-SNAPSHOT")) @Contract(name = "TokenERC20Contract", info = @Info(title = "TokenERC20Contract", description = "A java chaincode for erc20 token", version = "0.0.1-SNAPSHOT"))
@Default @Default
public final class TokenERC20Contract implements ContractInterface { public final class TokenERC20Contract implements ContractInterface {
private static final String BALANCE_PREFIX = "balance";
private static final String ALLOWANCE_PREFIX = "allowance";
private static final String NAME_KEY = "name";
private static final String SYMBOL_KEY = "symbol";
private static final String DECIMALS_KEY = "decimals";
private static final String TOTAL_SUPPLY_KEY = "totalSupply";
private static final String TRANSFER_EVENT = "Transfer";
private static final String FROM = "from";
private static final String TO = "to";
private static final String VALUE= "value";
private static final String ERC20_OWNER_MSPID= "Org1MSP";
final private String balancePrefix = "balance";
final private String allowancePrefix = "allowance";
final private String nameKey = "name";
final private String symbolKey = "symbol";
final private String decimalsKey = "decimals";
final private String totalSupplyKey = "totalSupply";
/** /**
* Return the name of the token - e.g. "MyToken". The original function name is * @Desc Return the name of the token - e.g. "MyToken". The original function name is `name` in
* `name` in ERC20 specification. However, 'name' conflicts with a parameter * ERC20 specification. However, 'name' conflicts with a parameter `name` in `Contract` class. As
* `name` in `Contract` class. As a work around, we use `TokenName` as an * a work around, we use `TokenName` as an alternative function name.
* alternative function name. * @param ctx the transaction context
* * @returns Returns the name of the token
* @param {Context} ctx the transaction context
* @returns {String} Returns the name of the token
*/ */
@Transaction() @Transaction()
public String tokenName(final Context ctx) { public String tokenName(final Context ctx) {
ChaincodeStub stub = ctx.getStub(); String tokenName = ctx.getStub().getStringState(NAME_KEY);
String tokenName = stub.getStringState(nameKey);
if (tokenName.isEmpty()) {
throw new ChaincodeException("Sorry ! Token name not found"); if (Strings.isNullOrEmpty(tokenName)) {
throw new ChaincodeException("Sorry ! Token name not found.");
} }
return tokenName; return tokenName;
@ -53,18 +57,15 @@ public final class TokenERC20Contract implements ContractInterface {
} }
/** /**
* Return the symbol of the token. E.g. HIX. * @Desc Return the symbol of the token. E.g. âœHIXâ??.
* * @param ctx the transaction context
* @param {Context} ctx the transaction context * @returns Returns the symbol of the token
* @returns {String} Returns the symbol of the token
*/ */
@Transaction() @Transaction()
public String tokenSymbol(final Context ctx) { public String tokenSymbol(final Context ctx) {
ChaincodeStub stub = ctx.getStub(); String tokenSymbol = ctx.getStub().getStringState(SYMBOL_KEY);
String tokenSymbol = stub.getStringState(symbolKey); if (Strings.isNullOrEmpty(tokenSymbol)) {
if (tokenSymbol.isEmpty()) { throw new ChaincodeException("Sorry ! Token symbol not found.");
throw new ChaincodeException("Sorry ! Token symbol not found");
} }
return tokenSymbol; return tokenSymbol;
@ -72,37 +73,34 @@ public final class TokenERC20Contract implements ContractInterface {
} }
/** /**
* Return the number of decimals the token uses e.g. 8, means to divide the * @Desc Return the number of decimals the token uses e.g. 8, means to divide the token amount by
* token amount by 100000000 to get its user representation. * 100000000 to get its user representation.
* * @param ctx the transaction context
* @param {Context} ctx the transaction context * @returns Returns the number of decimals
* @returns {Number} Returns the number of decimals
*/ */
@Transaction() @Transaction()
public Integer decimals(final Context ctx) { public int decimals(final Context ctx) {
ChaincodeStub stub = ctx.getStub(); String decimals = ctx.getStub().getStringState(DECIMALS_KEY);
String decimals = stub.getStringState(decimalsKey); if (Strings.isNullOrEmpty(decimals)) {
if (decimals.isEmpty()) {
throw new ChaincodeException("Sorry ! Decimal not found"); throw new ChaincodeException("Sorry ! Decimal not found.");
} }
return Integer.parseInt(decimals); return Integer.parseInt(decimals);
} }
/** /**
* Return the total token supply. * @Desc Return the total token supply.
* * @param ctx the transaction context
* @param {Context} ctx the transaction context * @returns Returns the total token supply
* @returns {Number} Returns the total token supply
*/ */
@Transaction() @Transaction()
public Long totalSupply(final Context ctx) { public long totalSupply(final Context ctx) {
ChaincodeStub stub = ctx.getStub();
String totalSupply = stub.getStringState(totalSupplyKey);
if (totalSupply.isEmpty()) {
throw new ChaincodeException("Sorry ! Total Supply not found"); String totalSupply = ctx.getStub().getStringState(TOTAL_SUPPLY_KEY);
if (Strings.isNullOrEmpty(totalSupply)) {
throw new ChaincodeException("Sorry ! Total Supply not found.");
} }
return Long.parseLong(totalSupply); return Long.parseLong(totalSupply);
} }
@ -110,139 +108,111 @@ public final class TokenERC20Contract implements ContractInterface {
/** /**
* BalanceOf returns the balance of the given account. * BalanceOf returns the balance of the given account.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} owner The owner from which the balance will be retrieved * @param owner The owner from which the balance will be retrieved
* @returns {Number} Returns the account balance * @returns Returns the account balance
*/ */
@Transaction() @Transaction()
public long balanceOf(final Context ctx, final String owner) { public long balanceOf(final Context ctx, final String owner) {
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, owner); CompositeKey balanceKey = ctx.getStub().createCompositeKey(BALANCE_PREFIX, owner);
String balance = stub.getStringState(balanceKey.toString());
String balance = stub.getStringState(balanceKey.toString().trim()); if (Strings.isNullOrEmpty(balance)) {
if (balance == null || balance.isEmpty() || balance.length() == 0) {
String errorMessage = String.format("Balance of the owner %s not exists", owner); String errorMessage = String.format("Balance of the owner %s not exists", owner);
System.out.println(errorMessage);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
return Long.parseLong(balance);
return Long.parseLong(balance.toString());
} }
/** /**
* Transfer transfers tokens from client account to recipient account. recipient * @Desc Transfer transfers tokens from client account to recipient account. recipient account
* account must be a valid clientID as returned by the ClientAccountID() * must be a valid clientID as returned by the ClientAccountID() function.
* function.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} to The recipient * @param to The recipient
* @param {Integer} value The amount of token to be transferred * @param value The amount of token to be transferred
* @returns {Boolean} Return whether the transfer was successful or not * @returns Return whether the transfer was successful or not
*/ */
@Transaction() @Transaction()
public boolean transfer(final Context ctx, final String to, String _value) { public void transfer(final Context ctx, final String to, long _value) {
String from = ctx.getClientIdentity().getId(); String from = ctx.getClientIdentity().getId();
long value = Long.parseLong(_value.trim()); this.doTransfer(ctx, from, to, _value);
boolean transferResp = this.doTransfer(ctx, from, to, value); ctx.getStub().setEvent(TRANSFER_EVENT, new JSONObject().put(FROM, from).put(TO, to)
.put(VALUE, _value).toString().getBytes(UTF_8));
if (!transferResp) {
String errorMessage = String.format("Cannot transfer to and from same client account");
System.out.println(errorMessage);
throw new ChaincodeException(errorMessage);
}
ChaincodeStub stub = ctx.getStub();
JSONObject obj = new JSONObject();
obj.put("from", from);
obj.put("to", to);
obj.put("value", value);
stub.setEvent("Transfer", this.serialize(obj));
return true;
} }
/** /**
* Transfer `value` amount of tokens from `from` to `to`. * Transfer `value` amount of tokens from `from` to `to`.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} from The sender * @param from The sender
* @param {String} to The recipient * @param to The recipient
* @param {Integer} value The amount of token to be transferred * @param value The amount of token to be transferred
* @returns {Boolean} Return whether the transfer was successful or not * @returns Return whether the transfer was successful or not
*/ */
@Transaction() @Transaction()
public boolean transferFrom(Context ctx, final String from, final String to, String _value) { public void transferFrom(Context ctx, final String from, final String to, String _value) {
String spender = ctx.getClientIdentity().getId(); String spender = ctx.getClientIdentity().getId();
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
// Retrieve the allowance of the spender // Retrieve the allowance of the spender
CompositeKey allowanceKey = stub.createCompositeKey(allowancePrefix, from, spender); CompositeKey allowanceKey = stub.createCompositeKey(ALLOWANCE_PREFIX, from, spender);
String currentAllowanceStr = stub.getStringState(allowanceKey.toString().trim()); String currentAllowanceStr = stub.getStringState(allowanceKey.toString());
if (currentAllowanceStr.isBlank() || currentAllowanceStr.length() == 0) { if (Strings.isNullOrEmpty(currentAllowanceStr)) {
String errorMessage = String.format("Spender %s has no allowance from %s", spender, from); String errorMessage = String.format("Spender %s has no allowance from %s", spender, from);
System.out.println(errorMessage);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
long currentAllowance = Long.parseLong(currentAllowanceStr.toString()); long currentAllowance = Long.parseLong(currentAllowanceStr);
// Convert value from string to int // Convert value from string to int
Long valueInt = Long.parseLong(_value); long valueInt = Long.parseLong(_value);
// Check if the transferred value is less than the allowance // Check if the transferred value is less than the allowance
if (currentAllowance < valueInt) { if (currentAllowance < valueInt) {
String errorMessage = String.format("The spender does not have enough allowance to spend."); String errorMessage = String.format("The spender does not have enough allowance to spend.");
System.out.println(errorMessage);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
boolean transferResp = this.doTransfer(ctx, from, to, valueInt); this.doTransfer(ctx, from, to, valueInt);
if (!transferResp) {
throw new ChaincodeException("Failed to transfer");
}
// Decrease the allowance // Decrease the allowance
long updatedAllowance = currentAllowance - valueInt; long updatedAllowance = currentAllowance - valueInt;
stub.putStringState(allowanceKey.toString().trim(), String.valueOf(updatedAllowance)); stub.putStringState(allowanceKey.toString(), String.valueOf(updatedAllowance));
System.out.printf("spender %s allowance updated from %d to %d", spender, currentAllowance, updatedAllowance); stub.setEvent(TRANSFER_EVENT, new JSONObject().put(FROM, from).put(TO, to)
.put(VALUE, valueInt).toString().getBytes(UTF_8));
JSONObject obj = new JSONObject();
obj.put("from", from);
obj.put("to", to);
obj.put("value", valueInt);
stub.setEvent("Transfer", this.serialize(obj));
System.out.println("transferFrom ended successfully");
return true;
} }
@Transaction() private void doTransfer(final Context ctx, final String _from, final String _to, long _value) {
private boolean doTransfer(final Context ctx, final String _from, final String _to, long _value) {
if (_from.equalsIgnoreCase(_to)) { if (_from.equalsIgnoreCase(_to)) {
throw new ChaincodeException("cannot transfer to and from same client account"); throw new ChaincodeException("cannot transfer to and from same client account");
} }
if (_value < 0) { // transfer of 0 is allowed in ERC20, so just validate against negative amounts if (_value < 0) { // transfer of 0 is allowed in ERC20, so just validate against negative
// amounts
throw new ChaincodeException("transfer amount cannot be negative"); throw new ChaincodeException("transfer amount cannot be negative");
} }
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
// Retrieve the current balance of the sender // Retrieve the current balance of the sender
CompositeKey fromBalanceKey = stub.createCompositeKey(balancePrefix, _from.trim()); CompositeKey fromBalanceKey = stub.createCompositeKey(BALANCE_PREFIX, _from);
String fromCurrentBalance = stub.getStringState(fromBalanceKey.toString().trim());
if (fromCurrentBalance.isBlank() || fromCurrentBalance.length() == 0) { String fromCurrentBalance = stub.getStringState(fromBalanceKey.toString());
if (Strings.isNullOrEmpty(fromCurrentBalance)) {
String errorMessage = String.format("client account %s has no balance", _from); String errorMessage = String.format("client account %s has no balance", _from);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
long _fromCurrentBalance = Long.parseLong(fromCurrentBalance.toString().trim()); long _fromCurrentBalance = Long.parseLong(fromCurrentBalance.toString());
// Check if the sender has enough tokens to spend. // Check if the sender has enough tokens to spend.
if (_fromCurrentBalance < _value) { if (_fromCurrentBalance < _value) {
@ -251,13 +221,13 @@ public final class TokenERC20Contract implements ContractInterface {
} }
// Retrieve the current balance of the recepient // Retrieve the current balance of the recepient
CompositeKey toBalanceKey = stub.createCompositeKey(balancePrefix, _to); CompositeKey toBalanceKey = stub.createCompositeKey(BALANCE_PREFIX, _to);
String toCurrentBalance = stub.getStringState(toBalanceKey.toString().trim()); String toCurrentBalance = stub.getStringState(toBalanceKey.toString());
long _toCurrentBalance = 0; long _toCurrentBalance = 0;
// If recipient current balance doesn't yet exist, we'll create it with a // If recipient current balance doesn't yet exist, we'll create it with a
// current balance of 0 // current balance of 0
if (toCurrentBalance.isBlank() || toCurrentBalance.length() == 0) { if (Strings.isNullOrEmpty(toCurrentBalance)) {
_toCurrentBalance = 0; _toCurrentBalance = 0;
} else { } else {
_toCurrentBalance = Long.parseLong(toCurrentBalance.trim()); _toCurrentBalance = Long.parseLong(toCurrentBalance.trim());
@ -267,51 +237,40 @@ public final class TokenERC20Contract implements ContractInterface {
long fromUpdatedBalance = _fromCurrentBalance - _value; long fromUpdatedBalance = _fromCurrentBalance - _value;
long toUpdatedBalance = _toCurrentBalance + _value; long toUpdatedBalance = _toCurrentBalance + _value;
stub.putStringState(fromBalanceKey.toString().trim(), String.valueOf(fromUpdatedBalance)); stub.putStringState(fromBalanceKey.toString(), String.valueOf(fromUpdatedBalance));
stub.putStringState(toBalanceKey.toString().trim(), String.valueOf(toUpdatedBalance)); stub.putStringState(toBalanceKey.toString(), String.valueOf(toUpdatedBalance));
System.out.printf("client %s balance updated from %d to %d", _from, _fromCurrentBalance, fromUpdatedBalance);
System.out.printf("recipient %s balance updated from %d to %d", _to, _toCurrentBalance, toUpdatedBalance);
return true;
} }
/** /**
* Allows `spender` to spend `value` amount of tokens from the owner. * @Desc Allows `spender` to spend `value` amount of tokens from the owner.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} spender The spender * @param spender The spender
* @param {Integer} value The amount of tokens to be approved for transfer * @param value The amount of tokens to be approved for transfer
* @returns {Boolean} Return whether the approval was successful or not * @returns Return whether the approval was successful or not
*/ */
@Transaction() @Transaction()
public boolean approve(final Context ctx, final String spender, final String value) { public void approve(final Context ctx, final String spender, final String value) {
String owner = ctx.getClientIdentity().getId(); String owner = ctx.getClientIdentity().getId();
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
CompositeKey allowanceKey = stub.createCompositeKey(allowancePrefix, owner, spender); CompositeKey allowanceKey = stub.createCompositeKey(ALLOWANCE_PREFIX, owner, spender);
long valueInt = Long.parseLong(value); long valueInt = Long.parseLong(value);
stub.putStringState(allowanceKey.toString().trim(), String.valueOf(valueInt)); stub.putStringState(allowanceKey.toString(), String.valueOf(valueInt));
JSONObject obj = new JSONObject(); stub.setEvent("Approval", new JSONObject().put("owner", owner).put("spender", spender)
obj.put("owner", owner); .put(VALUE, valueInt).toString().getBytes(UTF_8));
obj.put("spender", spender);
obj.put("value", valueInt);
stub.setEvent("Approval", this.serialize(obj));
System.out.println("Approve ended successfully");
return true;
} }
/** /**
* Returns the amount of tokens which `spender` is allowed to withdraw from * @Desc Returns the amount of tokens which `spender` is allowed to withdraw from `owner`.
* `owner`.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} owner The owner of tokens * @param owner The owner of tokens
* @param {String} spender The spender who are able to transfer the tokens * @param spender The spender who are able to transfer the tokens
* @returns {Number} Return the amount of remaining tokens allowed to spent * @returns Return the amount of remaining tokens allowed to spent
*/ */
@Transaction() @Transaction()
@ -319,90 +278,90 @@ public final class TokenERC20Contract implements ContractInterface {
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
CompositeKey allowanceKey = stub.createCompositeKey(allowancePrefix, owner, spender); CompositeKey allowanceKey = stub.createCompositeKey(ALLOWANCE_PREFIX, owner, spender);
String allowanceBytes = stub.getStringState(allowanceKey.toString().trim()); String allowanceBytes = stub.getStringState(allowanceKey.toString());
if (allowanceBytes.isBlank() || allowanceBytes.length() == 0) { if (Strings.isNullOrEmpty(allowanceBytes)) {
String errorMessage = String.format("spender account %s has no allowance from", spender, owner); String errorMessage = String.format("spender account %s has no allowance from", spender,
owner);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
long allowance = Long.parseLong(allowanceBytes.toString().trim()); long allowance = Long.parseLong(allowanceBytes);
return allowance; return allowance;
} }
/** /**
* Set optional infomation for a token. * @Desc Set optional information for a token.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {String} name The name of the token * @param name The name of the token
* @param {String} symbol The symbol of the token * @param symbol The symbol of the token
* @param {String} decimals The decimals of the token * @param decimals The decimals of the token
* @param {String} totalSupply The totalSupply of the token * @param totalSupply The totalSupply of the token
*/ */
@Transaction() @Transaction()
public boolean setOptions(final Context ctx, final String name, final String symbol, final String decimals) { public void setOptions(final Context ctx, final String name, final String symbol,
final String decimals) {
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
stub.putStringState(nameKey, name); stub.putStringState(NAME_KEY, name);
stub.putStringState(symbolKey, symbol); stub.putStringState(SYMBOL_KEY, symbol);
stub.putStringState(decimalsKey, decimals); stub.putStringState(DECIMALS_KEY, decimals);
System.out.printf("name:%s, symbol: %s, decimals: %s", name, symbol, decimals);
return true;
} }
/** /**
* Mint creates new tokens and adds them to minter's account balance * Mint creates new tokens and adds them to minter's account balance
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @param {Integer} amount amount of tokens to be minted * @param amount amount of tokens to be minted
* @returns {Object} The balance * @returns The balance
*/ */
@Transaction() @Transaction()
public boolean mint(final Context ctx, final String amount) { public void mint(final Context ctx, final String amount) {
// Check minter authorization - this sample assumes Org1 is the central banker // Check minter authorization - this sample assumes Org1 is the central banker
// with privilege to mint new tokens // with privilege to mint new tokens
String clientMSPID = ctx.getClientIdentity().getMSPID(); String clientMSPID = ctx.getClientIdentity().getMSPID();
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (!clientMSPID.equalsIgnoreCase("Org1MSP")) { if (!clientMSPID.equalsIgnoreCase(ERC20_OWNER_MSPID)) {
throw new ChaincodeException("client is not authorized to mint new tokens"); throw new ChaincodeException("Client is not authorized to mint new tokens");
} }
// Get ID of submitting client identity // Get ID of submitting client identity
String minter = ctx.getClientIdentity().getId(); String minter = ctx.getClientIdentity().getId();
long amountInt = Long.parseLong(amount.trim()); long amountInt = Long.parseLong(amount.trim());
if (amountInt <= 0) { if (amountInt <= 0) {
throw new ChaincodeException("mint amount must be a positive integer"); throw new ChaincodeException("Mint amount must be a positive integer");
} }
CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, minter); CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX, minter);
String currentBalanceBytes = stub.getStringState(balanceKey.toString().trim()); String currentBalanceBytes = stub.getStringState(balanceKey.toString());
// If minter current balance doesn't yet exist, we'll create it with a current // If minter current balance doesn't yet exist, we'll create it with a current
// balance of 0 // balance of 0
long currentBalance = 0; long currentBalance = 0;
if (currentBalanceBytes.isBlank() || currentBalanceBytes.length() == 0) { if (Strings.isNullOrEmpty(currentBalanceBytes)) {
currentBalance = 0; currentBalance = 0;
} else { } else {
currentBalance = Long.parseLong(currentBalanceBytes.toString()); currentBalance = Long.parseLong(currentBalanceBytes);
} }
long updatedBalance = currentBalance + amountInt; long updatedBalance = currentBalance + amountInt;
stub.putStringState(balanceKey.toString().trim(), String.valueOf(updatedBalance)); stub.putStringState(balanceKey.toString(), String.valueOf(updatedBalance));
// Increase totalSupply // Increase totalSupply
String totalSupplyBytes = stub.getStringState(totalSupplyKey.trim()); String totalSupplyBytes = stub.getStringState(TOTAL_SUPPLY_KEY);
long totalSupply = 0; long totalSupply = 0;
if (totalSupplyBytes.isBlank() || totalSupplyBytes.length() == 0) { if (Strings.isNullOrEmpty(totalSupplyBytes)) {
System.out.println("Initialize the tokenSupply");
totalSupply = 0; totalSupply = 0;
} else { } else {
@ -411,34 +370,26 @@ public final class TokenERC20Contract implements ContractInterface {
} }
totalSupply = totalSupply + amountInt; totalSupply = totalSupply + amountInt;
stub.putStringState(totalSupplyKey.trim(), String.valueOf(totalSupply)); stub.putStringState(TOTAL_SUPPLY_KEY, String.valueOf(totalSupply));
stub.setEvent(TRANSFER_EVENT, new JSONObject().put(FROM, "0x0").put(TO, minter)
.put(VALUE, amountInt).toString().getBytes(UTF_8));
JSONObject obj = new JSONObject();
obj.put("from", "0x0");
obj.put("to", minter);
obj.put("value", amountInt);
stub.setEvent("Transfer", this.serialize(obj));
// System.out.printf("minter account %s balance updated from %d to %d",minter,
// currentBalance ,updatedBalance);
return true;
} }
/** /**
* Burn redeem tokens from minter's account balance * @Desc Burn redeem tokens from minter's account balance.
* * @param ctx the transaction context
* @param {Context} ctx the transaction context * @param amount amount of tokens to be burned
* @param {Integer} amount amount of tokens to be burned * @returns The balance
* @returns {Object} The balance
*/ */
@Transaction() @Transaction()
public boolean burn(final Context ctx, final String amount) { public void burn(final Context ctx, final String amount) {
// Check minter authorization - this sample assumes Org1 is the central banker // Check minter authorization - this sample assumes Org1 is the central banker
// with privilege to burn tokens // with privilege to burn tokens
String clientMSPID = ctx.getClientIdentity().getMSPID(); String clientMSPID = ctx.getClientIdentity().getMSPID();
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (!clientMSPID.equalsIgnoreCase("Org1MSP")) { if (!clientMSPID.equalsIgnoreCase(ERC20_OWNER_MSPID)) {
throw new ChaincodeException("client is not authorized to mint new tokens"); throw new ChaincodeException("client is not authorized to mint new tokens");
} }
@ -446,41 +397,37 @@ public final class TokenERC20Contract implements ContractInterface {
long amountInt = Long.parseLong(amount); long amountInt = Long.parseLong(amount);
CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, minter); CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX, minter);
String currentBalanceBytes = stub.getStringState(balanceKey.toString().trim()); String currentBalanceBytes = stub.getStringState(balanceKey.toString());
if (currentBalanceBytes.isBlank() || currentBalanceBytes.length() == 0) { if (Strings.isNullOrEmpty(currentBalanceBytes)) {
throw new ChaincodeException("The balance does not exist"); throw new ChaincodeException("The balance does not exist");
} }
long currentBalance = Long.valueOf(currentBalanceBytes.toString()); long currentBalance = Long.valueOf(currentBalanceBytes);
long updatedBalance = currentBalance - amountInt; long updatedBalance = currentBalance - amountInt;
stub.putStringState(balanceKey.toString().trim(), String.valueOf(updatedBalance)); stub.putStringState(balanceKey.toString(), String.valueOf(updatedBalance));
// Decrease totalSupply // Decrease totalSupply
String totalSupplyBytes = stub.getStringState(totalSupplyKey.toString().trim()); String totalSupplyBytes = stub.getStringState(TOTAL_SUPPLY_KEY);
if (totalSupplyBytes.isBlank() || totalSupplyBytes.length() == 0) { if (Strings.isNullOrEmpty(totalSupplyBytes)) {
throw new ChaincodeException("totalSupply does not exist."); throw new ChaincodeException("totalSupply does not exist.");
} }
long totalSupply = Long.parseLong(totalSupplyBytes.toString()) - amountInt; long totalSupply = Long.parseLong(totalSupplyBytes.toString()) - amountInt;
stub.putStringState(totalSupplyKey.toString().trim(), String.valueOf(totalSupply)); stub.putStringState(TOTAL_SUPPLY_KEY, String.valueOf(totalSupply));
// Emit the Transfer event // Emit the Transfer event
JSONObject obj = new JSONObject(); stub.setEvent(TRANSFER_EVENT, new JSONObject().put(FROM, minter).put(TO, "0x0")
obj.put("from", minter); .put(VALUE, amountInt).toString().getBytes(UTF_8));
obj.put("to", "0x0");
obj.put("value", amountInt);
stub.setEvent("Transfer", this.serialize(obj));
System.out.printf("minter account %s balance updated from %d to %d", minter, currentBalance, updatedBalance);
return true;
} }
/** /**
* ClientAccountBalance returns the balance of the requesting client's account. * @Desc: ClientAccountBalance returns the balance of the requesting client's account.
* *
* @param {Context} ctx the transaction context * @param ctx the transaction context
* @returns {Number} Returns the account balance * @returns Returns the account balance
*/ */
@Transaction() @Transaction()
@ -488,22 +435,25 @@ public final class TokenERC20Contract implements ContractInterface {
// Get ID of submitting client identity // Get ID of submitting client identity
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String clientAccountID = ctx.getClientIdentity().getId(); String clientAccountID = ctx.getClientIdentity().getId();
CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, clientAccountID); CompositeKey balanceKey = stub.createCompositeKey(BALANCE_PREFIX, clientAccountID);
String balanceBytes = stub.getStringState(balanceKey.toString().trim()); String balanceBytes = stub.getStringState(balanceKey.toString());
if (balanceBytes.isBlank() || balanceBytes.length() == 0) { if (Strings.isNullOrEmpty(balanceBytes)) {
String errorMessage = String.format("the account %s does not exist", clientAccountID); String errorMessage = String.format("the account %s does not exist", clientAccountID);
throw new ChaincodeException(errorMessage); throw new ChaincodeException(errorMessage);
} }
long balance = Long.parseLong(balanceBytes.trim()); long balance = Long.parseLong(balanceBytes);
return balance; return balance;
} }
// ClientAccountID returns the id of the requesting client's account. /**
// In this implementation, the client account ID is the clientId itself. * @Desc: ClientAccountID returns the id of the requesting client's account. In this
// Users can use this function to get their own account id, which they can then * implementation, the client account ID is the clientId itself. Users can use this function to
// give to others as the payment address * get their own account id, which they can then give to others as the payment address.
*
*/
@Transaction() @Transaction()
public String getClientAccountID(final Context ctx) { public String getClientAccountID(final Context ctx) {
// Get ID of submitting client identity // Get ID of submitting client identity
@ -511,9 +461,4 @@ public final class TokenERC20Contract implements ContractInterface {
return clientAccountID; return clientAccountID;
} }
private byte[] serialize(Object object) {
String jsonStr = new JSONObject(object).toString();
return jsonStr.getBytes(UTF_8);
}
} }

View file

@ -4,7 +4,6 @@
*/ */
package org.example; package org.example;
import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ClientIdentity; import org.hyperledger.fabric.contract.ClientIdentity;
@ -21,7 +20,6 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -29,7 +27,6 @@ import static org.mockito.Mockito.when;
public class TokenERC20ContractTest { public class TokenERC20ContractTest {
final private String balancePrefix = "balance"; final private String balancePrefix = "balance";
final private String nameKey = "name"; final private String nameKey = "name";
final private String symbolKey = "symbol"; final private String symbolKey = "symbol";
final private String decimalsKey = "decimals"; final private String decimalsKey = "decimals";
@ -55,7 +52,7 @@ public class TokenERC20ContractTest {
@Test @Test
public void whenTokenNameDoesNotExist() { public void whenTokenNameDoesNotExist() {
TokenERC20Contract contract = new TokenERC20Contract(); TokenERC20Contract contract = new TokenERC20Contract();
Context ctx = mock(Context.class); final Context ctx = mock(Context.class);
ChaincodeStub stub = mock(ChaincodeStub.class); ChaincodeStub stub = mock(ChaincodeStub.class);
when(ctx.getStub()).thenReturn(stub); when(ctx.getStub()).thenReturn(stub);
when(stub.getStringState(nameKey)).thenReturn(""); when(stub.getStringState(nameKey)).thenReturn("");
@ -65,7 +62,7 @@ public class TokenERC20ContractTest {
}); });
assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
.hasMessage("Sorry ! Token name not found"); .hasMessage("Sorry ! Token name not found.");
} }
@Test @Test
@ -95,7 +92,7 @@ public class TokenERC20ContractTest {
}); });
assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
.hasMessage("Sorry ! Token symbol not found"); .hasMessage("Sorry ! Token symbol not found.");
} }
@Test @Test
@ -125,7 +122,7 @@ public class TokenERC20ContractTest {
}); });
assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
.hasMessage("Sorry ! Decimal not found"); .hasMessage("Sorry ! Decimal not found.");
} }
@Test @Test
@ -155,7 +152,7 @@ public class TokenERC20ContractTest {
}); });
assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause() assertThat(thrown).isInstanceOf(ChaincodeException.class).hasNoCause()
.hasMessage("Sorry ! Total Supply not found"); .hasMessage("Sorry ! Total Supply not found.");
} }
@Test @Test
@ -201,13 +198,12 @@ public class TokenERC20ContractTest {
when(ctx.getClientIdentity()).thenReturn(identity); when(ctx.getClientIdentity()).thenReturn(identity);
when(ctx.getStub()).thenReturn(stub); when(ctx.getStub()).thenReturn(stub);
boolean returnValue = contract.mint(ctx, "1000"); contract.mint(ctx, "1000");
assertThat(returnValue).isEqualTo(true); String totalSupply = stub.getStringState(totalSupplyKey);
String totalSupply = stub.getStringState(totalSupplyKey.trim());
assertThat(totalSupply).isEqualTo("1000"); assertThat(totalSupply).isEqualTo("1000");
String minter = ctx.getClientIdentity().getId(); String minter = ctx.getClientIdentity().getId();
CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, minter); CompositeKey balanceKey = stub.createCompositeKey(balancePrefix, minter);
String updatedBalance = stub.getStringState(balanceKey.toString().trim()); String updatedBalance = stub.getStringState(balanceKey.toString());
assertThat(updatedBalance).isEqualTo("1000"); assertThat(updatedBalance).isEqualTo("1000");
} }
@ -221,20 +217,20 @@ public class TokenERC20ContractTest {
final ClientIdentity identity = new ClientIdentity(stub); final ClientIdentity identity = new ClientIdentity(stub);
when(ctx.getClientIdentity()).thenReturn(identity); when(ctx.getClientIdentity()).thenReturn(identity);
when(ctx.getStub()).thenReturn(stub); when(ctx.getStub()).thenReturn(stub);
boolean returnValue = contract.mint(ctx, "1000"); contract.mint(ctx, "1000");
String minter = ctx.getClientIdentity().getId(); String minter = ctx.getClientIdentity().getId();
String _to = "x509::CN=User1@org2.example.com, L=San Francisco, ST=California," String _to = "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"; + " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
boolean transferResult = contract.transfer(ctx, _to, "100"); contract.transfer(ctx, _to, 100);
CompositeKey toBalanceKey = stub.createCompositeKey(balancePrefix, _to); CompositeKey toBalanceKey = stub.createCompositeKey(balancePrefix, _to);
String _toCurrentBalance = stub.getStringState(toBalanceKey.toString().trim()); String _toCurrentBalance = stub.getStringState(toBalanceKey.toString());
Long totalSupply = contract.totalSupply(ctx); Long totalSupply = contract.totalSupply(ctx);
Long fromBalance = contract.balanceOf(ctx, minter); Long fromBalance = contract.balanceOf(ctx, minter);
((ChaincodeStubNaiveImpl) stub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS); ((ChaincodeStubNaiveImpl) stub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS);
Long _toBalance = contract.balanceOf(ctx, _to); Long _toBalance = contract.balanceOf(ctx, _to);
assertThat(transferResult).isEqualTo(true);
assertThat(returnValue).isEqualTo(true);
assertThat(totalSupply).isEqualTo(1000); assertThat(totalSupply).isEqualTo(1000);
assertThat(_toCurrentBalance).isEqualTo("100"); assertThat(_toCurrentBalance).isEqualTo("100");
assertThat(fromBalance).isEqualTo(900); assertThat(fromBalance).isEqualTo(900);
@ -251,13 +247,11 @@ public class TokenERC20ContractTest {
final ClientIdentity identity = new ClientIdentity(stub); final ClientIdentity identity = new ClientIdentity(stub);
when(ctx.getClientIdentity()).thenReturn(identity); when(ctx.getClientIdentity()).thenReturn(identity);
when(ctx.getStub()).thenReturn(stub); when(ctx.getStub()).thenReturn(stub);
boolean returnValue = contract.mint(ctx, "1000"); contract.mint(ctx, "1000");
String minter = ctx.getClientIdentity().getId(); String minter = ctx.getClientIdentity().getId();
boolean burnResult = contract.burn(ctx, "100"); contract.burn(ctx, "100");
Long totalSupply = contract.totalSupply(ctx); Long totalSupply = contract.totalSupply(ctx);
Long fromBalance = contract.balanceOf(ctx, minter); Long fromBalance = contract.balanceOf(ctx, minter);
assertThat(returnValue).isEqualTo(true);
assertThat(burnResult).isEqualTo(true);
assertThat(totalSupply).isEqualTo(900); assertThat(totalSupply).isEqualTo(900);
assertThat(fromBalance).isEqualTo(900); assertThat(fromBalance).isEqualTo(900);
@ -295,8 +289,7 @@ public class TokenERC20ContractTest {
String spender = "x509::CN=User1@org2.example.com, L=San Francisco, ST=California," String spender = "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"; + " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
boolean result = contract.approve(ctx, spender, "200"); contract.approve(ctx, spender, "200");
assertThat(result).isEqualTo(true);
String owner = ctx.getClientIdentity().getId(); String owner = ctx.getClientIdentity().getId();
long allowance = contract.allowance(ctx, owner, spender); long allowance = contract.allowance(ctx, owner, spender);
assertThat(allowance).isEqualTo(200); assertThat(allowance).isEqualTo(200);
@ -307,31 +300,25 @@ public class TokenERC20ContractTest {
public void allowanceTransferFromTest() throws Exception { public void allowanceTransferFromTest() throws Exception {
/* /*
* ChaincodeStub localStub = new ChaincodeStubNaiveImpl(); * ChaincodeStub localStub = new ChaincodeStubNaiveImpl(); ((ChaincodeStubNaiveImpl)
* ((ChaincodeStubNaiveImpl) * localStub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS); Context localCtx =
* localStub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS); Context * mock(Context.class); ClientIdentity localidentity = new ClientIdentity(localStub);
* localCtx = mock(Context.class); ClientIdentity localidentity = new
* ClientIdentity(localStub);
* when(localCtx.getClientIdentity()).thenReturn(localidentity); * when(localCtx.getClientIdentity()).thenReturn(localidentity);
* when(localCtx.getStub()).thenReturn(localStub); * when(localCtx.getStub()).thenReturn(localStub);
*/ */
String spender = "x509::CN=User1@org2.example.com, L=San Francisco, ST=California," String spender = "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"; + " C=US::CN=ca.org2.example.com, O=org2.example.com, L=San Francisco, ST=California, C=US";
String to = "x509::CN=User2@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"; contract.approve(ctx, spender, "200");
boolean result = contract.approve(ctx, spender, "200");
String owner = ctx.getClientIdentity().getId(); String owner = ctx.getClientIdentity().getId();
((ChaincodeStubNaiveImpl) stub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS); ((ChaincodeStubNaiveImpl) stub).setCertificate(ChaincodeStubNaiveImpl.CERT_WITH_DNS);
identity = new ClientIdentity(stub); identity = new ClientIdentity(stub);
when(ctx.getClientIdentity()).thenReturn(identity); when(ctx.getClientIdentity()).thenReturn(identity);
when(ctx.getStub()).thenReturn(stub); when(ctx.getStub()).thenReturn(stub);
boolean transferResult = contract.transferFrom(ctx, owner, to, "100"); contract.transferFrom(ctx, owner, spender, "100");
long allowance = contract.allowance(ctx, owner, spender); long allowance = contract.allowance(ctx, owner, spender);
assertThat(result).isEqualTo(true);
assertThat(transferResult).isEqualTo(true);
assertThat(allowance).isEqualTo(100); assertThat(allowance).isEqualTo(100);
} }