Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions custom-endorser/Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
TOOLS_IMAGE ?= ghcr.io/hyperledger/fabric-x-tools:0.0.15
TOOLS = docker run --rm -v "$(PWD):/workspace" -w /workspace $(TOOLS_IMAGE)
COMMITTER_IMAGE ?= docker.io/hyperledger/fabric-x-committer-test-node:1.0.3

.PHONY: init-network
# The test committer ships its own crypto material (peer-org-0 / peer-org-1),
# genesis block and per-service configs. We extract it so the endorser, client
# and fxconfig use the same identities the committer trusts.
init-network:
rm -rf testdata/crypto
@docker pull $(TOOLS_IMAGE)
@$(TOOLS) cryptogen generate --config testdata/crypto-config.yaml --output testdata/crypto
@$(TOOLS) configtxgen -configPath testdata --channelID mychannel --profile OrgsChannel --outputBlock testdata/crypto/sc-genesis-block.proto.bin
@rm -rf testdata/crypto && mkdir -p testdata/crypto
@docker pull $(COMMITTER_IMAGE)
@cid=$$(docker create $(COMMITTER_IMAGE)); \
docker cp $$cid:/root/artifacts/. testdata/crypto; \
docker rm $$cid >/dev/null
@chmod -R a+rX testdata/crypto

.PHONY: clean
clean: stop
clean: stop-network
@rm -rf bin testdata/crypto

.PHONY: start-network
Expand Down
27 changes: 16 additions & 11 deletions custom-endorser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,40 @@ logic.
make build # compiles bin/endorser and bin/client
```

### 2. Generate crypto material and start the test network
### 2. Extract crypto material and start the test network

```shell
make init-network # generate TLS certs and MSP material (only once)
make start-network # start committer + orderer in Docker
make init-network # extract the committer's crypto material (only once)
make start-network # start committer + orderer in Docker and create the namespace
```

The test committer ships its own crypto material, genesis block and service configs.
`init-network` extracts that material into `testdata/crypto` so the endorser, client and
`fxconfig` all use the identities the committer already trusts.

### 3. Start the endorsers

Run each in a **separate terminal** — logs stream to stdout so you can see what's happening:

**Terminal 1**
```shell
./bin/endorser -c sampleconfig/endorser-org1.yaml
./bin/endorser -c sampleconfig/endorser1.yaml
```

**Terminal 2**
```shell
./bin/endorser -c sampleconfig/endorser-org2.yaml
./bin/endorser -c sampleconfig/endorser2.yaml
```

Each endorser logs `starting endorser` and then listens for proposals (endorser1 on `:9001`,
endorser2 on `:9002`).

> [!NOTE]
> Our "network" consists of two organizations, as defined in [testdata/crypto-config.yaml](./testdata/crypto-config.yaml).
> The fact that we need two endorsers is defined by the endorsement policy in
> fxconfig-init container: `--policy=AND('Org1MSP.member', 'Org2MSP.member')`. It means both
> organizations have to sign the read/write set for it to be accepted on the ledger.
> Our "network" consists of two organizations (`peer-org-0` and `peer-org-1`), whose crypto
> material is embedded in the test committer image. The fact that we need two endorsers is defined
> by the endorsement policy used when creating the namespace:
> `--policy=AND('peer-org-0.member', 'peer-org-1.member')`. It means both organizations have to
> sign the read/write set for it to be accepted on the ledger.

### 4. Send a transaction

Expand Down Expand Up @@ -115,7 +120,7 @@ modify the resulting read/write set directly before returning `endorsement.Succe
├── config/ # Configuration structures
├── sampleconfig/ # Sample config files (endorser1/2, client)
├── service/ # Service implementation and integration tests
├── testdata/ # Network config, crypto-config, and generated crypto material
├── testdata/ # Network config + crypto material extracted from the committer image
├── compose.yml # Docker Compose for committer + orderer
├── Makefile
├── go.mod
Expand All @@ -142,7 +147,7 @@ and crypto material used by the test environment.
Any config field can be overridden with the `ENDORSER_` prefix:

```shell
ENDORSER_SERVER_ENDPOINT_PORT=8080 ./bin/endorser -c sampleconfig/endorser-org1.yaml
ENDORSER_SERVER_ENDPOINT_PORT=8080 ./bin/endorser -c sampleconfig/endorser1.yaml
```

## Core Dependencies
Expand Down
8 changes: 6 additions & 2 deletions custom-endorser/cmd/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"os/signal"
"syscall"
"time"

