Skip to content

Commit a513d68

Browse files
committed
signed transaction samples
Signed-off-by: May.Buzaglo <May.Buzaglo@ibm.com>
1 parent d0816f1 commit a513d68

File tree

3 files changed

+169
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)