mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-22 17:45:10 +00:00
[FAB-11951] Interest-rate swap example for SBE
This is an example on how to represent and implement basic interest rate swap handling using fabric. It demonstrates the usage of state-based endorsement. Change-Id: I04e631299d95262e54e1532489766aa20477064c Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com> Signed-off-by: Alessandro Sorniotti <ale.linux@sopit.net> Signed-off-by: David Enyeart <enyeart@us.ibm.com>
This commit is contained in:
parent
9567985d29
commit
5cd277fdc0
7 changed files with 1258 additions and 0 deletions
176
interest_rate_swaps/README.md
Normal file
176
interest_rate_swaps/README.md
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
# Interest-rate swaps
|
||||||
|
|
||||||
|
This is a sample of how interest-rate swaps can be handled on a blockchain using
|
||||||
|
fabric and state-based endorsement. [State-based endorsement](https://hyperledger-fabric.readthedocs.io/en/release-1.3/endorsement-policies.html#key-level-endorsement)
|
||||||
|
is a new feature released in Hyperledger Fabric 1.3.
|
||||||
|
|
||||||
|
An interest-rate swap is a financial swap traded over the counter. It is a
|
||||||
|
contractual agreement between two parties, where two parties (A and B) exchange
|
||||||
|
payments. The amount of individual payments is based on the principal amount of the
|
||||||
|
swap and an interest rate. The interest rates of the two parties differ. In a
|
||||||
|
typical scenario, one payment (A to B) is based on a fixed rate set in the
|
||||||
|
contract. The other payment (B to A) is based on a floating rate. This rate is
|
||||||
|
defined through a reference rate, such as LIBOR from LSE, and an offset to this
|
||||||
|
rate.
|
||||||
|
|
||||||
|
## Network
|
||||||
|
|
||||||
|
We assume organizations of the following roles participate in our network:
|
||||||
|
* Parties that want to exchange payments
|
||||||
|
* Parties that provide reference rates
|
||||||
|
* Auditors that need to audit certain swaps
|
||||||
|
|
||||||
|
The chaincode-level endorsement policy is set to require an endorsement from an
|
||||||
|
auditor as well as an endorsement from any swap participant.
|
||||||
|
|
||||||
|
## Data model
|
||||||
|
We represent a swap on the ledger as a JSON with the following fields:
|
||||||
|
* `StartDate` and `EndDate` of the swap
|
||||||
|
* `PaymentInterval` - the time interval of the payments
|
||||||
|
* `PrincipalAmount` - the principal amount of the swap
|
||||||
|
* `FixedRate` - the fixed rate of the swap
|
||||||
|
* `FloatingRate` - the floating rate of the swap (offset to the reference rate)
|
||||||
|
* `ReferenceRate` - the key name of the KVS pair that holds the reference rate
|
||||||
|
|
||||||
|
The key for the swap is a unique identifier combined with a common prefix `swap`
|
||||||
|
that identifies swap entries in the KVS namespace. Upon creation the key-level
|
||||||
|
endorsement policy for the swap is set to the participants of the swap and,
|
||||||
|
potentially, an auditor.
|
||||||
|
|
||||||
|
We represent the payment information as a single KVS entry per swap with the
|
||||||
|
same unique identifier as the swap itself and a common prefix `payment` for payments.
|
||||||
|
If payments are due, the entry states the amount due. Otherwise, it is "none".
|
||||||
|
A payment information KVS entry has the same key-level endorsement policy
|
||||||
|
set as its corresponding swap entry.
|
||||||
|
|
||||||
|
We represent the reference rates as a KVS entry per rate with an identifier per
|
||||||
|
rate and a common prefix for reference rates. The key-level endorsement policy
|
||||||
|
for a reference rate entry is set to the provider of the corresponding reference
|
||||||
|
rate, such as LSE for LIBOR.
|
||||||
|
The reference rate could also be modeled via a separate chaincode, where the
|
||||||
|
chaincode-level endorsement policies only allows reference rate providers to
|
||||||
|
create keys.
|
||||||
|
|
||||||
|
Taken together, here is an example of the KVS entries involved in a swap:
|
||||||
|
```
|
||||||
|
KEY | VALUE
|
||||||
|
-------------|-----------------------------------------------------
|
||||||
|
swap1 | {StartDate: 2018-10-01, ..., ReferenceRate: "libor"}
|
||||||
|
payment1 | "none"
|
||||||
|
rr_libor | 0.27
|
||||||
|
```
|
||||||
|
In this example, the swap with ID 1 is represented by the `swap1` and `payment1`
|
||||||
|
KVS entries. The reference rate is set to `libor`, which will cause the chaincode
|
||||||
|
to look up the `rr_libor` entry in the KVS to calculate the rate for the
|
||||||
|
floating leg of the swap.
|
||||||
|
|
||||||
|
## Chaincode
|
||||||
|
The interest-rate swap chaincode provides the following API:
|
||||||
|
* `createSwap(swapID, swap_info, partyA, partyB)` - create a new swap with the
|
||||||
|
given identifier and swap parameters among the two parties specified. This
|
||||||
|
function creates the entry for the swap and the corresponding payment. It
|
||||||
|
also sets the key-level endorsement policies for both keys to the participants
|
||||||
|
to the swap. In case the swap's prinicpal amount exceeds a certain threshold,
|
||||||
|
it adds an auditor to the endorsement policy for the keys.
|
||||||
|
* `calculatePayment(swapID)` - calculate the net payment from party A to party
|
||||||
|
B and set the payment entry accordingly. If the payment information is negative,
|
||||||
|
the payment due flows from B to A. The payment information is calculated based
|
||||||
|
on the rates specified in the swap and the principal amount. If the payment
|
||||||
|
key is not "none", this function returns an error, indicating that a prior
|
||||||
|
payment has not been settled yet.
|
||||||
|
* `settlePayment(swapID)` - set the payment entry for the given swap ID to "none".
|
||||||
|
This function is supposed to be invoked after the two parties have settled the
|
||||||
|
payment off-chain.
|
||||||
|
* `setReferenceRate(rrID, value)` - set a given reference rate to a given value.
|
||||||
|
* `Init(auditor, threshold, rrProviders...)` - the chaincode namespace is initialized
|
||||||
|
with a threshold for the principal amount above which a designated auditor
|
||||||
|
needs to be involved as well as a list of reference rate providers and rate IDs.
|
||||||
|
|
||||||
|
## Trust model
|
||||||
|
The state-based endorsement policies used in this sample ensure the following
|
||||||
|
trust model:
|
||||||
|
* All operations related to a specific swap need to be endorsed (at least) by
|
||||||
|
the participants to that swap. This includes both creation of a swap, as well
|
||||||
|
as calculating the payment information and agreeing that the payments have
|
||||||
|
been settled.
|
||||||
|
* Operations related to a reference rate need to be endorsed by the provider of
|
||||||
|
a reference rate.
|
||||||
|
* Under certain circumstances an auditor needs to endorse operations for a swap,
|
||||||
|
e.g., if it exceeds a threshold for the principal amount.
|
||||||
|
|
||||||
|
The chaincode-level endorsement policy requires at least one potential swap
|
||||||
|
participant and an auditor. This endorsement policy sets the trust relationship
|
||||||
|
for creating a swap.
|
||||||
|
|
||||||
|
## Sample network
|
||||||
|
|
||||||
|
The `network` subdirectory contains scripts that will launch a sample network
|
||||||
|
and run a swap transaction flow from creation to settlement.
|
||||||
|
|
||||||
|
### Prerequesites
|
||||||
|
|
||||||
|
The following prerequisites are needed to run this sample:
|
||||||
|
* Fabric docker images. By default the `network/network.sh` script will look for
|
||||||
|
fabric images with the `latest` tag, this can be adapted with the `-i` command
|
||||||
|
line parameter of the script.
|
||||||
|
* A local installation of `configtxgen` and `cryptogen` in the `PATH` environment,
|
||||||
|
or included in `fabric-samples/bin` directory.
|
||||||
|
* Vendoring the chaincode. In the chaincode directory, run `govendor init` and
|
||||||
|
`govendor add +external` to vendor the shim from your local copy of fabric.
|
||||||
|
|
||||||
|
### Bringing up the network
|
||||||
|
|
||||||
|
Simply run `network.sh up` to bring up the network. This will spawn docker
|
||||||
|
containers running a network of 3 "regular" organizations, one auditor
|
||||||
|
organization and one reference rate provider as well as a solo orderer.
|
||||||
|
|
||||||
|
An additional CLI container will run `network/scripts/script.sh` to join the
|
||||||
|
peers to the `irs` channel and deploy the chaincode. In the init parameters it
|
||||||
|
supplies the audit threshold, the auditor organization and the reference rate
|
||||||
|
provider with the corresponding reference rate ID. In the following transactions
|
||||||
|
it sets the reference rate, creates a swap, calculates payment information for
|
||||||
|
the swap and marks them as settled afterwards. We will show the corresponding
|
||||||
|
commands in the following section.
|
||||||
|
|
||||||
|
### Transactions
|
||||||
|
|
||||||
|
The chaincode is instantiated as follows:
|
||||||
|
```
|
||||||
|
peer chaincode instantiate -o irs-orderer:7050 -C irs -n irscc -l golang -v 0 -c '{"Args":["init","auditor","100000","rrprovider","myrr"]}' -P "AND(OR('partya.peer','partyb.peer','partyc.peer'), 'auditor.peer')"
|
||||||
|
```
|
||||||
|
This sets an auditing threshold of 1M, above which the `auditor` organization
|
||||||
|
needs to be involved. It also specifies the `myrr` reference rate provided by
|
||||||
|
the `rrprovider` organization.
|
||||||
|
|
||||||
|
To set a reference rate:
|
||||||
|
```
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-rrprovider:7051 -c '{"Args":["setReferenceRate","myrr","3"]}'
|
||||||
|
```
|
||||||
|
Note that the transaction is endorsed by a peer of the organization we have
|
||||||
|
specified as providing this reference rate in the init parameters.
|
||||||
|
|
||||||
|
To create a swap named "myswap":
|
||||||
|
```
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":365,\"PrincipalAmount\":10000000,\"FixedRate\":4,\"FloatingRate\":5,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}'
|
||||||
|
```
|
||||||
|
Note that the transaction is endorsed by both parties that are part of this
|
||||||
|
swap as well as the auditor. Since the principal amount in this case is lower
|
||||||
|
than the audit threshold we set as init parameters, no auditor will be required
|
||||||
|
to endorse changes to the payment info or swap details.
|
||||||
|
|
||||||
|
To calculate payment info for "myswap":
|
||||||
|
```
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 -c '{"Args":["calculatePayment","myswap"]}'
|
||||||
|
```
|
||||||
|
Note that we target only peers of
|
||||||
|
party A and party B, since the swap is below the auditing threshold.
|
||||||
|
|
||||||
|
To settle payment of "myswap":
|
||||||
|
```
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc `--peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 -c '{"Args":["settlePayment","myswap"]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
As an exercise, try to create a new swap above the auditing threshold and see
|
||||||
|
how validation fails if the auditor is not involved in every operation on the
|
||||||
|
swap. Also try to calculate payment info before settling a prior payment to a
|
||||||
|
swap.
|
||||||
313
interest_rate_swaps/chaincode/chaincode.go
Normal file
313
interest_rate_swaps/chaincode/chaincode.go
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
/*
|
||||||
|
Copyright IBM Corp. All Rights Reserved.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric/core/chaincode/shim"
|
||||||
|
"github.com/hyperledger/fabric/core/chaincode/shim/ext/statebased"
|
||||||
|
pb "github.com/hyperledger/fabric/protos/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* InterestRateSwap represents an interest rate swap on the ledger
|
||||||
|
* The swap is active between its start- and end-date.
|
||||||
|
* At the specified interval, two parties A and B exchange the following payments:
|
||||||
|
* A->B (PrincipalAmount * FixedRateBPS) / 100
|
||||||
|
* B->A (PrincipalAmount * (ReferenceRateBPS + FloatingRateBPS)) / 100
|
||||||
|
* We represent rates as basis points, with one basis point being equal to 1/100th
|
||||||
|
* of 1% (see https://www.investopedia.com/terms/b/basispoint.asp)
|
||||||
|
*/
|
||||||
|
type InterestRateSwap struct {
|
||||||
|
StartDate time.Time
|
||||||
|
EndDate time.Time
|
||||||
|
PaymentInterval time.Duration
|
||||||
|
PrincipalAmount uint64
|
||||||
|
FixedRateBPS uint64
|
||||||
|
FloatingRateBPS uint64
|
||||||
|
ReferenceRate string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SwapManager is the chaincode that handles interest rate swaps.
|
||||||
|
The chaincode endorsement policy includes an auditing organization.
|
||||||
|
It provides the following functions:
|
||||||
|
-) createSwap: create swap with participants
|
||||||
|
-) calculatePayment: calculate what needs to be paid
|
||||||
|
-) settlePayment: mark payment done
|
||||||
|
-) setReferenceRate: for providers to set the reference rate
|
||||||
|
|
||||||
|
The SwapManager stores three different kinds of information on the ledger:
|
||||||
|
-) the actual swap data ("swap" + ID)
|
||||||
|
-) the payment information ("payment" + ID), if "none", the payment has been settled
|
||||||
|
-) the reference rate ("rr" + ID)
|
||||||
|
*/
|
||||||
|
type SwapManager struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init callback
|
||||||
|
func (cc *SwapManager) Init(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
args := stub.GetArgs()
|
||||||
|
if len(args) < 5 {
|
||||||
|
return shim.Error("Insufficient number of arguments. Expected: <function> <auditor_MSPID> <audit_threshold> <rr_provider1_MSPID> <rr_provider1_rateID> ... <rr_providerN_MSPID> <rr_providerN_rateID>")
|
||||||
|
}
|
||||||
|
// set the limit above which the auditor needs to be involved, require it
|
||||||
|
// to be endorsed by the auditor
|
||||||
|
err := stub.PutState("audit_limit", args[2])
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
auditorEP, err := statebased.NewStateEP(nil)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = auditorEP.AddOrgs(statebased.RoleTypePeer, string(args[1]))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
epBytes, err := auditorEP.Policy()
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = stub.SetStateValidationParameter("audit_limit", epBytes)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the reference rates, require them to be endorsed by the provider
|
||||||
|
for i := 3; i+1 < len(args); i += 2 {
|
||||||
|
org := string(args[i])
|
||||||
|
rrID := "rr" + string(args[i+1])
|
||||||
|
err = stub.PutState(rrID, []byte("0"))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
ep, err := statebased.NewStateEP(nil)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = ep.AddOrgs(statebased.RoleTypePeer, org)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
epBytes, err = ep.Policy()
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = stub.SetStateValidationParameter(rrID, epBytes)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke dispatcher
|
||||||
|
func (cc *SwapManager) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
funcName, _ := stub.GetFunctionAndParameters()
|
||||||
|
if function, ok := functions[funcName]; ok {
|
||||||
|
fmt.Printf("Invoking %s\n", funcName)
|
||||||
|
return function(stub)
|
||||||
|
}
|
||||||
|
return shim.Error(fmt.Sprintf("Unknown function %s", funcName))
|
||||||
|
}
|
||||||
|
|
||||||
|
var functions = map[string]func(stub shim.ChaincodeStubInterface) pb.Response{
|
||||||
|
"createSwap": createSwap,
|
||||||
|
"calculatePayment": calculatePayment,
|
||||||
|
"settlePayment": settlePayment,
|
||||||
|
"setReferenceRate": setReferenceRate,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new swap among participants.
|
||||||
|
// The creation of the swap needs to be endorsed by the chaincode endorsement policy.
|
||||||
|
// Once created, the swap needs to be endorsed by its participants as well as the
|
||||||
|
// auditor in case the principal amount of the swap exceeds the audit threshold.
|
||||||
|
// This is enforced through the state-based endorsement policy that is set in this
|
||||||
|
// function.
|
||||||
|
// Parameters: swap ID, a JSONized InterestRateSwap, MSP ID of participant 1,
|
||||||
|
// MSP ID of participant 2
|
||||||
|
func createSwap(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
_, parameters := stub.GetFunctionAndParameters()
|
||||||
|
if len(parameters) != 4 {
|
||||||
|
return shim.Error("Wrong number of arguments supplied. Expected: <swap_ID> <interest_rate_swap_json> <participant1_MSPID> <participant2_MSPID>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the swap
|
||||||
|
swapID := "swap" + string(parameters[0])
|
||||||
|
irsJSON := []byte(parameters[1])
|
||||||
|
var irs InterestRateSwap
|
||||||
|
err := json.Unmarshal(irsJSON, &irs)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = stub.PutState(swapID, irsJSON)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the auditing threshold
|
||||||
|
auditLimit, err := stub.GetState("audit_limit")
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
threshold, err := strconv.Atoi(string(auditLimit))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// set endorsers
|
||||||
|
ep, err := statebased.NewStateEP(nil)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = ep.AddOrgs(statebased.RoleTypePeer, parameters[2], parameters[3])
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
// if the swap principal amount exceeds the audit threshold set in init, the auditor needs to endorse as well
|
||||||
|
if irs.PrincipalAmount > uint64(threshold) {
|
||||||
|
fmt.Printf("Adding auditor for swap %s with prinicipal amount %v above threshold %v\n", parameters[0], irs.PrincipalAmount, uint64(threshold))
|
||||||
|
err = ep.AddOrgs(statebased.RoleTypePeer, "auditor")
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the endorsement policy for the swap
|
||||||
|
epBytes, err := ep.Policy()
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = stub.SetStateValidationParameter(swapID, epBytes)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and set the key for the payment
|
||||||
|
paymentID := "payment" + string(parameters[0])
|
||||||
|
err = stub.PutState(paymentID, []byte("none"))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = stub.SetStateValidationParameter(paymentID, epBytes)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the payment due for a given swap
|
||||||
|
func calculatePayment(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
_, parameters := stub.GetFunctionAndParameters()
|
||||||
|
if len(parameters) != 1 {
|
||||||
|
return shim.Error("Wrong number of arguments supplied. Expected: <swap_ID>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve swap
|
||||||
|
swapID := "swap" + parameters[0]
|
||||||
|
irsJSON, err := stub.GetState(swapID)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
if irsJSON == nil {
|
||||||
|
return shim.Error(fmt.Sprintf("Swap %s does not exist", parameters[0]))
|
||||||
|
}
|
||||||
|
var irs InterestRateSwap
|
||||||
|
err = json.Unmarshal(irsJSON, &irs)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the previous payment has been settled
|
||||||
|
paymentID := "payment" + parameters[0]
|
||||||
|
paid, err := stub.GetState(paymentID)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
if paid == nil {
|
||||||
|
return shim.Error("Unexpected error: payment entry is nil. This should not happen.")
|
||||||
|
}
|
||||||
|
if string(paid) != "none" {
|
||||||
|
return shim.Error("Previous payment has not been settled yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get reference rate
|
||||||
|
referenceRateBytes, err := stub.GetState("rr" + irs.ReferenceRate)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
if referenceRateBytes == nil {
|
||||||
|
return shim.Error(fmt.Sprintf("Reference rate %s not found", irs.ReferenceRate))
|
||||||
|
}
|
||||||
|
referenceRate, err := strconv.Atoi(string(referenceRateBytes))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate payment
|
||||||
|
p1 := int((irs.PrincipalAmount * irs.FixedRateBPS) / 100)
|
||||||
|
p2 := int((irs.PrincipalAmount * (irs.FloatingRateBPS + uint64(referenceRate))) / 100)
|
||||||
|
payment := strconv.Itoa(p1 - p2)
|
||||||
|
err = stub.PutState(paymentID, []byte(payment))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return shim.Success([]byte(payment))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settle the payment for a given swap
|
||||||
|
func settlePayment(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
_, parameters := stub.GetFunctionAndParameters()
|
||||||
|
if len(parameters) != 1 {
|
||||||
|
return shim.Error("Wrong number of arguments supplied. Expected: <swap_ID>")
|
||||||
|
}
|
||||||
|
paymentID := "payment" + parameters[0]
|
||||||
|
paid, err := stub.GetState(paymentID)
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
if paid == nil {
|
||||||
|
return shim.Error("Unexpected error: payment entry is nil. This should not happen.")
|
||||||
|
}
|
||||||
|
if string(paid) == "none" {
|
||||||
|
return shim.Error("Payment has already been settled.")
|
||||||
|
}
|
||||||
|
err = stub.PutState(paymentID, []byte("none"))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
return shim.Success([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the reference rate for a given rate provider
|
||||||
|
func setReferenceRate(stub shim.ChaincodeStubInterface) pb.Response {
|
||||||
|
_, parameters := stub.GetFunctionAndParameters()
|
||||||
|
if len(parameters) != 2 {
|
||||||
|
return shim.Error("Wrong number of arguments supplied. Expected: <reference_rate_ID> <reference_rate_BPS>")
|
||||||
|
}
|
||||||
|
|
||||||
|
rrID := "rr" + parameters[0]
|
||||||
|
err := stub.PutState(rrID, []byte(parameters[1]))
|
||||||
|
if err != nil {
|
||||||
|
return shim.Error(err.Error())
|
||||||
|
}
|
||||||
|
return shim.Success([]byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := shim.Start(new(SwapManager))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error starting IRS chaincode: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
192
interest_rate_swaps/network/configtx.yaml
Normal file
192
interest_rate_swaps/network/configtx.yaml
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
# Copyright IBM Corp. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
Organizations:
|
||||||
|
- &orderer
|
||||||
|
Name: orderer
|
||||||
|
ID: orderer
|
||||||
|
MSPDir: crypto-config/ordererOrganizations/example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('orderer.member')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('orderer.member')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('orderer.admin')
|
||||||
|
|
||||||
|
|
||||||
|
- &partya
|
||||||
|
Name: partya
|
||||||
|
ID: partya
|
||||||
|
MSPDir: crypto-config/peerOrganizations/partya.example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partya.admin', 'partya.peer', 'partya.client')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partya.admin', 'partya.client')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partya.admin')
|
||||||
|
AnchorPeers:
|
||||||
|
- Host: irs-partya
|
||||||
|
Port: 7051
|
||||||
|
|
||||||
|
- &partyb
|
||||||
|
Name: partyb
|
||||||
|
ID: partyb
|
||||||
|
MSPDir: crypto-config/peerOrganizations/partyb.example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyb.admin', 'partyb.peer', 'partyb.client')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyb.admin', 'partyb.client')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyb.admin')
|
||||||
|
AnchorPeers:
|
||||||
|
- Host: irs-partyb
|
||||||
|
Port: 7051
|
||||||
|
|
||||||
|
- &partyc
|
||||||
|
Name: partyc
|
||||||
|
ID: partyc
|
||||||
|
MSPDir: crypto-config/peerOrganizations/partyc.example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyc.admin', 'partyc.peer', 'partyc.client')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyc.admin', 'partyc.client')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('partyc.admin')
|
||||||
|
AnchorPeers:
|
||||||
|
- Host: irs-partyc
|
||||||
|
Port: 7051
|
||||||
|
|
||||||
|
- &auditor
|
||||||
|
Name: auditor
|
||||||
|
ID: auditor
|
||||||
|
MSPDir: crypto-config/peerOrganizations/auditor.example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('auditor.admin', 'auditor.peer', 'auditor.client')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('auditor.admin', 'auditor.client')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('auditor.admin')
|
||||||
|
AnchorPeers:
|
||||||
|
- Host: irs-auditor
|
||||||
|
Port: 7051
|
||||||
|
|
||||||
|
- &rrprovider
|
||||||
|
Name: rrprovider
|
||||||
|
ID: rrprovider
|
||||||
|
MSPDir: crypto-config/peerOrganizations/rrprovider.example.com/msp
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('rrprovider.admin', 'rrprovider.peer', 'rrprovider.client')
|
||||||
|
Writers:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('rrprovider.admin', 'rrprovider.client')
|
||||||
|
Admins:
|
||||||
|
Type: Signature
|
||||||
|
Rule: OR('rrprovider.admin')
|
||||||
|
AnchorPeers:
|
||||||
|
- Host: irs-rrprovider
|
||||||
|
Port: 7051
|
||||||
|
|
||||||
|
Channel: &ChannelDefaults
|
||||||
|
Capabilities:
|
||||||
|
V1_3: true
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Readers
|
||||||
|
Writers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Writers
|
||||||
|
Admins:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: MAJORITY Admins
|
||||||
|
|
||||||
|
Orderer: &OrdererDefaults
|
||||||
|
OrdererType: solo
|
||||||
|
Capabilities:
|
||||||
|
V1_1: true
|
||||||
|
Addresses:
|
||||||
|
- irs-orderer:7050
|
||||||
|
BatchTimeout: 2s
|
||||||
|
BatchSize:
|
||||||
|
MaxMessageCount: 10
|
||||||
|
AbsoluteMaxBytes: 99 MB
|
||||||
|
PreferredMaxBytes: 512 KB
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Readers
|
||||||
|
Writers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Writers
|
||||||
|
Admins:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: MAJORITY Admins
|
||||||
|
BlockValidation:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Writers
|
||||||
|
Organizations:
|
||||||
|
|
||||||
|
Application: &ApplicationDefaults
|
||||||
|
Capabilities:
|
||||||
|
V1_3: true
|
||||||
|
Policies:
|
||||||
|
Readers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Readers
|
||||||
|
Writers:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: ANY Writers
|
||||||
|
Admins:
|
||||||
|
Type: ImplicitMeta
|
||||||
|
Rule: MAJORITY Admins
|
||||||
|
Organizations:
|
||||||
|
|
||||||
|
Profiles:
|
||||||
|
IRSNetGenesis:
|
||||||
|
<<: *ChannelDefaults
|
||||||
|
Orderer:
|
||||||
|
<<: *OrdererDefaults
|
||||||
|
Organizations:
|
||||||
|
- *orderer
|
||||||
|
Consortiums:
|
||||||
|
SampleConsortium:
|
||||||
|
Organizations:
|
||||||
|
- *partya
|
||||||
|
- *partyb
|
||||||
|
- *partyc
|
||||||
|
- *rrprovider
|
||||||
|
- *auditor
|
||||||
|
IRSChannel:
|
||||||
|
Consortium: SampleConsortium
|
||||||
|
Application:
|
||||||
|
<<: *ApplicationDefaults
|
||||||
|
Organizations:
|
||||||
|
- *partya
|
||||||
|
- *partyb
|
||||||
|
- *partyc
|
||||||
|
- *rrprovider
|
||||||
|
- *auditor
|
||||||
51
interest_rate_swaps/network/crypto-config.yaml
Normal file
51
interest_rate_swaps/network/crypto-config.yaml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright IBM Corp. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
OrdererOrgs:
|
||||||
|
- Name: orderer
|
||||||
|
Domain: example.com
|
||||||
|
Specs:
|
||||||
|
- Hostname: orderer
|
||||||
|
|
||||||
|
PeerOrgs:
|
||||||
|
- Name: partya
|
||||||
|
Domain: partya.example.com
|
||||||
|
EnableNodeOUs: true
|
||||||
|
Template:
|
||||||
|
Count: 1
|
||||||
|
Users:
|
||||||
|
Count: 1
|
||||||
|
|
||||||
|
- Name: partyb
|
||||||
|
Domain: partyb.example.com
|
||||||
|
EnableNodeOUs: true
|
||||||
|
Template:
|
||||||
|
Count: 1
|
||||||
|
Users:
|
||||||
|
Count: 1
|
||||||
|
|
||||||
|
- Name: partyc
|
||||||
|
Domain: partyc.example.com
|
||||||
|
EnableNodeOUs: true
|
||||||
|
Template:
|
||||||
|
Count: 1
|
||||||
|
Users:
|
||||||
|
Count: 1
|
||||||
|
|
||||||
|
- Name: auditor
|
||||||
|
Domain: auditor.example.com
|
||||||
|
EnableNodeOUs: true
|
||||||
|
Template:
|
||||||
|
Count: 1
|
||||||
|
Users:
|
||||||
|
Count: 1
|
||||||
|
|
||||||
|
- Name: rrprovider
|
||||||
|
Domain: rrprovider.example.com
|
||||||
|
EnableNodeOUs: true
|
||||||
|
Template:
|
||||||
|
Count: 1
|
||||||
|
Users:
|
||||||
|
Count: 1
|
||||||
163
interest_rate_swaps/network/docker-compose.yaml
Normal file
163
interest_rate_swaps/network/docker-compose.yaml
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
# Copyright IBM Corp. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
orderer.example.com:
|
||||||
|
peer0.partya.example.com:
|
||||||
|
peer0.partyb.example.com:
|
||||||
|
peer0.partyc.example.com:
|
||||||
|
peer0.auditor.example.com:
|
||||||
|
peer0.rrprovider.example.com:
|
||||||
|
|
||||||
|
services:
|
||||||
|
peer-base:
|
||||||
|
image: hyperledger/fabric-peer:$IMAGE_TAG
|
||||||
|
environment:
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- FABRIC_LOGGING_SPEC=INFO
|
||||||
|
- CORE_PEER_TLS_ENABLED=false
|
||||||
|
- CORE_PEER_GOSSIP_USELEADERELECTION=true
|
||||||
|
- CORE_PEER_GOSSIP_ORGLEADER=false
|
||||||
|
- CORE_PEER_PROFILE_ENABLED=true
|
||||||
|
- CORE_PEER_ADDRESSAUTODETECT=true
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
|
command: peer node start
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
|
||||||
|
orderer:
|
||||||
|
container_name: irs-orderer
|
||||||
|
image: hyperledger/fabric-orderer:$IMAGE_TAG
|
||||||
|
environment:
|
||||||
|
- FABRIC_LOGGING_SPEC=INFO
|
||||||
|
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
|
||||||
|
- ORDERER_GENERAL_GENESISMETHOD=file
|
||||||
|
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
|
||||||
|
- ORDERER_GENERAL_LOCALMSPID=orderer
|
||||||
|
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
|
||||||
|
- ORDERER_GENERAL_TLS_ENABLED=false
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
|
||||||
|
command: orderer
|
||||||
|
volumes:
|
||||||
|
- ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
|
||||||
|
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
|
||||||
|
- orderer.example.com:/var/hyperledger/production/orderer
|
||||||
|
ports:
|
||||||
|
- 7050:7050
|
||||||
|
|
||||||
|
|
||||||
|
partya:
|
||||||
|
container_name: irs-partya
|
||||||
|
extends:
|
||||||
|
service: peer-base
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=partya.peer0
|
||||||
|
- CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=irs-partya:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=partya
|
||||||
|
- CORE_CHAINCODE_LOGGING_SHIM=INFO
|
||||||
|
volumes:
|
||||||
|
- ./crypto-config/peerOrganizations/partya.example.com/peers/peer0.partya.example.com/msp:/etc/hyperledger/fabric/msp
|
||||||
|
- peer0.partya.example.com:/var/hyperledger/production
|
||||||
|
ports:
|
||||||
|
- 7051:7051
|
||||||
|
- 7053:7053
|
||||||
|
|
||||||
|
partyb:
|
||||||
|
container_name: irs-partyb
|
||||||
|
extends:
|
||||||
|
service: peer-base
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=partyb.peer0
|
||||||
|
- CORE_PEER_ADDRESS=irs-partyb:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=irs-partyb:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=partyb
|
||||||
|
volumes:
|
||||||
|
- ./crypto-config/peerOrganizations/partyb.example.com/peers/peer0.partyb.example.com/msp:/etc/hyperledger/fabric/msp
|
||||||
|
- peer0.partyb.example.com:/var/hyperledger/production
|
||||||
|
ports:
|
||||||
|
- 8051:7051
|
||||||
|
- 8053:7053
|
||||||
|
|
||||||
|
partyc:
|
||||||
|
container_name: irs-partyc
|
||||||
|
extends:
|
||||||
|
service: peer-base
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=partyc.peer0
|
||||||
|
- CORE_PEER_ADDRESS=irs-partyc:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=irs-partyc:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=partyc
|
||||||
|
volumes:
|
||||||
|
- ./crypto-config/peerOrganizations/partyc.example.com/peers/peer0.partyc.example.com/msp:/etc/hyperledger/fabric/msp
|
||||||
|
- peer0.partyc.example.com:/var/hyperledger/production
|
||||||
|
ports:
|
||||||
|
- 9051:7051
|
||||||
|
- 9053:7053
|
||||||
|
|
||||||
|
auditor:
|
||||||
|
container_name: irs-auditor
|
||||||
|
extends:
|
||||||
|
service: peer-base
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=auditor.peer0
|
||||||
|
- CORE_PEER_ADDRESS=irs-auditor:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=irs-auditor:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=auditor
|
||||||
|
volumes:
|
||||||
|
- ./crypto-config/peerOrganizations/auditor.example.com/peers/peer0.auditor.example.com/msp:/etc/hyperledger/fabric/msp
|
||||||
|
- peer0.auditor.example.com:/var/hyperledger/production
|
||||||
|
ports:
|
||||||
|
- 10051:7051
|
||||||
|
- 10053:7053
|
||||||
|
|
||||||
|
rrprovider:
|
||||||
|
container_name: irs-rrprovider
|
||||||
|
extends:
|
||||||
|
service: peer-base
|
||||||
|
environment:
|
||||||
|
- CORE_PEER_ID=rrprovider.peer0
|
||||||
|
- CORE_PEER_ADDRESS=irs-rrprovider:7051
|
||||||
|
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=irs-rrprovider:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=rrprovider
|
||||||
|
- CORE_LOGGING_LEVEL=DEBUG
|
||||||
|
volumes:
|
||||||
|
- ./crypto-config/peerOrganizations/rrprovider.example.com/peers/peer0.rrprovider.example.com/msp:/etc/hyperledger/fabric/msp
|
||||||
|
- peer0.rrprovider.example.com:/var/hyperledger/production
|
||||||
|
ports:
|
||||||
|
- 11051:7051
|
||||||
|
- 11053:7053
|
||||||
|
|
||||||
|
cli:
|
||||||
|
container_name: cli
|
||||||
|
image: hyperledger/fabric-tools:$IMAGE_TAG
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
|
environment:
|
||||||
|
- GOPATH=/opt/gopath
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- FABRIC_LOGGING_SPEC=INFO
|
||||||
|
- CORE_PEER_ID=cli
|
||||||
|
- CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=partya
|
||||||
|
- CORE_PEER_TLS_ENABLED=false
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/Admin@partya.example.com/msp
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
|
command: /bin/bash
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ../chaincode/:/opt/gopath/src/irscc
|
||||||
|
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
|
||||||
|
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
|
||||||
|
- ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
|
||||||
|
depends_on:
|
||||||
|
- orderer
|
||||||
|
- partya
|
||||||
|
- partyb
|
||||||
|
- partyc
|
||||||
|
- auditor
|
||||||
|
- rrprovider
|
||||||
240
interest_rate_swaps/network/network.sh
Executable file
240
interest_rate_swaps/network/network.sh
Executable file
|
|
@ -0,0 +1,240 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp All Rights Reserved
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
# This script brings up a test network for the interest-rate swap fabric example.
|
||||||
|
# It relies on two tools:
|
||||||
|
# * cryptogen - generates the x509 certificates used to identify and
|
||||||
|
# authenticate the various components in the network.
|
||||||
|
# * configtxgen - generates the requisite configuration artifacts for orderer
|
||||||
|
# bootstrap and channel creation.
|
||||||
|
#
|
||||||
|
# Each tool consumes a configuration yaml file, within which we specify the topology
|
||||||
|
# of our network (cryptogen) and the location of our certificates for various
|
||||||
|
# configuration operations (configtxgen). Once the tools have been successfully run,
|
||||||
|
# we are able to launch our network. More detail on the tools and the structure of
|
||||||
|
# the network will be provided later in this document. For now, let's get going...
|
||||||
|
|
||||||
|
# prepending $PWD/../bin to PATH to ensure we are picking up the correct binaries
|
||||||
|
# this may be commented out to resolve installed version of tools if desired
|
||||||
|
export PATH=${PWD}/../../bin:${PWD}:$PATH
|
||||||
|
export FABRIC_CFG_PATH=${PWD}
|
||||||
|
export VERBOSE=false
|
||||||
|
|
||||||
|
# Print the usage message
|
||||||
|
function printHelp() {
|
||||||
|
echo "Usage: "
|
||||||
|
echo " start_network.sh <mode> [-t <timeout>] [-i <imagetag>] [-v]"
|
||||||
|
echo " <mode> - one of 'up', 'down' or 'generate'"
|
||||||
|
echo " - 'up' - bring up the network with docker-compose up"
|
||||||
|
echo " - 'down' - clear the network with docker-compose down"
|
||||||
|
echo " - 'generate' - generate required certificates and genesis block"
|
||||||
|
echo " -t <timeout> - CLI timeout duration in seconds (defaults to 10)"
|
||||||
|
echo " -i <imagetag> - the tag to be used to launch the network (defaults to \"latest\")"
|
||||||
|
echo " -v - verbose mode"
|
||||||
|
echo
|
||||||
|
echo "Typically, one would first generate the required certificates and "
|
||||||
|
echo "genesis block, then bring up the network."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Obtain CONTAINER_IDS and remove them
|
||||||
|
# TODO Might want to make this optional - could clear other containers
|
||||||
|
function clearContainers() {
|
||||||
|
CONTAINER_IDS=$(docker ps -a | awk '($2 ~ /dev-.*irscc.*/) {print $1}')
|
||||||
|
if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then
|
||||||
|
echo "---- No containers available for deletion ----"
|
||||||
|
else
|
||||||
|
docker rm -f $CONTAINER_IDS
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete any images that were generated as a part of this setup
|
||||||
|
# specifically the following images are often left behind:
|
||||||
|
# TODO list generated image naming patterns
|
||||||
|
function removeUnwantedImages() {
|
||||||
|
DOCKER_IMAGE_IDS=$(docker images | awk '($1 ~ /dev.*irscc.*/) {print $3}')
|
||||||
|
if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then
|
||||||
|
echo "---- No images available for deletion ----"
|
||||||
|
else
|
||||||
|
docker rmi -f $DOCKER_IMAGE_IDS
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Versions of fabric known not to work with this release of first-network
|
||||||
|
BLACKLISTED_VERSIONS="^1\.0\. ^1\.1\.0-preview ^1\.1\.0-alpha"
|
||||||
|
|
||||||
|
# Do some basic sanity checking to make sure that the appropriate versions of fabric
|
||||||
|
# binaries/images are available. In the future, additional checking for the presence
|
||||||
|
# of go or other items could be added.
|
||||||
|
function checkPrereqs() {
|
||||||
|
# Note, we check configtxlator externally because it does not require a config file, and peer in the
|
||||||
|
# docker image because of FAB-8551 that makes configtxlator return 'development version' in docker
|
||||||
|
LOCAL_VERSION=$(configtxgen -version | sed -ne 's/ Version: //p')
|
||||||
|
DOCKER_IMAGE_VERSION=$(docker run --rm hyperledger/fabric-tools:$IMAGETAG peer version | sed -ne 's/ Version: //p' | head -1)
|
||||||
|
|
||||||
|
echo "LOCAL_VERSION=$LOCAL_VERSION"
|
||||||
|
echo "DOCKER_IMAGE_VERSION=$DOCKER_IMAGE_VERSION"
|
||||||
|
|
||||||
|
if [ "$LOCAL_VERSION" != "$DOCKER_IMAGE_VERSION" ]; then
|
||||||
|
echo "=================== WARNING ==================="
|
||||||
|
echo " Local fabric binaries and docker images are "
|
||||||
|
echo " out of sync. This may cause problems. "
|
||||||
|
echo "==============================================="
|
||||||
|
fi
|
||||||
|
|
||||||
|
for UNSUPPORTED_VERSION in $BLACKLISTED_VERSIONS; do
|
||||||
|
echo "$LOCAL_VERSION" | grep -q $UNSUPPORTED_VERSION
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "ERROR! Local Fabric binary version of $LOCAL_VERSION does not match this newer version of BYFN and is unsupported. Either move to a later version of Fabric or checkout an earlier version of fabric-samples."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$DOCKER_IMAGE_VERSION" | grep -q $UNSUPPORTED_VERSION
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "ERROR! Fabric Docker image version of $DOCKER_IMAGE_VERSION does not match this newer version of BYFN and is unsupported. Either move to a later version of Fabric or checkout an earlier version of fabric-samples."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate the needed certificates, the genesis block and start the network.
|
||||||
|
function networkUp() {
|
||||||
|
checkPrereqs
|
||||||
|
# generate artifacts if they don't exist
|
||||||
|
if [ ! -d "crypto-config" ]; then
|
||||||
|
generateCerts
|
||||||
|
generateChannelArtifacts
|
||||||
|
fi
|
||||||
|
IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d orderer partya partyb partyc auditor rrprovider cli 2>&1
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "ERROR !!!! Unable to start network"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# now run the end to end script
|
||||||
|
docker exec cli scripts/script.sh
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "ERROR !!!! Test failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tear down running network
|
||||||
|
function networkDown() {
|
||||||
|
# stop org3 containers also in addition to org1 and org2, in case we were running sample to add org3
|
||||||
|
docker-compose -f $COMPOSE_FILE down --volumes --remove-orphans
|
||||||
|
|
||||||
|
# Bring down the network, deleting the volumes
|
||||||
|
#Delete any ledger backups
|
||||||
|
docker run -v $PWD:/tmp/first-network --rm hyperledger/fabric-tools:$IMAGETAG rm -Rf /tmp/first-network/ledgers-backup
|
||||||
|
#Cleanup the chaincode containers
|
||||||
|
clearContainers
|
||||||
|
#Cleanup images
|
||||||
|
removeUnwantedImages
|
||||||
|
# remove orderer block and other channel configuration transactions and certs
|
||||||
|
rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generates Org certs using cryptogen tool
|
||||||
|
function generateCerts() {
|
||||||
|
which cryptogen
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
echo "cryptogen tool not found. exiting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "##### Generate certificates using cryptogen tool #########"
|
||||||
|
|
||||||
|
if [ -d "crypto-config" ]; then
|
||||||
|
rm -Rf crypto-config
|
||||||
|
fi
|
||||||
|
cryptogen generate --config=./crypto-config.yaml
|
||||||
|
res=$?
|
||||||
|
if [ $res -ne 0 ]; then
|
||||||
|
echo "Failed to generate certificates..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate orderer genesis block and channel configuration transaction with configtxgen
|
||||||
|
function generateChannelArtifacts() {
|
||||||
|
which configtxgen
|
||||||
|
if [ "$?" -ne 0 ]; then
|
||||||
|
echo "configtxgen tool not found. exiting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "######### Generating Orderer Genesis block ##############"
|
||||||
|
mkdir channel-artifacts
|
||||||
|
configtxgen -profile IRSNetGenesis -outputBlock ./channel-artifacts/genesis.block
|
||||||
|
res=$?
|
||||||
|
if [ $res -ne 0 ]; then
|
||||||
|
echo "Failed to generate orderer genesis block..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
echo "### Generating channel configuration transaction 'channel.tx' ###"
|
||||||
|
configtxgen -profile IRSChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
|
||||||
|
res=$?
|
||||||
|
if [ $res -ne 0 ]; then
|
||||||
|
echo "Failed to generate channel configuration transaction..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Obtain the OS and Architecture string that will be used to select the correct
|
||||||
|
# native binaries for your platform, e.g., darwin-amd64 or linux-amd64
|
||||||
|
OS_ARCH=$(echo "$(uname -s | tr '[:upper:]' '[:lower:]' | sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}')
|
||||||
|
CHANNEL_NAME="irs"
|
||||||
|
COMPOSE_FILE=docker-compose.yaml
|
||||||
|
COMPOSE_PROJECT_NAME=fabric-irs
|
||||||
|
#
|
||||||
|
# default image tag
|
||||||
|
IMAGETAG="latest"
|
||||||
|
# Parse commandline args
|
||||||
|
MODE=$1
|
||||||
|
shift
|
||||||
|
# Determine whether starting, stopping, generating
|
||||||
|
if [ "$MODE" == "up" ]; then
|
||||||
|
EXPMODE="Starting"
|
||||||
|
elif [ "$MODE" == "down" ]; then
|
||||||
|
EXPMODE="Stopping"
|
||||||
|
elif [ "$MODE" == "generate" ]; then
|
||||||
|
EXPMODE="Generating certs and genesis block"
|
||||||
|
else
|
||||||
|
printHelp
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while getopts "t:i:v" opt; do
|
||||||
|
case "$opt" in
|
||||||
|
t)
|
||||||
|
CLI_TIMEOUT=$OPTARG
|
||||||
|
;;
|
||||||
|
i)
|
||||||
|
IMAGETAG=$(go env GOARCH)"-"$OPTARG
|
||||||
|
;;
|
||||||
|
v)
|
||||||
|
VERBOSE=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Announce what was requested
|
||||||
|
echo "${EXPMODE} for channel '${CHANNEL_NAME}'"
|
||||||
|
|
||||||
|
#Create the network using docker compose
|
||||||
|
if [ "${MODE}" == "up" ]; then
|
||||||
|
networkUp
|
||||||
|
elif [ "${MODE}" == "down" ]; then ## Clear the network
|
||||||
|
networkDown
|
||||||
|
elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts
|
||||||
|
generateCerts
|
||||||
|
generateChannelArtifacts
|
||||||
|
else
|
||||||
|
printHelp
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
123
interest_rate_swaps/network/scripts/script.sh
Executable file
123
interest_rate_swaps/network/scripts/script.sh
Executable file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
DELAY="3"
|
||||||
|
TIMEOUT="10"
|
||||||
|
VERBOSE="false"
|
||||||
|
COUNTER=1
|
||||||
|
MAX_RETRY=5
|
||||||
|
|
||||||
|
CC_SRC_PATH="irscc/"
|
||||||
|
|
||||||
|
createChannel() {
|
||||||
|
CORE_PEER_LOCALMSPID=partya
|
||||||
|
CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/Admin@partya.example.com/msp
|
||||||
|
echo "===================== Creating channel ===================== "
|
||||||
|
peer channel create -o irs-orderer:7050 -c irs -f ./channel-artifacts/channel.tx
|
||||||
|
echo "===================== Channel created ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
joinChannel () {
|
||||||
|
for org in partya partyb partyc auditor rrprovider
|
||||||
|
do
|
||||||
|
CORE_PEER_LOCALMSPID=$org
|
||||||
|
CORE_PEER_ADDRESS=irs-$org:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/$org.example.com/users/Admin@$org.example.com/msp
|
||||||
|
echo "===================== Org $org joining channel ===================== "
|
||||||
|
peer channel join -b irs.block -o irs-orderer:7050
|
||||||
|
echo "===================== Channel joined ===================== "
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
installChaincode() {
|
||||||
|
for org in partya partyb partyc auditor rrprovider
|
||||||
|
do
|
||||||
|
CORE_PEER_LOCALMSPID=$org
|
||||||
|
CORE_PEER_ADDRESS=irs-$org:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/$org.example.com/users/Admin@$org.example.com/msp
|
||||||
|
echo "===================== Org $org installing chaincode ===================== "
|
||||||
|
peer chaincode install -n irscc -v 0 -l golang -p ${CC_SRC_PATH}
|
||||||
|
echo "===================== Org $org chaincode installed ===================== "
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
instantiateChaincode() {
|
||||||
|
CORE_PEER_LOCALMSPID=partya
|
||||||
|
CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/Admin@partya.example.com/msp
|
||||||
|
echo "===================== Instantiating chaincode ===================== "
|
||||||
|
peer chaincode instantiate -o irs-orderer:7050 -C irs -n irscc -l golang -v 0 -c '{"Args":["init","auditor","100000","rrprovider","myrr"]}' -P "AND(OR('partya.peer','partyb.peer','partyc.peer'), 'auditor.peer')"
|
||||||
|
echo "===================== Chaincode instantiated ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
setReferenceRate() {
|
||||||
|
CORE_PEER_LOCALMSPID=rrprovider
|
||||||
|
CORE_PEER_ADDRESS=irs-rrprovider:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/rrprovider.example.com/users/User1@rrprovider.example.com/msp
|
||||||
|
echo "===================== Invoking chaincode ===================== "
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-rrprovider:7051 -c '{"Args":["setReferenceRate","myrr","300"]}'
|
||||||
|
echo "===================== Chaincode invoked ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
createSwap() {
|
||||||
|
CORE_PEER_LOCALMSPID=partya
|
||||||
|
CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/User1@partya.example.com/msp
|
||||||
|
echo "===================== Invoking chaincode ===================== "
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":10,\"FixedRate\":400,\"FloatingRate\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}'
|
||||||
|
echo "===================== Chaincode invoked ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatePayment() {
|
||||||
|
CORE_PEER_LOCALMSPID=partya
|
||||||
|
CORE_PEER_ADDRESS=irs-partya:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partya.example.com/users/User1@partya.example.com/msp
|
||||||
|
echo "===================== Invoking chaincode ===================== "
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 -c '{"Args":["calculatePayment","myswap"]}'
|
||||||
|
echo "===================== Chaincode invoked ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
settlePayment() {
|
||||||
|
CORE_PEER_LOCALMSPID=partyb
|
||||||
|
CORE_PEER_ADDRESS=irs-partyb:7051
|
||||||
|
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/partyb.example.com/users/User1@partyb.example.com/msp
|
||||||
|
echo "===================== Invoking chaincode ===================== "
|
||||||
|
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 -c '{"Args":["settlePayment","myswap"]}'
|
||||||
|
echo "===================== Chaincode invoked ===================== "
|
||||||
|
}
|
||||||
|
|
||||||
|
## Create channel
|
||||||
|
sleep 1
|
||||||
|
echo "Creating channel..."
|
||||||
|
createChannel
|
||||||
|
|
||||||
|
## Join all the peers to the channel
|
||||||
|
echo "Having all peers join the channel..."
|
||||||
|
joinChannel
|
||||||
|
|
||||||
|
## Install chaincode on all peers
|
||||||
|
echo "Installing chaincode..."
|
||||||
|
installChaincode
|
||||||
|
|
||||||
|
# Instantiate chaincode
|
||||||
|
echo "Instantiating chaincode..."
|
||||||
|
instantiateChaincode
|
||||||
|
|
||||||
|
echo "Setting myrr reference rate"
|
||||||
|
sleep 3
|
||||||
|
setReferenceRate
|
||||||
|
|
||||||
|
echo "Creating swap between A and B"
|
||||||
|
createSwap
|
||||||
|
|
||||||
|
echo "Calculate payment information"
|
||||||
|
calculatePayment
|
||||||
|
|
||||||
|
echo "Mark payment settled"
|
||||||
|
settlePayment
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "========= IRS network sample setup completed =========== "
|
||||||
|
echo
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
Reference in a new issue