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
68 changes: 57 additions & 11 deletions common/tools/armageddon/armageddon.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/hyperledger/fabric-x-orderer/config"
genconfig "github.com/hyperledger/fabric-x-orderer/config/generate"
"github.com/hyperledger/fabric-x-orderer/node/comm"
"github.com/hyperledger/fabric-x-orderer/node/crypto"
"github.com/hyperledger/fabric-x-orderer/testutil/fabric"
"github.com/hyperledger/fabric-x-orderer/testutil/signutil"
"github.com/hyperledger/fabric-x-orderer/testutil/tx"
Expand Down Expand Up @@ -123,6 +124,7 @@ type CLI struct {
loadTransactions *int
loadRate *string
loadTxSize *int
loadSignedMode *string
// receive command flags
receiveUserConfigFile **os.File
receiveExpectedNumOfTxs *int
Expand Down Expand Up @@ -173,6 +175,7 @@ func (cli *CLI) configureCommands() {
cli.loadTransactions = load.Flag("transactions", "The number of transactions to be sent").Int()
cli.loadRate = load.Flag("rate", "The rate specifies the number of transactions per second to be sent as one or more rate numbers separated by space").String()
cli.loadTxSize = load.Flag("txSize", "The required transaction size in bytes").Int()
cli.loadSignedMode = load.Flag("signedMode", "Signing mode has three option: none = unsigned, full = sign including the full certificate, short = sign using known certificate").String()
commands["load"] = load

receive := cli.app.Command("receive", "Pull txs from some assembler and report statistics")
Expand Down Expand Up @@ -219,7 +222,7 @@ func (cli *CLI) Run(args []string) {

// "load" command
case cli.commands["load"].FullCommand():
load(cli.loadUserConfigFile, cli.loadTransactions, cli.loadRate, cli.loadTxSize)
load(cli.loadUserConfigFile, cli.loadTransactions, cli.loadRate, cli.loadTxSize, cli.loadSignedMode)

// "receive" command
case cli.commands["receive"].FullCommand():
Expand Down Expand Up @@ -478,7 +481,7 @@ func submit(userConfigFile **os.File, transactions *int, rate *int, txSize *int)
}

// load command makes txs and sends them to all routers
func load(userConfigFile **os.File, transactions *int, rate *string, txSize *int) {
func load(userConfigFile **os.File, transactions *int, rate *string, txSize *int, signedMode *string) {
rates := strings.Fields(*rate)
// check transaction size
txMinimumSize := 16 + 8 + 8
Expand All @@ -493,6 +496,9 @@ func load(userConfigFile **os.File, transactions *int, rate *string, txSize *int
fmt.Fprintf(os.Stderr, "Error reading config: %s", err)
os.Exit(-1)
}

logger.Infof("Load command uses signed transactions mode: %s", *signedMode)

convertedRates := make([]int, len(rates))
for i := 0; i < len(rates); i++ {
convertedRates[i], err = strconv.Atoi(rates[i])
Expand All @@ -504,13 +510,13 @@ func load(userConfigFile **os.File, transactions *int, rate *string, txSize *int
// send txs to the routers
for i := 0; i < len(rates); i++ {
start := time.Now()
SendTxsToAllAvailableRouters(userConfig, *transactions, convertedRates[i], *txSize, nil)
SendTxsToAllAvailableRouters(userConfig, *transactions, convertedRates[i], *txSize, nil, *signedMode)
elapsed := time.Since(start)
reportLoadResults(*transactions, elapsed, *txSize)
}
}

func SendTxsToAllAvailableRouters(userConfig *UserConfig, numOfTxs int, rate int, txSize int, txsMap *protectedMap) {
func SendTxsToAllAvailableRouters(userConfig *UserConfig, numOfTxs int, rate int, txSize int, txsMap *protectedMap, signedMode string) {
broadcastClient := NewBroadcastTxClient(userConfig)
err := broadcastClient.InitStreams()
if err != nil {
Expand Down Expand Up @@ -541,16 +547,56 @@ func SendTxsToAllAvailableRouters(userConfig *UserConfig, numOfTxs int, rate int
go ReceiveResponseFromRouter(userConfig, streamInfo)
}

for i := 0; i < numOfTxs; i++ {
env := tx.PrepareEnvWithTimestamp(i, txSize, sessionNumber)
var signer *crypto.ECDSASigner
var certBytes []byte
var org string
var env *common.Envelope

status := rl.GetToken()
if !status {
fmt.Fprintf(os.Stderr, "failed to send tx %d", i+1)
if signedMode == "full" || signedMode == "short" {
org, err = signutil.GetMspIDfromDir(userConfig.MSPDir)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to get mspID from user msp dir: %v", err)
os.Exit(3)
}

broadcastClient.SendTxToAllRouters(env)
signer, certBytes, err = signutil.LoadCryptoMaterialForSigner(userConfig.MSPDir)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load crypto materials: %v", err)
os.Exit(3)
}
}

switch signedMode {
case "full":
for i := 0; i < numOfTxs; i++ {
env = tx.PrepareSignedEnvelopeWithCertificate(i, txSize, sessionNumber, signer, certBytes, org)
status := rl.GetToken()
if !status {
fmt.Fprintf(os.Stderr, "failed to send tx %d in signed mode %s", i+1, signedMode)
os.Exit(3)
}
broadcastClient.SendTxToAllRouters(env)
}
case "short":
for i := 0; i < numOfTxs; i++ {
env = tx.PrepareSignedEnvelopeWithCertificateID(i, txSize, sessionNumber, signer, certBytes, org)
status := rl.GetToken()
if !status {
fmt.Fprintf(os.Stderr, "failed to send tx %d in signed mode %s", i+1, signedMode)
os.Exit(3)
}
broadcastClient.SendTxToAllRouters(env)
}
default:
for i := 0; i < numOfTxs; i++ {
env = tx.PrepareUnsignedEnvelope(i, txSize, sessionNumber)
status := rl.GetToken()
if !status {
fmt.Fprintf(os.Stderr, "failed to send tx %d in unsigned mode", i+1)
os.Exit(3)
}
broadcastClient.SendTxToAllRouters(env)
}
}
rl.Stop()

Expand Down Expand Up @@ -902,7 +948,7 @@ func calculateDelayOfTx(data []byte, acceptedTime time.Time) time.Duration {
}

func sendTx(txsMap *protectedMap, streams []ab.AtomicBroadcast_BroadcastClient, i int, txSize int, sessionNumber []byte) {
env := tx.PrepareEnvWithTimestamp(i, txSize, sessionNumber)
env := tx.PrepareUnsignedEnvelope(i, txSize, sessionNumber)
data, _ := tx.GetDataFromEnvelope(env)
if txsMap != nil {
logger.Debugf("Add tx %x to the map", data)
Expand Down
58 changes: 58 additions & 0 deletions common/tools/armageddon/armageddon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,64 @@ func TestLoadAndReceive(t *testing.T) {
waitForTxToBeSentAndReceived.Wait()
}

// Scenario:
// 1. Create a config YAML file to be an input to armageddon
// 2. Run armageddon generate command to create config files in a folder structure
// 3. Run arma with the generated config files to run each of the nodes for all parties
// 4. Run armageddon load command with --signedMode=full
// 5. Run armageddon receive command to verify transactions are received
//
// This test verifies that the --signedMode flag works correctly with the load and receive commands.
// It does NOT verify cryptographic signature correctness, and test only the full option
// TODO: add another test for "--signedMode=short" as well.
func TestLoadAndReceiveSignedModeAsFull(t *testing.T) {
dir, err := os.MkdirTemp("", t.Name())
require.NoError(t, err)
defer os.RemoveAll(dir)

// 1. Create network config
configPath := filepath.Join(dir, "config.yaml")
netInfo := testutil.CreateNetwork(t, configPath, 4, 2, "none", "none")
defer netInfo.CleanUp()

// 2. Generate armageddon config files
armageddonCLI := armageddon.NewCLI()
armageddonCLI.Run([]string{"generate", "--config", configPath, "--output", dir})

// 3. Build and run arma nodes
armaBinaryPath, err := gexec.BuildWithEnvironment("github.com/hyperledger/fabric-x-orderer/cmd/arma", []string{"GOPRIVATE=" + os.Getenv("GOPRIVATE")})
require.NoError(t, err)
require.NotNil(t, armaBinaryPath)

readyChan := make(chan string, 20)
armaNetwork := testutil.RunArmaNodes(t, dir, armaBinaryPath, readyChan, netInfo)
defer armaNetwork.Stop()

testutil.WaitReady(t, readyChan, 20, 10)

// 4. + 5. Run load and receive commands in parallel
userConfigPath := path.Join(dir, "config", "party1", "user_config.yaml")
rate := "100"
txs := "200"
txSize := "128"
signedMode := "full"

var waitForLoadAndReceive sync.WaitGroup
waitForLoadAndReceive.Add(2)

go func() {
defer waitForLoadAndReceive.Done()
armageddonCLI.Run([]string{"load", "--config", userConfigPath, "--transactions", txs, "--rate", rate, "--txSize", txSize, "--signedMode", signedMode})
}()

go func() {
defer waitForLoadAndReceive.Done()
armageddonCLI.Run([]string{"receive", "--config", userConfigPath, "--pullFromPartyId", "1", "--expectedTxs", txs, "--output", dir})
}()

waitForLoadAndReceive.Wait()
}

// Scenario:
// 1. Create a config YAML file to be an input to armageddon
// 2. Run armageddon generate command to create config files in a folder structure
Expand Down
3 changes: 3 additions & 0 deletions docs/cli/armageddon.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ Flags:
per second to be sent as one or more rate numbers
separated by space
--txSize=TXSIZE The required transaction size in bytes
--signedMode=SIGNEDMODE Signing mode has three option: none = unsigned,
full = sign including the full certificate,
short = sign using known certificate
```


Expand Down
20 changes: 18 additions & 2 deletions testutil/signutil/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func CreateTestSigner(t *testing.T, mspID, dir string) *TestSigner {
}

func CreateSignerForUser(userMspDir string) (identity.SignerSerializer, error) {
mspID, err := getMspIDfromDir(userMspDir)
mspID, err := GetMspIDfromDir(userMspDir)
if err != nil {
return nil, fmt.Errorf("failed to get mspID from user msp dir: %s, err: %v", userMspDir, err)
}
Expand All @@ -93,7 +93,23 @@ func CreateSignerForUser(userMspDir string) (identity.SignerSerializer, error) {
return signer, nil
}

func getMspIDfromDir(mspDir string) (string, error) {
func LoadCryptoMaterialForSigner(mspDir string) (*crypto.ECDSASigner, []byte, error) {
keyBytes, err := os.ReadFile(filepath.Join(mspDir, "keystore", "priv_sk"))
if err != nil {
return nil, nil, fmt.Errorf("failed to read private key file: %w", err)
}
privateKey, err := tx.CreateECDSAPrivateKey(keyBytes)
if err != nil {
return nil, nil, fmt.Errorf("failed to create private key: %w", err)
}
certBytes, err := os.ReadFile(filepath.Join(mspDir, "signcerts", "sign-cert.pem"))
if err != nil {
return nil, nil, fmt.Errorf("failed to read sign certificate: %w", err)
}
return (*crypto.ECDSASigner)(privateKey), certBytes, nil
}

func GetMspIDfromDir(mspDir string) (string, error) {
re := regexp.MustCompile(`/ordererOrganizations/([^/]+)/`)
matches := re.FindStringSubmatch(mspDir)
if matches == nil || len(matches) > 2 {
Expand Down
19 changes: 17 additions & 2 deletions testutil/tx/tx_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,9 @@ var headersOverheadSize = func() int {
return len(envBytes) - size
}()

// PrepareEnvWithTimestamp prepares an envelope of size envSize, accounting for the overhead introduced by additional headers in the transaction.
func PrepareEnvWithTimestamp(txNumber int, envSize int, sessionNumber []byte) *common.Envelope {
// PrepareUnsignedEnvelope prepares an envelope of size envSize, accounting for the overhead introduced by additional headers in the transaction.
// this method will be used for creating envelope for unsigned signing mode.
func PrepareUnsignedEnvelope(txNumber int, envSize int, sessionNumber []byte) *common.Envelope {
var dataSize int

overheadSize := headersOverheadSize
Expand All @@ -222,6 +223,20 @@ func PrepareEnvWithTimestamp(txNumber int, envSize int, sessionNumber []byte) *c
return CreateStructuredEnvelope(data)
}

// PrepareSignedEnvelopeWithCertificate prepares an fully signed envelope.
// this method will be used for creating signed envelope for full signing mode.
func PrepareSignedEnvelopeWithCertificate(txNumber int, envSize int, sessionNumber []byte, signer *crypto.ECDSASigner, certBytes []byte, org string) *common.Envelope {
data := PrepareTxWithTimestamp(txNumber, envSize, sessionNumber)
return CreateSignedStructuredEnvelope(data, signer, certBytes, org)
}

// TODO: implement the following method:
// PrepareSignedEnvelopeWithCertificateID prepares an signed envelope using known certificates which are referenced by hash.
// this method will be used for creating signed envelope for short signing mode.
func PrepareSignedEnvelopeWithCertificateID(txNumber int, envSize int, sessionNumber []byte, signer *crypto.ECDSASigner, certBytes []byte, org string) *common.Envelope {
return nil
}

func CreateSignedStructuredEnvelope(data []byte, signer *crypto.ECDSASigner, certBytes []byte, org string) *common.Envelope {
payload := createSignedStructuredPayload(data, certBytes, org)
payloadBytes := deterministicMarshall(payload)
Expand Down
15 changes: 4 additions & 11 deletions testutil/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
nodeconfig "github.com/hyperledger/fabric-x-orderer/node/config"
"github.com/hyperledger/fabric-x-orderer/node/crypto"
configMocks "github.com/hyperledger/fabric-x-orderer/test/mocks"
"github.com/hyperledger/fabric-x-orderer/testutil/tx"
"github.com/hyperledger/fabric-x-orderer/testutil/signutil"
"github.com/onsi/gomega/gexec"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -694,16 +694,9 @@ func sortArmaNodeInfo(infos []*ArmaNodeInfo) func(i, j int) bool {
}

func LoadCryptoMaterialsFromDir(t *testing.T, mspDir string) (*crypto.ECDSASigner, []byte, error) {
Comment thread
moran-abilea marked this conversation as resolved.
keyBytes, err := os.ReadFile(filepath.Join(mspDir, "keystore", "priv_sk"))
require.NoError(t, err, "failed to read private key file")

privateKey, err := tx.CreateECDSAPrivateKey(keyBytes)
require.NoError(t, err, "failed to create private key")

certBytes, err := os.ReadFile(filepath.Join(mspDir, "signcerts", "sign-cert.pem"))
require.NoError(t, err, "failed to read sign certificate file")

return (*crypto.ECDSASigner)(privateKey), certBytes, nil
signer, certBytes, err := signutil.LoadCryptoMaterialForSigner(mspDir)
require.NoError(t, err)
return signer, certBytes, nil
}

func CreateAssemblerBundleForTest(sequence uint64) channelconfig.Resources {
Expand Down
Loading