Skip to content

Commit cfe4c4a

Browse files
committed
signed transaction samples
Signed-off-by: May.Buzaglo <[email protected]>
1 parent d0816f1 commit cfe4c4a

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

common/tools/armageddon/armageddon.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,10 @@ func sendTx(txsMap *protectedMap, streams []ab.AtomicBroadcast_BroadcastClient,
13331333
}
13341334
}
13351335

1336+
// prepareTX prepares a transaction that includes:
1337+
// 1. A session number (a run ID)
1338+
// 2. A transaction number
1339+
// 3. A timestamp (in nanoseconds)
13361340
func prepareTx(txNumber int, txSize int, sessionNumber []byte) []byte {
13371341
// create timestamp (8 bytes)
13381342
timeStamp := uint64(time.Now().UnixNano())
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package armageddon
8+
9+
import (
10+
"crypto/ecdsa"
11+
"crypto/elliptic"
12+
"crypto/rand"
13+
"crypto/sha256"
14+
"crypto/x509"
15+
"fmt"
16+
"math/big"
17+
"os"
18+
19+
"github.com/hyperledger/fabric-protos-go-apiv2/common"
20+
"github.com/hyperledger/fabric-x-orderer/internal/cryptogen/ca"
21+
"github.com/hyperledger/fabric-x-orderer/node/crypto"
22+
)
23+
24+
// SignedTransactionService holds signed transactions with the cryptographic keys.
25+
type SignedTransactionService struct {
26+
txs []*common.Envelope
27+
privateKey *ecdsa.PrivateKey
28+
certificate *x509.Certificate
29+
}
30+
31+
func NewSignedTransactionService(numOfTxs int, txSize int) (*SignedTransactionService, error) {
32+
// Create private key and certificate used to sign and verify txs
33+
pk, cert, err := createSignerPKAndCert()
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
// Create signed transactions
39+
txs, err := createSignedTransactions(numOfTxs, txSize, (*crypto.ECDSASigner)(pk))
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
service := &SignedTransactionService{
45+
txs: txs,
46+
privateKey: pk,
47+
certificate: cert,
48+
}
49+
50+
return service, nil
51+
}
52+
53+
// GetRandomTransactionIndex returns a random number in [0, len(txs)).
54+
func (sts *SignedTransactionService) GetRandomTransactionIndex() (int, error) {
55+
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(sts.txs))))
56+
return int(n.Int64()), err
57+
}
58+
59+
// VerifyTransaction verifies the transaction[txIndex] in the transactions list.
60+
func (sts *SignedTransactionService) VerifyTransaction(txIndex int) bool {
61+
// Get the transaction from the list
62+
envelope := sts.txs[txIndex]
63+
digest := sha256.Sum256(envelope.Payload)
64+
65+
// Extract public key from the cert
66+
publicKey := sts.certificate.PublicKey.(*ecdsa.PublicKey)
67+
68+
// Verify the signature over the digest with the public key
69+
valid := ecdsa.VerifyASN1(publicKey, digest[:], envelope.Signature)
70+
return valid
71+
}
72+
73+
// createSignerPKAndCert create a CA, private key and a certificate.
74+
// NOTE: this function is based on Fabric internal methods.
75+
func createSignerPKAndCert() (*ecdsa.PrivateKey, *x509.Certificate, error) {
76+
// Create a fake CA
77+
dir, err := os.MkdirTemp("", "ca")
78+
if err != nil {
79+
return nil, nil, fmt.Errorf("failed to create a temp dir, err: %s", err)
80+
}
81+
defer os.RemoveAll(dir)
82+
83+
signCA, err := ca.NewCA(dir, "signCA", "ca", "US", "California", "San Francisco", "ARMA", "addr", "12345", "ecdsa")
84+
if err != nil {
85+
return nil, nil, fmt.Errorf("failed to create a fake CA, err: %s", err)
86+
}
87+
88+
// Create private key
89+
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
90+
if err != nil {
91+
return nil, nil, fmt.Errorf("failed to create a private key, err: %s", err)
92+
}
93+
94+
// Issue a certificate with the public key associated to the generated private key (the certificate contains the public key)
95+
cert, err := signCA.SignCertificate(dir, "signer", nil, nil, getPublicKey(privateKey), x509.KeyUsageDigitalSignature, []x509.ExtKeyUsage{})
96+
if err != nil {
97+
return nil, nil, fmt.Errorf("failed to create a certificate, err: %s", err)
98+
}
99+
100+
return privateKey, cert, err
101+
}
102+
103+
func createSignedTransactions(numOfTxs int, txSize int, signer *crypto.ECDSASigner) ([]*common.Envelope, error) {
104+
sessionNumber := make([]byte, 16)
105+
_, err := rand.Read(sessionNumber)
106+
if err != nil {
107+
return nil, fmt.Errorf("failed to create a session number, err: %s", err)
108+
}
109+
110+
txs := make([]*common.Envelope, numOfTxs)
111+
for i := 0; i < numOfTxs; i++ {
112+
payload := createPayload(i, txSize, sessionNumber)
113+
signedTx, err := signTransaction(payload, signer)
114+
if err != nil {
115+
return nil, fmt.Errorf("failed to sign transaction %d", i)
116+
}
117+
txs[i] = signedTx
118+
}
119+
120+
return txs, nil
121+
}
122+
123+
func createPayload(txNum int, txSize int, sessionNumber []byte) []byte {
124+
payload := prepareTx(txNum, txSize, sessionNumber)
125+
return payload
126+
}
127+
128+
func signTransaction(payload []byte, signer *crypto.ECDSASigner) (*common.Envelope, error) {
129+
signature, err := signer.Sign(payload)
130+
if err != nil {
131+
return nil, err
132+
}
133+
134+
envelope := &common.Envelope{
135+
Payload: payload,
136+
Signature: signature,
137+
}
138+
return envelope, nil
139+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package armageddon
8+
9+
import (
10+
"fmt"
11+
"testing"
12+
"time"
13+
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func TestSignedTransactionService(t *testing.T) {
18+
numOfTxs := 100
19+
txSize := 300
20+
service, err := NewSignedTransactionService(numOfTxs, txSize)
21+
require.NoError(t, err)
22+
23+
// verify random tx
24+
idx, err := service.GetRandomTransactionIndex()
25+
require.NoError(t, err)
26+
27+
isValid := service.VerifyTransaction(idx)
28+
require.True(t, isValid)
29+
30+
// verify all txs
31+
start := time.Now()
32+
for i := 0; i < numOfTxs; i++ {
33+
isValid = service.VerifyTransaction(i)
34+
require.True(t, isValid)
35+
}
36+
stop := time.Since(start)
37+
fmt.Printf("verifying %d txs took: %s\n", numOfTxs, stop)
38+
}

0 commit comments

Comments
 (0)