"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-x-common/common/viperutil"
Expand Down Expand Up @@ -153,12 +154,15 @@ need to confirm that the transaction has been committed.`,
defer ec.Close() //nolint:errcheck

ordererConfs := []network.OrdererConf{cfg.Orderer.ToOrdererConf()}
// Broadcast is asynchronous, so wait after submitting to let the
// envelope reach the orderer (and the tx commit) before we close.
const submitWait = 2 * time.Second
var submitter *network.FabricSubmitter
switch cfg.Protocol {
case "fabric":
submitter, err = nfab.NewSubmitter(ordererConfs, signer, 0, logger)
submitter, err = nfab.NewSubmitter(cmd.Context(), ordererConfs, signer, submitWait, logger)
case "fabric-x", "":
submitter, err = nfabx.NewSubmitter(ordererConfs, signer, 0, logger)
submitter, err = nfabx.NewSubmitter(cmd.Context(), ordererConfs, signer, submitWait, logger)
default:
return fmt.Errorf("unknown protocol %q: must be \"fabric\" or \"fabric-x\"", cfg.Protocol)
}
Expand Down
4 changes: 2 additions & 2 deletions custom-endorser/cmd/endorser/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"syscall"

"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-x-committer/utils/connection"
"github.com/hyperledger/fabric-x-committer/utils/serve"
"github.com/hyperledger/fabric-x-common/common/viperutil"
"github.com/hyperledger/fabric-x-samples/custom-endorser/config"
"github.com/hyperledger/fabric-x-samples/custom-endorser/service"
Expand Down Expand Up @@ -100,7 +100,7 @@ func run(cmd *cobra.Command, args []string) error {
// - Service registration
// - Background task execution
// - Graceful shutdown on context cancellation
if err := connection.StartService(cmd.Context(), svc, cfg.Server); err != nil {
if err := serve.StartAndServe(cmd.Context(), svc, &serve.Config{GRPC: *cfg.Server}); err != nil {
return fmt.Errorf("service failed: %w", err)
}

Expand Down
65 changes: 28 additions & 37 deletions custom-endorser/compose.yml
Original file line number Diff line number Diff line change
@@ -1,65 +1,56 @@
name: fabric-x-test-committer

services:
# All-in-one test committer: embedded DB, mock orderer and the committer
# microservices in one container. Ships its own crypto and genesis block,
# so nothing is mounted in. The --add-host entries let the internal services
# reach each other on localhost.
test-committer:
image: ghcr.io/hyperledger/fabric-x-committer-test-node:0.1.9
image: docker.io/hyperledger/fabric-x-committer-test-node:1.0.3
pull_policy: missing
restart: unless-stopped
container_name: fabric-x-committer-test-node
command: run db orderer committer
ports:
- "4001:4001"
- "2110:2110"
- "2114:2114"
- "2117:2117"
- "7001:7001"
- "7050:7050"
- "5433:5433"
volumes:
- ./testdata/crypto:/root/config/crypto
- ./testdata/crypto/sc-genesis-block.proto.bin:/root/config/sc-genesis-block.proto.bin
- ./testdata/crypto/sc-genesis-block.proto.bin:/root/artifacts/config-block.pb.bin
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/server.crt:/server-certs/public-key.pem
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/server.key:/server-certs/private-key.pem
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/ca.crt:/server-certs/ca-certificate.pem
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/server.crt:/client-certs/public-key.pem
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/server.key:/client-certs/private-key.pem
- ./testdata/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/tls/ca.crt:/client-certs/ca-certificate.pem
extra_hosts:
- "coordinator:127.0.0.1"
- "verifier:127.0.0.1"
- "vc:127.0.0.1"
- "db:127.0.0.1"
environment:
SC_SIDECAR_ORDERER_IDENTITY_MSP_DIR: /root/config/crypto/peerOrganizations/Org1/peers/committer.org1.example.com/msp
SC_SIDECAR_ORDERER_IDENTITY_MSP_ID: Org1MSP
SC_SIDECAR_ORDERER_CHANNEL_ID: mychannel
SC_SIDECAR_ORDERER_SIGNED_ENVELOPES: "true"
SC_QUERY_SERVICE_SERVER_ENDPOINT: ":7001"
SC_ORDERER_BLOCK_SIZE: "1"
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 7001"]
test: ["CMD", "/root/bin/healthcheck"]
interval: 2s
timeout: 3s
retries: 30
start_period: 10s
networks:
default:
aliases:
- committer

# Creates the namespace. Shares the committer's network namespace so it can
# reach the services on localhost (matching their cert SANs). The namespace
# endorsement policy needs both orgs, so the transaction is endorsed by
# peer-org-0 and peer-org-1 before being submitted.
fxconfig-init:
image: ghcr.io/hyperledger/fabric-x-tools:0.0.15
image: docker.io/hyperledger/fabric-x-tools:1.0.0
pull_policy: missing
container_name: fxconfig-init
entrypoint:
- fxconfig
- namespace
- create
- mynamespace
- --policy=AND('Org1MSP.member', 'Org2MSP.member')
- --endorse
- --submit
- --wait
- --config=/testdata/fxconfig-docker.yaml
network_mode: "service:test-committer"
restart: "no"
volumes:
- ./testdata:/testdata
entrypoint:
- sh
- -c
- |
fxconfig namespace list --config=/testdata/fxconfig-docker.yaml 2>/dev/null | grep -q 'mynamespace' || ( \
fxconfig namespace create mynamespace --policy="AND('peer-org-0.member','peer-org-1.member')" --output /tmp/tx.json --config=/testdata/fxconfig-docker.yaml && \
fxconfig tx endorse /tmp/tx.json --output /tmp/tx.org0.json --config=/testdata/fxconfig-docker.yaml && \
FXCONFIG_MSP_LOCALMSPID=peer-org-1 FXCONFIG_MSP_CONFIGPATH=/testdata/crypto/peerOrganizations/peer-org-1.com/users/client@peer-org-1.com/msp \
fxconfig tx endorse /tmp/tx.org0.json --output /tmp/tx.org01.json --config=/testdata/fxconfig-docker.yaml && \
fxconfig tx submit /tmp/tx.org01.json --wait --config=/testdata/fxconfig-docker.yaml )
depends_on:
test-committer:
condition: service_healthy
restart: "no"
7 changes: 5 additions & 2 deletions custom-endorser/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"strconv"

"github.com/hyperledger/fabric-lib-go/common/flogging"
"github.com/hyperledger/fabric-x-committer/utils/connection"
"github.com/hyperledger/fabric-x-committer/utils/serve"
"github.com/hyperledger/fabric-x-sdk/network"
)

Expand All @@ -23,7 +23,7 @@ type Config struct {
ChannelID string `mapstructure:"channel-id"`

// Server is the main gRPC server configuration with TLS, rate limiting, etc.
Server *connection.ServerConfig `mapstructure:"server"`
Server *serve.ServerConfig `mapstructure:"server"`

// Committer is a client connection to a committing peer (sidecar in case of fabric-x).
Committer ClientConfig `mapstructure:"committer,omitempty"`
Expand Down Expand Up @@ -130,6 +130,9 @@ func (cfg Config) Validate() error {
if cfg.Committer.Endpoint == nil {
errs = append(errs, errors.New("committer.endpoint is required"))
}
if cfg.Server == nil {
errs = append(errs, errors.New("server configuration is required"))
}

return errors.Join(errs...)
}
45 changes: 23 additions & 22 deletions custom-endorser/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@

module github.com/hyperledger/fabric-x-samples/custom-endorser

go 1.26.2
go 1.26.3

require (
github.com/hyperledger/fabric-lib-go v1.1.3
github.com/hyperledger/fabric-lib-go v1.1.5-0.20260607181445-fc4b05c5d38f
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7
github.com/hyperledger/fabric-x-committer v0.1.9
github.com/hyperledger/fabric-x-common v0.1.1-0.20260219094834-26c5a49ed548
github.com/hyperledger/fabric-x-sdk v0.0.0-20260422070946-52f2faebbaff
github.com/hyperledger/fabric-x-committer v1.0.3
github.com/hyperledger/fabric-x-common v0.2.6
github.com/hyperledger/fabric-x-sdk v0.0.0-20260618080057-dddd57eee118
github.com/spf13/cobra v1.10.2
google.golang.org/grpc v1.80.0
google.golang.org/grpc v1.81.1
)

require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cockroachdb/errors v1.12.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/IBM/idemix v0.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cockroachdb/errors v1.13.0 // indirect
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
github.com/cockroachdb/redact v1.1.8 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
github.com/getsentry/sentry-go v0.46.2 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand All @@ -33,9 +35,8 @@ require (
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/miekg/pkcs11 v1.1.2 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
Expand All @@ -46,15 +47,15 @@ require (
github.com/sykesm/zap-logfmt v0.0.4 // indirect
github.com/yugabyte/pgx/v5 v5.7.6-yb-1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap v1.28.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
golang.org/x/time v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/net v0.54.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.44.0 // indirect
golang.org/x/text v0.37.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.70.0 // indirect
Expand Down
Loading