mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
se elimino otro ejemplo no utilizado
This commit is contained in:
parent
8e8cba0aa8
commit
2ed8904c0f
5 changed files with 0 additions and 797 deletions
|
|
@ -1,231 +0,0 @@
|
|||
# UTXO token scenario
|
||||
|
||||
The UTXO token smart contract demonstrates how to create and transfer fungible tokens using a UTXO (unspent transaction output) model. In a UTXO model, unspent transaction outputs representing a number of tokens can be 'spent' to transfer tokens between participants.
|
||||
An unspent transaction output can only be spent once, and the full value must be completely spent. A transaction that spends UTXOs as input will also generate new UTXOs as outputs, where the value of the inputs must equal the value of the outputs. As an example, if you own an unspent transaction output representing 5000 tokens, and you need to transfer 100 tokens to a recipient, the transaction would spend the 5000 token UTXO as input, create a new 100 token UTXO output owned by the recipient, and return a new 4900 token UTXO to you as 'change'.
|
||||
|
||||
Each UTXO in this sample has a key derived from the transaction id that created it, as well as a number of tokens, and an owner that is authorized to spend the tokens.
|
||||
Ownership of each UTXO could be represented at the organization level or client identity level. In this sample UTXO ownership is based on a client identity, where the client ID is simply a base64-encoded concatenation of the issuer and subject from the client identity's enrollment certificate. The client ID can therefore be used as the payment address when transferring tokens in a UTXO transaction.
|
||||
|
||||
While a transfer transaction spends UTXOs and creates new UTXOs for the recipient(s), a mint transaction can create new UTXOs. In this sample it is assumed that only one organization (played by Org1) is in a central banker role and can mint new tokens owned by their client ID. Any client from any organization can transfer tokens in a UTXO transaction.
|
||||
|
||||
In this tutorial, you will mint and transfer tokens as follows:
|
||||
|
||||
- A member of Org1 uses the `Mint` function to create a UTXO representing a number of tokens. The `Mint` function reads the certificate information of the client identity that submitted the transaction using the `GetClientIdentity.GetID()` API and assigns the UTXO ownership to the minter client ID.
|
||||
- The same minter client will then use the `Transfer` function to transfer the requested number of tokens to a recipient. The minted UTXO key gets passed as input to the `Transfer` function, a UTXO output representing the number of transferred tokens gets created for the recipient, and another UTXO output representing the 'change' gets created for the minter. It is assumed that the recipient has provided their client ID to the transfer caller out of band. The recipient can then transfer tokens to other registered users in the same fashion.
|
||||
|
||||
## Bring up the test network
|
||||
|
||||
You can run the UTXO token transfer scenario using the Fabric test network. Open a command terminal and navigate to the test network directory in your local clone of the `fabric-samples`. We will operate from the `test-network` directory for the remainder of the tutorial.
|
||||
```
|
||||
cd fabric-samples/test-network
|
||||
```
|
||||
|
||||
Run the following command to start the test network:
|
||||
```
|
||||
./network.sh up createChannel -ca
|
||||
```
|
||||
|
||||
The test network is deployed with two peer organizations. The `createChannel` flag deploys the network with a single channel named `mychannel` with Org1 and Org2 as channel members.
|
||||
The -ca flag is used to deploy the network using certificate authorities. This allows you to use each organization's CA to register and enroll new users for this tutorial.
|
||||
|
||||
## Deploy the smart contract to the channel
|
||||
|
||||
You can use the test network script to deploy the UTXO token contract to the channel that was just created. Deploy the smart contract to `mychannel` using the following command:
|
||||
```
|
||||
./network.sh deployCC -ccn token_utxo -ccp ../token-utxo/chaincode-go/ -ccl go
|
||||
```
|
||||
|
||||
The above command deploys the go chaincode with short name `token_utxo`. The smart contract will utilize the default endorsement policy of majority of channel members.
|
||||
Since the channel has two members, this implies that we'll need to get peer endorsements from 2 out of the 2 channel members.
|
||||
|
||||
Now you are ready to call the deployed smart contract via peer CLI calls. But let's first create the client identities for our scenario.
|
||||
|
||||
## Register identities
|
||||
|
||||
The smart contract supports UTXO ownership based on individual client identities from organizations that are members of the channel. In our scenario, the minter of the tokens will be a member of Org1, while the recipient will belong to Org2. To highlight the connection between the `GetClientIdentity().GetID()` API and the information within a user's certificate, we will register two new identities using the Org1 and Org2 Certificate Authorities (CA's), and then use the CA's to generate each identity's certificate and private key.
|
||||
|
||||
First, we need to set the following environment variables to use the the Fabric CA client (and subsequent commands)
|
||||
```
|
||||
export PATH=${PWD}/../bin:${PWD}:$PATH
|
||||
export FABRIC_CFG_PATH=$PWD/../config/
|
||||
```
|
||||
|
||||
The terminal we have been using will represent Org1. We will use the Org1 CA to create the minter identity. Set the Fabric CA client home to the MSP of the Org1 CA admin (this identity was generated by the test network script):
|
||||
```
|
||||
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/
|
||||
```
|
||||
|
||||
You can register a new minter client identity using the `fabric-ca-client` tool:
|
||||
```
|
||||
fabric-ca-client register --caname ca-org1 --id.name minter --id.secret minterpw --id.type client --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
|
||||
```
|
||||
|
||||
You can now generate the identity certificates and MSP folder by providing the enroll name and secret to the enroll command:
|
||||
```
|
||||
fabric-ca-client enroll -u https://minter:minterpw@localhost:7054 --caname ca-org1 -M "${PWD}/organizations/peerOrganizations/org1.example.com/users/minter@org1.example.com/msp" --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
|
||||
```
|
||||
|
||||
Run the command below to copy the Node OU configuration file into the minter identity MSP folder.
|
||||
```
|
||||
cp "${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/org1.example.com/users/minter@org1.example.com/msp/config.yaml"
|
||||
```
|
||||
|
||||
Open a new terminal to represent Org2 and navigate to fabric-samples/test-network. We'll use the Org2 CA to create the Org2 recipient identity. Set the Fabric CA client home to the MSP of the Org2 CA admin:
|
||||
```
|
||||
cd fabric-samples/test-network
|
||||
export PATH=${PWD}/../bin:${PWD}:$PATH
|
||||
export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org2.example.com/
|
||||
```
|
||||
|
||||
You can register a recipient client identity using the `fabric-ca-client` tool:
|
||||
```
|
||||
fabric-ca-client register --caname ca-org2 --id.name recipient --id.secret recipientpw --id.type client --tls.certfiles "${PWD}/organizations/fabric-ca/org2/tls-cert.pem"
|
||||
```
|
||||
|
||||
We can now enroll to generate the identity certificates and MSP folder:
|
||||
```
|
||||
fabric-ca-client enroll -u https://recipient:recipientpw@localhost:8054 --caname ca-org2 -M "${PWD}/organizations/peerOrganizations/org2.example.com/users/recipient@org2.example.com/msp" --tls.certfiles "${PWD}/organizations/fabric-ca/org2/tls-cert.pem"
|
||||
```
|
||||
|
||||
Run the command below to copy the Node OU configuration file into the recipient identity MSP folder.
|
||||
```
|
||||
cp "${PWD}/organizations/peerOrganizations/org2.example.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/org2.example.com/users/recipient@org2.example.com/msp/config.yaml"
|
||||
```
|
||||
|
||||
## Initialize the contract
|
||||
Once we created the identity of the minter we can now initialize the contract.
|
||||
Note that we need to call the initialize function before being able to use any functions of the contract. Initialize() can be called only once.
|
||||
|
||||
Shift back to the Org1 terminal, we'll set the following environment variables to operate the `peer` CLI as the minter identity from Org1.
|
||||
```
|
||||
export CORE_PEER_TLS_ENABLED=true
|
||||
export CORE_PEER_LOCALMSPID=Org1MSP
|
||||
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/minter@org1.example.com/msp
|
||||
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
|
||||
export CORE_PEER_ADDRESS=localhost:7051
|
||||
export TARGET_TLS_OPTIONS=(-o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt")
|
||||
```
|
||||
|
||||
We can then invoke the smart contract to initilize it
|
||||
```
|
||||
peer chaincode invoke "${TARGET_TLS_OPTIONS[@]}" -C mychannel -n token_utxo -c '{"function":"Initialize","Args":["some name", "some symbol"]}'
|
||||
```
|
||||
|
||||
## Mint some tokens
|
||||
|
||||
Now that we have initialized the contract and created the identity of the minter, we can invoke the smart contract to mint some tokens.
|
||||
Shift back to the Org1 terminal, we'll set the following environment variables to operate the `peer` CLI as the minter identity from Org1.
|
||||
```
|
||||
export CORE_PEER_TLS_ENABLED=true
|
||||
export CORE_PEER_LOCALMSPID=Org1MSP
|
||||
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/minter@org1.example.com/msp
|
||||
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
|
||||
export CORE_PEER_ADDRESS=localhost:7051
|
||||
export TARGET_TLS_OPTIONS=(-o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt")
|
||||
```
|
||||
|
||||
The last environment variable above will be utilized within the CLI invoke commands to set the target peers for endorsement, and the target ordering service endpoint and TLS options.
|
||||
|
||||
We can then invoke the smart contract to mint 5000 tokens:
|
||||
```
|
||||
peer chaincode invoke "${TARGET_TLS_OPTIONS[@]}" -C mychannel -n token_utxo -c '{"function":"Mint","Args":["5000"]}'
|
||||
```
|
||||
|
||||
The mint function validated that the client is a member of the minter organization, and then created a UTXO with 5000 tokens belonging to the minter client identity.
|
||||
The function returns the UTXO that was created so that we can inspect it. Here is the returned UTXO with JSON formatting applied:
|
||||
```
|
||||
{
|
||||
"utxo_key":"c3706696c537e7bd6940fedfd52f4a3a630d253297db0ecc2b3ba514b45f5e7c.0",
|
||||
"owner":"eDUwOTo6Q049bWludGVyLE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzEuZXhhbXBsZS5jb20sTz1vcmcxLmV4YW1wbGUuY29tLEw9RHVyaGFtLFNUPU5vcnRoIENhcm9saW5hLEM9VVM=",
|
||||
"amount":5000
|
||||
}
|
||||
```
|
||||
|
||||
Notice that the utxo_key is set to the transaction id, concatenated with ".0" to indicate that this is the first (and only) UTXO output of the transaction. Your transaction id will be different and unique. The owner is set to the minter's client ID, meaning that only this client can spend the UTXO, and the amount is "5000".
|
||||
|
||||
The utxo_key that was created will be needed when you spend the UTXO in the Transfer function below. Copy the utxo_key value (including the ".0") so that you'll have it available for the Transfer function.
|
||||
|
||||
We can check the minter client's total set of owned UTXOs by calling the `ClientUTXOs` function.
|
||||
```
|
||||
peer chaincode query -C mychannel -n token_utxo -c '{"function":"ClientUTXOs","Args":[]}'
|
||||
```
|
||||
|
||||
The same minted UTXO is returned.
|
||||
|
||||
|
||||
## Transfer tokens
|
||||
|
||||
The minter intends to transfer 100 tokens to the Org2 recipient, but first the Org2 recipient needs to provide their own client ID as the payment address.
|
||||
A client can derive their client ID from their own public certificate, but to be sure the client ID is accurate, the contract has a `ClientID` utility function that simply looks at the callers certificate and returns the calling client's ID.
|
||||
Let's prepare the Org2 terminal by setting the environment variables for the Org2 recipient user.
|
||||
```
|
||||
export FABRIC_CFG_PATH=$PWD/../config/
|
||||
export CORE_PEER_TLS_ENABLED=true
|
||||
export CORE_PEER_LOCALMSPID=Org2MSP
|
||||
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/recipient@org2.example.com/msp
|
||||
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||
export CORE_PEER_ADDRESS=localhost:9051
|
||||
```
|
||||
|
||||
Using the Org2 terminal, the Org2 recipient user can retrieve their own client ID:
|
||||
```
|
||||
peer chaincode query -C mychannel -n token_utxo -c '{"function":"ClientID","Args":[]}'
|
||||
```
|
||||
|
||||
The function returns of recipient's client ID:
|
||||
```
|
||||
eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==
|
||||
```
|
||||
|
||||
Let's base64 decode the client ID to make sure it is the Org2 recipient user:
|
||||
```
|
||||
echo eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw== | base64 --decode
|
||||
```
|
||||
|
||||
The result shows that the subject and issuer is indeed the recipient user from Org2:
|
||||
```
|
||||
x509::CN=recipient,OU=client,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org2.example.com,O=org2.example.com,L=Hursley,ST=Hampshire,C=UK
|
||||
```
|
||||
|
||||
After the Org2 recipient provides their client ID to the minter, the minter can initiate a transfer of tokens. We'll pass in the utxo_key of the UTXO with 5000 tokens to spend, and request that two new UTXOs get created, a UTXO with 100 tokens for the recipient, and a UTXO with 4900 tokens for the minter as the 'change'. Since the contract will create the UTXO output keys, we'll initially leave the output keys blank.
|
||||
Back in the Org1 terminal, request the UTXO transfer. **Replace YOUR_UTXO_KEY below with the key you saved earlier**:
|
||||
```
|
||||
peer chaincode invoke "${TARGET_TLS_OPTIONS[@]}" -C mychannel -n token_utxo -c '{"function":"Transfer","Args":["[\"YOUR_UTXO_KEY\"]"," [{\"utxo_key\":\"\",\"owner\":\"eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==\",\"amount\":100},{\"utxo_key\":\"\",\"owner\":\"eDUwOTo6Q049bWludGVyLE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzEuZXhhbXBsZS5jb20sTz1vcmcxLmV4YW1wbGUuY29tLEw9RHVyaGFtLFNUPU5vcnRoIENhcm9saW5hLEM9VVM=\",\"amount\":4900}]"]}'
|
||||
```
|
||||
|
||||
The `Transfer` function verifies that the calling client owns the input UTXO, and that the sum of the input amounts equals the sum of the output amounts. It will then delete (spend) the input UTXO, and create the two output UTXOs. If you passed the incorrect UTXO input key, or requested UTXO output values that don't total 5000, you'll get an error indicating as such.
|
||||
|
||||
The new UTXO outputs are returned in the successful response:
|
||||
```
|
||||
[{\"utxo_key\":\"e51c3d19e92326f772e49e8a3e58f2bbf72bc3905e55fcd649b97a91b9b2cf44.0\",\"owner\":\"eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==\",\"amount\":100},{\"utxo_key\":\"e51c3d19e92326f772e49e8a3e58f2bbf72bc3905e55fcd649b97a91b9b2cf44.1\",\"owner\":\"eDUwOTo6Q049bWludGVyLE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzEuZXhhbXBsZS5jb20sTz1vcmcxLmV4YW1wbGUuY29tLEw9RHVyaGFtLFNUPU5vcnRoIENhcm9saW5hLEM9VVM=\",\"amount\":4900}]
|
||||
```
|
||||
|
||||
While still in the Org1 terminal, let's request the minter's UTXOs again:
|
||||
```
|
||||
peer chaincode query -C mychannel -n token_utxo -c '{"function":"ClientUTXOs","Args":[]}'
|
||||
```
|
||||
|
||||
The new UTXO worth 4900 tokens is returned.
|
||||
|
||||
And then using the Org2 terminal, let's request the recipient's UTXOs:
|
||||
```
|
||||
peer chaincode query -C mychannel -n token_utxo -c '{"function":"ClientUTXOs","Args":[]}'
|
||||
```
|
||||
|
||||
The new UTXO worth 100 tokens is returned.
|
||||
|
||||
Congratulations, you've transferred 100 tokens! The Org2 recipient can now transfer tokens to other registered users in the same manner.
|
||||
|
||||
## 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:
|
||||
```
|
||||
./network.sh down
|
||||
```
|
||||
|
||||
## Contract extension ideas
|
||||
|
||||
You can extend the basic UTXO token sample to meet other requirements. For example:
|
||||
|
||||
* Rather than using the default 'majority' endorsement policy, you could set the endorsement policy to a subset of organizations that represent trust anchors for the contract execution.
|
||||
* You could utilize anonymous payment addresses on the UTXO outputs based on private-public key pairs, instead of UTXOs assigned to a client ID. In order to spend the tokens, the client would have to sign the transfer input as proof that they own the address private key, which the contract would then validate, similar to the Bitcoin model in the permissionless blockchain space. However, in a permissioned blockchain such as Fabric, only registered clients are authorized to participate. Furthermore, if you don't want to leak the registered client identity associated with each address, the clients could be registered using an Identity Mixer MSP, so that the client itself is also anonymous in each of the token transactions.
|
||||
|
|
@ -1,379 +0,0 @@
|
|||
package chaincode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
|
||||
)
|
||||
|
||||
// SmartContract provides functions for transferring tokens using UTXO transactions
|
||||
type SmartContract struct {
|
||||
contractapi.Contract
|
||||
}
|
||||
|
||||
// UTXO represents an unspent transaction output
|
||||
type UTXO struct {
|
||||
Key string `json:"utxo_key"`
|
||||
Owner string `json:"owner"`
|
||||
Amount int `json:"amount"`
|
||||
}
|
||||
|
||||
// Define key names for options
|
||||
const nameKey = "name"
|
||||
const symbolKey = "symbol"
|
||||
|
||||
// Mint creates a new unspent transaction output (UTXO) owned by the minter
|
||||
func (s *SmartContract) Mint(ctx contractapi.TransactionContextInterface, amount int) (*UTXO, error) {
|
||||
|
||||
// check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to mint new tokens
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get MSPID: %v", err)
|
||||
}
|
||||
if clientMSPID != "Org1MSP" {
|
||||
return nil, errors.New("client is not authorized to mint new tokens")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
minter, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get client id: %v", err)
|
||||
}
|
||||
|
||||
if amount <= 0 {
|
||||
return nil, errors.New("mint amount must be a positive integer")
|
||||
}
|
||||
|
||||
utxo := UTXO{}
|
||||
utxo.Key = ctx.GetStub().GetTxID() + ".0"
|
||||
utxo.Owner = minter
|
||||
utxo.Amount = amount
|
||||
|
||||
// the utxo has a composite key of owner:utxoKey, this enables ClientUTXOs() function to query for an owner's utxos.
|
||||
utxoCompositeKey, err := ctx.GetStub().CreateCompositeKey("utxo", []string{minter, utxo.Key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(utxoCompositeKey, []byte(strconv.Itoa(amount)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("utxo minted: %+v", utxo)
|
||||
|
||||
return &utxo, nil
|
||||
}
|
||||
|
||||
// Transfer transfers UTXOs containing tokens from client to recipient(s)
|
||||
func (s *SmartContract) Transfer(ctx contractapi.TransactionContextInterface, utxoInputKeys []string, utxoOutputs []UTXO) ([]UTXO, error) {
|
||||
|
||||
// check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get client id: %v", err)
|
||||
}
|
||||
|
||||
// Validate and summarize utxo inputs
|
||||
utxoInputs := make(map[string]*UTXO)
|
||||
var totalInputAmount int
|
||||
for _, utxoInputKey := range utxoInputKeys {
|
||||
if utxoInputs[utxoInputKey] != nil {
|
||||
return nil, errors.New("the same utxo input can not be spend twice")
|
||||
}
|
||||
|
||||
utxoInputCompositeKey, err := ctx.GetStub().CreateCompositeKey("utxo", []string{clientID, utxoInputKey})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create composite key: %v", err)
|
||||
}
|
||||
|
||||
// validate that client has a utxo matching the input key
|
||||
valueBytes, err := ctx.GetStub().GetState(utxoInputCompositeKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read utxoInputCompositeKey %s from world state: %v", utxoInputCompositeKey, err)
|
||||
}
|
||||
|
||||
if valueBytes == nil {
|
||||
return nil, fmt.Errorf("utxoInput %s not found for client %s", utxoInputKey, clientID)
|
||||
}
|
||||
|
||||
amount, _ := strconv.Atoi(string(valueBytes)) // Error handling not needed since Itoa() was used when setting the utxo amount, guaranteeing it was an integer.
|
||||
|
||||
utxoInput := &UTXO{
|
||||
Key: utxoInputKey,
|
||||
Owner: clientID,
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
totalInputAmount, err = add(totalInputAmount, amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
utxoInputs[utxoInputKey] = utxoInput
|
||||
}
|
||||
|
||||
// Validate and summarize utxo outputs
|
||||
var totalOutputAmount int
|
||||
txID := ctx.GetStub().GetTxID()
|
||||
for i, utxoOutput := range utxoOutputs {
|
||||
|
||||
if utxoOutput.Amount <= 0 {
|
||||
return nil, errors.New("utxo output amount must be a positive integer")
|
||||
}
|
||||
|
||||
utxoOutputs[i].Key = fmt.Sprintf("%s.%d", txID, i)
|
||||
|
||||
totalOutputAmount, err = add(totalOutputAmount, utxoOutput.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate total inputs equals total outputs
|
||||
if totalInputAmount != totalOutputAmount {
|
||||
return nil, fmt.Errorf("total utxoInput amount %d does not equal total utxoOutput amount %d", totalInputAmount, totalOutputAmount)
|
||||
}
|
||||
|
||||
// Since the transaction is valid, now delete utxo inputs from owner's state
|
||||
for _, utxoInput := range utxoInputs {
|
||||
|
||||
utxoInputCompositeKey, err := ctx.GetStub().CreateCompositeKey("utxo", []string{utxoInput.Owner, utxoInput.Key})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create composite key: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().DelState(utxoInputCompositeKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("utxoInput deleted: %+v", utxoInput)
|
||||
}
|
||||
|
||||
// Create utxo outputs using a composite key based on the owner and utxo key
|
||||
for _, utxoOutput := range utxoOutputs {
|
||||
utxoOutputCompositeKey, err := ctx.GetStub().CreateCompositeKey("utxo", []string{utxoOutput.Owner, utxoOutput.Key})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create composite key: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(utxoOutputCompositeKey, []byte(strconv.Itoa(utxoOutput.Amount)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("utxoOutput created: %+v", utxoOutput)
|
||||
}
|
||||
|
||||
return utxoOutputs, nil
|
||||
}
|
||||
|
||||
// ClientUTXOs returns all UTXOs owned by the calling client
|
||||
func (s *SmartContract) ClientUTXOs(ctx contractapi.TransactionContextInterface) ([]*UTXO, error) {
|
||||
|
||||
// check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return nil, errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get client id: %v", err)
|
||||
}
|
||||
|
||||
// since utxos have a composite key of owner:utxoKey, we can query for all utxos matching owner:*
|
||||
utxoResultsIterator, err := ctx.GetStub().GetStateByPartialCompositeKey("utxo", []string{clientID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer utxoResultsIterator.Close()
|
||||
|
||||
var utxos []*UTXO
|
||||
for utxoResultsIterator.HasNext() {
|
||||
utxoRecord, err := utxoResultsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// composite key is expected to be owner:utxoKey
|
||||
_, compositeKeyParts, err := ctx.GetStub().SplitCompositeKey(utxoRecord.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(compositeKeyParts) != 2 {
|
||||
return nil, errors.New("expected composite key with two parts (owner:utxoKey)")
|
||||
}
|
||||
|
||||
utxoKey := compositeKeyParts[1] // owner is at [0], utxoKey is at[1]
|
||||
|
||||
if utxoRecord.Value == nil {
|
||||
return nil, fmt.Errorf("utxo %s has no value", utxoKey)
|
||||
}
|
||||
|
||||
amount, _ := strconv.Atoi(string(utxoRecord.Value)) // Error handling not needed since Itoa() was used when setting the utxo amount, guaranteeing it was an integer.
|
||||
|
||||
utxo := &UTXO{
|
||||
Key: utxoKey,
|
||||
Owner: clientID,
|
||||
Amount: amount,
|
||||
}
|
||||
|
||||
utxos = append(utxos, utxo)
|
||||
}
|
||||
return utxos, nil
|
||||
}
|
||||
|
||||
// ClientID returns the client id of the calling client
|
||||
// Users can use this function to get their own client id, which they can then give to others as the payment address
|
||||
func (s *SmartContract) ClientID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
// check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
// Get ID of submitting client identity
|
||||
clientID, err := ctx.GetClientIdentity().GetID()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get client id: %v", err)
|
||||
}
|
||||
|
||||
return clientID, nil
|
||||
}
|
||||
|
||||
// Name returns a descriptive name for fungible tokens in this contract
|
||||
// returns {String} Returns the name of the token
|
||||
|
||||
func (s *SmartContract) Name(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
// check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Name bytes: %s", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Symbol returns an abbreviated name for fungible tokens in this contract.
|
||||
// returns {String} Returns the symbol of the token
|
||||
|
||||
func (s *SmartContract) Symbol(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
|
||||
// Check if contract has been intilized first
|
||||
initialized, err := checkInitialized(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check if contract is already initialized: %v", err)
|
||||
}
|
||||
if !initialized {
|
||||
return "", errors.New("contract options need to be set before calling any function, call Initialize() to initialize contract")
|
||||
}
|
||||
|
||||
bytes, err := ctx.GetStub().GetState(symbolKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get Symbol: %v", err)
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Set information for a token and intialize contract.
|
||||
// param {String} name The name of the token
|
||||
// param {String} symbol The symbol of the token
|
||||
func (s *SmartContract) Initialize(ctx contractapi.TransactionContextInterface, name string, symbol string) (bool, error) {
|
||||
|
||||
// Check minter authorization - this sample assumes Org1 is the central banker with privilege to intitialize contract
|
||||
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get MSPID: %v", err)
|
||||
}
|
||||
if clientMSPID != "Org1MSP" {
|
||||
return false, errors.New("client is not authorized to initialize contract")
|
||||
}
|
||||
|
||||
// check contract options are not already set, client is not authorized to change them once intitialized
|
||||
bytes, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get Name: %v", err)
|
||||
}
|
||||
if bytes != nil {
|
||||
return false, errors.New("contract options are already set, client is not authorized to change them")
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(nameKey, []byte(name))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set token name: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().PutState(symbolKey, []byte(symbol))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to set symbol: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("name: %v, symbol: %v", name, symbol)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Checks that contract options have been already initialized
|
||||
func checkInitialized(ctx contractapi.TransactionContextInterface) (bool, error) {
|
||||
tokenName, err := ctx.GetStub().GetState(nameKey)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get token name: %v", err)
|
||||
}
|
||||
if tokenName == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// add two number checking for overflow
|
||||
func add(b int, q int) (int, error) {
|
||||
|
||||
// Check overflow
|
||||
sum := q + b
|
||||
|
||||
if (sum < q) == (b >= 0 && q >= 0) {
|
||||
return 0, fmt.Errorf("math: addition overflow occurred %d + %d", b, q)
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
module github.com/hyperledger/fabric-samples/token-utxo/chaincode-go
|
||||
|
||||
go 1.21
|
||||
|
||||
require github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0
|
||||
|
||||
require (
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gobuffalo/envy v1.10.2 // indirect
|
||||
github.com/gobuffalo/packd v1.0.2 // indirect
|
||||
github.com/gobuffalo/packr v1.30.1 // indirect
|
||||
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af // indirect
|
||||
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
google.golang.org/grpc v1.64.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4=
|
||||
github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8=
|
||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw=
|
||||
github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8=
|
||||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af h1:WT4NjX7Uk03GSeH++jF3a0wp4FhybTM86zDPCETvmSk=
|
||||
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0-20240618210511-f7903324a8af/go.mod h1:f/ER25FaBepxJugwpLhbD2hLAoZaZEVqkBjOcHjw72Y=
|
||||
github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0 h1:IDiCGVOBlRd6zpL0Y+f6V7IpBqa4/Z5JAK9SF7a5ea8=
|
||||
github.com/hyperledger/fabric-contract-api-go/v2 v2.0.0/go.mod h1:pdqhe7ALf4lmXgQdprCyNWYdnCPxgj02Vhf8JF5w8po=
|
||||
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk=
|
||||
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
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/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
|
||||
"github.com/hyperledger/fabric-samples/token-utxo/chaincode-go/chaincode"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tokenChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
|
||||
if err != nil {
|
||||
log.Panicf("Error creating token-utxo chaincode: %v", err)
|
||||
}
|
||||
|
||||
if err := tokenChaincode.Start(); err != nil {
|
||||
log.Panicf("Error starting token-utxo chaincode: %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue