Skip to content

Commit

Permalink
Improve off-line signing documentation (#706)
Browse files Browse the repository at this point in the history
Highlight relationship between hash and off-line signing implementation.

Signed-off-by: Mark S. Lewis <Mark.S.Lewis@outlook.com>
  • Loading branch information
bestbeforetoday committed Apr 29, 2024
1 parent a83519a commit 400d523
Show file tree
Hide file tree
Showing 13 changed files with 61 additions and 43 deletions.
Expand Up @@ -71,11 +71,16 @@
* <li>Returning the serialized proposal, transaction or commit status message along with its digest to the client
* for them to generate a signature.</li>
* <li>On receipt of the serialized message and signature from the client, creating a signed proposal, transaction
* or commit using the Gateway's {@link Gateway#newSignedProposal(byte[], byte[])},
* or commit using the {@link Gateway#newSignedProposal(byte[], byte[])},
* {@link Gateway#newSignedTransaction(byte[], byte[])} or {@link Gateway#newSignedCommit(byte[], byte[])} methods
* respectively.</li>
* </ol>
*
* <p>Note that the message digest is created with the hash implementation specified by the
* {@link Gateway.Builder#hash(Function)} option used to create the {@link Gateway} instance. For off-line signing
* implementations that require the entire message content, a {@link Hash#NONE NONE} (or no-op) hash implementation should
* be specified.</p>
*
* <p>Signing of a proposal that can then be evaluated or endorsed:</p>
* <pre>{@code
* Proposal unsignedProposal = contract.newProposal("transactionName").build();
Expand Down
5 changes: 3 additions & 2 deletions java/src/main/java/org/hyperledger/fabric/client/Gateway.java
Expand Up @@ -228,9 +228,10 @@ interface Builder {
Builder signer(Signer signer);

/**
* Specify the hashing implementation used to generate digests of messages sent to the Fabric network.
* Specify the hashing implementation used to generate digests of messages sent to the Fabric network. If this
* option is not specified, SHA-256 is used by default.
*
* <p>Standard hash implementation are provided in {@link Hash}. The default value is {@link Hash#SHA256}.</p>
* <p>Standard hash implementations are provided in {@link Hash}.</p>
* @param hash A hashing function.
* @return The builder instance, allowing multiple configuration options to be chained.
*/
Expand Down
31 changes: 19 additions & 12 deletions node/src/contract.ts
Expand Up @@ -20,18 +20,6 @@ import { SubmittedTransaction } from './submittedtransaction';
* submitted using {@link evaluate} or {@link submit} respectively. The result of a submitted transaction can be
* accessed prior to its commit to the ledger using {@link submitAsync}.
*
* A finer-grained transaction flow can be employed by using {@link newProposal}. This allows retry of individual steps
* in the flow in response to errors.
*
* By default, proposal, transaction and commit status messages will be signed using the signing implementation
* specified when connecting the Gateway. In cases where an external client holds the signing credentials, a default
* signing implementation can be omitted and off-line signing can be carried out by:
* 1. Returning the serialized proposal, transaction or commit status message along with its digest to the client for
* them to generate a signature.
* 1. With the serialized message and signature received from the client to create a signed proposal, transaction or
* commit using the Gateway's {@link Gateway.newSignedProposal}, {@link Gateway.newSignedTransaction} or
* {@link Gateway.newSignedCommit} methods respectively.
*
* @example Evaluate transaction
* ```typescript
* const result = await contract.evaluate('transactionName', {
Expand Down Expand Up @@ -63,6 +51,9 @@ import { SubmittedTransaction } from './submittedtransaction';
* }
* ```
*
* A finer-grained transaction flow can be employed by using {@link newProposal}. This allows retry of individual steps
* in the flow in response to errors.
*
* @example Fine-grained submit transaction
* ```typescript
* const proposal = contract.newProposal('transactionName');
Expand All @@ -73,6 +64,22 @@ import { SubmittedTransaction } from './submittedtransaction';
* const status = await commit.getStatus();
* ```
*
* ## Off-line signing
*
* By default, proposal, transaction and commit status messages will be signed using the signing implementation
* specified when connecting the Gateway. In cases where an external client holds the signing credentials, a default
* signing implementation can be omitted and off-line signing can be carried out by:
* 1. Returning the serialized proposal, transaction or commit status message along with its digest to the client for
* them to generate a signature.
* 1. With the serialized message and signature received from the client to create a signed proposal, transaction or
* commit using the {@link Gateway.newSignedProposal}, {@link Gateway.newSignedTransaction} or
* {@link Gateway.newSignedCommit} methods respectively.
*
* Note that the message digest is created with the hash implementation specified by the
* {@link ConnectOptions.hash | hash} option passed to the {@link connect} function used to create the {@link Gateway}
* instance. For off-line signing implementations that require the entire message content, a {@link hash.none | none}
* (or no-op) hash implementation should be specified.
*
* @example Off-line signing
* ```typescript
* const unsignedProposal = contract.newProposal('transactionName');
Expand Down
3 changes: 2 additions & 1 deletion node/src/gateway.ts
Expand Up @@ -162,7 +162,8 @@ export interface ConnectOptions {
signer?: Signer;

/**
* Hash implementation used by the gateway to generate digital signatures.
* Hash implementation used by the gateway to generate digital signatures. If this option is not specified, SHA-256
* is used by default.
*/
hash?: Hash;

Expand Down
4 changes: 2 additions & 2 deletions node/src/identity/signers.ts
Expand Up @@ -22,8 +22,8 @@ import { Signer } from './signer';
* The P-256 and P-384 signers operate on a pre-computed message digest, and should be combined with an appropriate
* hash algorithm. P-256 is typically used with a SHA-256 hash, and P-384 is typically used with a SHA-384 hash.
*
* The Ed25519 signer operates on the full message content, and should be combined with a `none` (or no-op) hash
* implementation to ensure the complete message is passed to the signer.
* The Ed25519 signer operates on the full message content, and should be combined with a {@link hash.none | none} (or
* no-op) hash implementation to ensure the complete message is passed to the signer.
*
* @param key - A private key.
* @returns A signing implementation.
Expand Down
31 changes: 17 additions & 14 deletions pkg/client/contract.go
Expand Up @@ -11,27 +11,30 @@ import (
)

// Contract represents a smart contract, and allows applications to:
//
// - Evaluate transactions that query state from the ledger using the EvaluateTransaction() method.
//
// - Submit transactions that store state to the ledger using the SubmitTransaction() method.
// - Evaluate transactions that query state from the ledger using the [Contract.EvaluateTransaction] method.
// - Submit transactions that store state to the ledger using the [Contract.SubmitTransaction] method.
//
// For more complex transaction invocations, such as including transient data, transactions can be evaluated or
// submitted using the Evaluate() or Submit() methods respectively. The result of a submitted transaction can be
// accessed prior to its commit to the ledger using SubmitAsync().
// submitted using the [Contract.Evaluate] or [Contract.Submit] methods respectively. The result of a submitted
// transaction can be accessed prior to its commit to the ledger using [Contract.SubmitAsync].
//
// A finer-grained transaction flow can be employed by using [Contract.NewProposal]. This allows retry of individual
// steps in the flow in response to errors.
//
// A finer-grained transaction flow can be employed by using NewProposal(). This allows retry of individual steps in
// the flow in response to errors.
// # Off-line signing
//
// By default, proposal, transaction and commit status messages will be signed using the signing implementation
// specified when connecting the Gateway. In cases where an external client holds the signing credentials, a signing
// implementation can be omitted when connecting the Gateway and off-line signing can be carried out by:
//
// 1. Returning the serialized proposal, transaction or commit status message along with its digest to the client for
// them to generate a signature.
//
// 2. With the serialized message and signature received from the client to create a signed proposal, transaction or
// commit using the Gateway's NewSignedProposal(), NewSignedTransaction() or NewSignedCommit() methods respectively.
// 1. Returning the serialized proposal, transaction or commit status message along with its digest to the client for
// them to generate a signature.
// 2. With the serialized message and signature received from the client to create a signed proposal, transaction or
// commit using the [Gateway.NewSignedProposal], [Gateway.NewSignedTransaction] or [Gateway.NewSignedCommit] methods
// respectively.
//
// Note that the message digest is created with the hash implementation specified by the [WithHash] option passed to the
// [Connect] function used to create the [Gateway] instance. For off-line signing implementations that require the
// entire message content, a NONE (or no-op) hash implementation should be specified.
type Contract struct {
client *gatewayClient
signingID *signingIdentity
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/errors.go
Expand Up @@ -36,7 +36,7 @@ func newTransactionError(err error, transactionID string) *TransactionError {
}
}

// TransactionError represents an error invoking a transaction. This is a gRPC status error.
// TransactionError represents an error invoking a transaction. This is a gRPC [status] error.
type TransactionError struct {
*grpcError
TransactionID string
Expand Down
6 changes: 3 additions & 3 deletions pkg/client/filecheckpointer.go
Expand Up @@ -11,11 +11,11 @@ import (
"os"
)

// FileCheckpointer is a Checkpoint implementation backed by persistent file storage. It can be used to checkpoint
// FileCheckpointer is a [Checkpoint] implementation backed by persistent file storage. It can be used to checkpoint
// progress after successfully processing events, allowing eventing to be resumed from this point.
//
// Instances should be created using the NewFileCheckpointer() constructor function. Close() should be called when the
// checkpointer is no longer needed to free resources.
// Instances should be created using the [NewFileCheckpointer] constructor function. [FileCheckpointer.Close] should
// be called when the checkpointer is no longer needed to free resources.
type FileCheckpointer struct {
file *os.File
state *checkpointState
Expand Down
3 changes: 2 additions & 1 deletion pkg/client/gateway.go
Expand Up @@ -87,7 +87,8 @@ func WithSign(sign identity.Sign) ConnectOption {
}
}

// WithHash uses the supplied hashing implementation to generate digital signatures.
// WithHash uses the supplied hashing implementation to generate digital signatures. If this option is not specified,
// SHA-256 is used by default.
func WithHash(hash hash.Hash) ConnectOption {
return func(gw *Gateway) error {
gw.signingID.hash = hash
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/inmemorycheckpointer.go
Expand Up @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0

package client

// InMemoryCheckpointer is a non-persistent Checkpoint implementation. It can be used to checkpoint progress after
// InMemoryCheckpointer is a non-persistent [Checkpoint] implementation. It can be used to checkpoint progress after
// successfully processing events, allowing eventing to be resumed from this point.
type InMemoryCheckpointer struct {
blockNumber uint64
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/network.go
Expand Up @@ -15,7 +15,7 @@ import (

// Network represents a network of nodes that are members of a specific Fabric channel. The Network can be used to
// access deployed smart contracts, and to listen for events emitted when blocks are committed to the ledger. Network
// instances are obtained from a Gateway using the Gateway's GetNetwork() method.
// instances are obtained from a Gateway using the [Gateway.GetNetwork] method.
//
// To safely handle connection errors during eventing, it is recommended to use a checkpointer to track eventing
// progress. This allows eventing to be resumed with no loss or duplication of events.
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/proposalbuilder.go
Expand Up @@ -185,7 +185,7 @@ func stringsAsBytes(strings []string) [][]byte {
}

// WithTransient specifies the transient data associated with a transaction proposal.
// This is usually used in combination with WithEndorsingOrganizations for private data scenarios
// This is usually used in combination with [WithEndorsingOrganizations] for private data scenarios.
func WithTransient(transient map[string][]byte) ProposalOption {
return func(builder *proposalBuilder) error {
builder.transient = transient
Expand All @@ -194,7 +194,7 @@ func WithTransient(transient map[string][]byte) ProposalOption {
}

// WithEndorsingOrganizations specifies the organizations that should endorse the transaction proposal.
// No other organizations will be sent the proposal. This is usually used in combination with WithTransient
// No other organizations will be sent the proposal. This is usually used in combination with [WithTransient]
// for private data scenarios, or for state-based endorsement when specific organizations have to endorse the proposal.
func WithEndorsingOrganizations(mspids ...string) ProposalOption {
return func(builder *proposalBuilder) error {
Expand Down
4 changes: 2 additions & 2 deletions pkg/identity/sign.go
Expand Up @@ -19,8 +19,8 @@ type Sign = func(digest []byte) ([]byte, error)
// NewPrivateKeySign returns a Sign function that uses the supplied private key.
//
// Currently supported private key types are:
// - ECDSA.
// - Ed25519.
// - ECDSA.
// - Ed25519.
//
// Note that the Sign implementations have different expectations on the input data supplied to them.
//
Expand Down

0 comments on commit 400d523

Please sign in to comment.