github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/server/idemix/nonce_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package idemix_test
     8  
     9  import (
    10  	"database/sql"
    11  	"testing"
    12  	"time"
    13  
    14  	. "github.com/hyperledger/fabric-ca/lib/server/idemix"
    15  	"github.com/hyperledger/fabric-ca/lib/server/idemix/mocks"
    16  	dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks"
    17  	"github.com/hyperledger/fabric-ca/util"
    18  	"github.com/hyperledger/fabric/idemix"
    19  	"github.com/pkg/errors"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  func TestNewNonceManager(t *testing.T) {
    24  	issuer := new(mocks.MyIssuer)
    25  	issuer.On("Name").Return("ca1")
    26  	opts := &Config{
    27  		NonceExpiration:    "15",
    28  		NonceSweepInterval: "15",
    29  	}
    30  	clock := new(mocks.Clock)
    31  	lib := new(mocks.Lib)
    32  	issuer.On("Config").Return(opts)
    33  	issuer.On("IdemixLib").Return(lib)
    34  	_, err := NewNonceManager(issuer, clock, 1)
    35  	assert.Error(t, err, "NewNonceManager should return error if the NonceExpiration config option is not in time.Duration string format")
    36  	assert.Contains(t, err.Error(), "Failed to parse idemix.nonceexpiration config option while initializing Nonce manager for Issuer 'ca1'")
    37  
    38  	opts.NonceExpiration = "15s"
    39  	_, err = NewNonceManager(issuer, clock, 1)
    40  	assert.Error(t, err, "NewNonceManager should return error if the NonceSweepInterval config option is not in time.Duration string format")
    41  	assert.Contains(t, err.Error(), "Failed to parse idemix.noncesweepinterval config option while initializing Nonce manager for Issuer 'ca1'")
    42  
    43  	opts.NonceSweepInterval = "15m"
    44  	_, err = NewNonceManager(issuer, clock, 1)
    45  	assert.NoError(t, err)
    46  }
    47  
    48  func TestGetNonce(t *testing.T) {
    49  	issuer := new(mocks.MyIssuer)
    50  	issuer.On("Name").Return("ca1")
    51  
    52  	lib := new(mocks.Lib)
    53  	rnd, err := idemix.GetRand()
    54  	if err != nil {
    55  		t.Fatalf("Error generating a random number")
    56  	}
    57  	rmo := idemix.RandModOrder(rnd)
    58  	lib.On("RandModOrder", rnd).Return(rmo, nil)
    59  
    60  	issuer.On("IdemixRand").Return(rnd)
    61  
    62  	noncestr := util.B64Encode(idemix.BigToBytes(rmo))
    63  	now := time.Now()
    64  	nonceObj := &Nonce{
    65  		Val:    noncestr,
    66  		Expiry: now.UTC().Add(time.Second * 15),
    67  		Level:  1,
    68  	}
    69  
    70  	numResultForRowsAffectedCalls := 0
    71  	f1 := getResultForRowsAffectedFunc(&numResultForRowsAffectedCalls)
    72  	result := new(dmocks.Result)
    73  	result.On("RowsAffected").Return(f1, nil)
    74  
    75  	db := new(dmocks.FabricCADB)
    76  	numResultForInsertNonceCalls := 0
    77  	numErrorForInsertNonceCalls := 0
    78  	f2 := getResultForInsertNonceFunc(result, &numResultForInsertNonceCalls)
    79  	f3 := getErrorForInsertNonceFunc(result, &numErrorForInsertNonceCalls)
    80  	db.On("NamedExec", "InsertNonce", InsertNonce, nonceObj).Return(f2, f3)
    81  	issuer.On("DB").Return(db)
    82  
    83  	opts := &Config{
    84  		NonceExpiration:    "15s",
    85  		NonceSweepInterval: "15m",
    86  	}
    87  	clock := new(mocks.Clock)
    88  	clock.On("Now").Return(now)
    89  	issuer.On("Config").Return(opts)
    90  	issuer.On("IdemixLib").Return(lib)
    91  	nm, err := NewNonceManager(issuer, clock, 1)
    92  
    93  	_, err = nm.GetNonce()
    94  	assert.Error(t, err, "Executing insert SQL should return an error")
    95  	if err != nil {
    96  		assert.Contains(t, err.Error(), "Failed to add nonce to the datastore")
    97  	}
    98  
    99  	_, err = nm.GetNonce()
   100  	assert.Error(t, err, "Get rows affected from result should return an error")
   101  	if err != nil {
   102  		assert.Contains(t, err.Error(), "Failed to add nonce to the datastore; no rows affected")
   103  	}
   104  
   105  	_, err = nm.GetNonce()
   106  	assert.Error(t, err, "Get rows affected from result should return an error")
   107  	if err != nil {
   108  		assert.Contains(t, err.Error(), "Expected to affect 1 entry in revocation component info table but affected")
   109  	}
   110  
   111  	_, err = nm.GetNonce()
   112  	assert.NoError(t, err)
   113  }
   114  
   115  func TestCheckNonce(t *testing.T) {
   116  	issuer := new(mocks.MyIssuer)
   117  	issuer.On("Name").Return("ca1")
   118  
   119  	lib := new(mocks.Lib)
   120  	rnd, err := idemix.GetRand()
   121  	if err != nil {
   122  		t.Fatalf("Error generating a random number")
   123  	}
   124  	rmo := idemix.RandModOrder(rnd)
   125  	lib.On("RandModOrder", rnd).Return(rmo)
   126  
   127  	issuer.On("IdemixRand").Return(rnd)
   128  	issuer.On("GetIdemixLib").Return(lib)
   129  	noncestr := util.B64Encode(idemix.BigToBytes(rmo))
   130  
   131  	db := new(dmocks.FabricCADB)
   132  	tx := new(dmocks.FabricCATx)
   133  	tx.On("Commit", "CheckNonce").Return(nil)
   134  	tx.On("Rollback", "CheckNonce").Return(nil)
   135  	nonces := []Nonce{}
   136  	tx.On("Rebind", SelectNonce).Return(SelectNonce)
   137  	db.On("BeginTx").Return(tx)
   138  	numTxSelectCalls := 0
   139  	f := getTxSelectNonceFunc(&nonces, noncestr, &numTxSelectCalls)
   140  	tx.On("Select", "GetNonce", &nonces, SelectNonce, noncestr).Return(f)
   141  	numTxRemoveResultCalls := 0
   142  	numTxRemoveErrorCalls := 0
   143  	tx.On("Rebind", RemoveNonce).Return(RemoveNonce)
   144  	f1 := getTxRemoveNonceResultFunc(noncestr, &numTxRemoveResultCalls)
   145  	f2 := getTxRemoveNonceErrorFunc(&numTxRemoveErrorCalls)
   146  	tx.On("Exec", "GetNonce", RemoveNonce, noncestr).Return(f1, f2)
   147  	issuer.On("DB").Return(db)
   148  
   149  	opts := &Config{
   150  		NonceExpiration:    "15s",
   151  		NonceSweepInterval: "15m",
   152  	}
   153  	issuer.On("Config").Return(opts)
   154  	now := time.Now()
   155  	clock := new(mocks.Clock)
   156  	clock.On("Now").Return(now)
   157  	nm, err := NewNonceManager(issuer, clock, 1)
   158  	if err != nil {
   159  		t.Fatalf("Failed to get new instance of Nonce Manager")
   160  	}
   161  	err = nm.CheckNonce(rmo)
   162  	assert.Error(t, err)
   163  	assert.Contains(t, err.Error(), "Failed to retrieve nonce from the datastore")
   164  
   165  	err = nm.CheckNonce(rmo)
   166  	assert.Error(t, err)
   167  	assert.Contains(t, err.Error(), "Nonce not found in the datastore")
   168  
   169  	err = nm.CheckNonce(rmo)
   170  	assert.Error(t, err)
   171  	assert.Contains(t, err.Error(), "Nonce is either unknown or has expired")
   172  
   173  	err = nm.CheckNonce(rmo)
   174  	assert.NoError(t, err)
   175  
   176  	err = nm.CheckNonce(rmo)
   177  	assert.NoError(t, err)
   178  }
   179  
   180  func TestSweepExpiredNonces(t *testing.T) {
   181  	issuer := new(mocks.MyIssuer)
   182  	issuer.On("Name").Return("ca1")
   183  	now := time.Now()
   184  
   185  	numRemoveExpiredNoncesErrorFuncCalls := 0
   186  	f := getRemoveExpiredNoncesErrorFunc(&numRemoveExpiredNoncesErrorFuncCalls)
   187  	db := new(dmocks.FabricCADB)
   188  	db.On("Rebind", RemoveExpiredNonces).Return(RemoveExpiredNonces)
   189  	db.On("Exec", "RemoveExpiredNonces", RemoveExpiredNonces, now.UTC()).Return(nil, f) // errors.New("error"))
   190  	issuer.On("DB").Return(db)
   191  
   192  	lib := new(mocks.Lib)
   193  	opts := &Config{
   194  		NonceExpiration:    "15s",
   195  		NonceSweepInterval: "15m",
   196  	}
   197  	issuer.On("Config").Return(opts)
   198  	issuer.On("IdemixLib").Return(lib)
   199  	clock := new(mocks.Clock)
   200  	clock.On("Now").Return(now)
   201  	nm, err := NewNonceManager(issuer, clock, 1)
   202  	if err != nil {
   203  		t.Fatalf("Failed to get new instance of Nonce Manager")
   204  	}
   205  	err = nm.SweepExpiredNonces()
   206  	assert.Error(t, err)
   207  	if err != nil {
   208  		assert.Contains(t, err.Error(), "Failed to remove expired nonces from DB")
   209  	}
   210  
   211  	err = nm.SweepExpiredNonces()
   212  	assert.NoError(t, err)
   213  }
   214  
   215  func getResultForInsertNonceFunc(result sql.Result, numResultForInsertNonceCalls *int) func(string, string, interface{}) sql.Result {
   216  	return func(funcName string, query string, args interface{}) sql.Result {
   217  		if *numResultForInsertNonceCalls == 0 {
   218  			*numResultForInsertNonceCalls = *numResultForInsertNonceCalls + 1
   219  			return nil
   220  		}
   221  		return result
   222  
   223  	}
   224  }
   225  func getErrorForInsertNonceFunc(result sql.Result, numErrorForInsertNonceCalls *int) func(string, string, interface{}) error {
   226  	return func(funcName string, query string, args interface{}) error {
   227  		if *numErrorForInsertNonceCalls == 0 {
   228  			*numErrorForInsertNonceCalls = *numErrorForInsertNonceCalls + 1
   229  			return errors.New("Error executing insert")
   230  		}
   231  		return nil
   232  	}
   233  }
   234  func getResultForRowsAffectedFunc(numResultForRowsAffectedCalls *int) func() int64 {
   235  	return func() int64 {
   236  		if *numResultForRowsAffectedCalls == 0 {
   237  			*numResultForRowsAffectedCalls = *numResultForRowsAffectedCalls + 1
   238  			return int64(0)
   239  		}
   240  		if *numResultForRowsAffectedCalls == 1 {
   241  			*numResultForRowsAffectedCalls = *numResultForRowsAffectedCalls + 1
   242  			return int64(2)
   243  		}
   244  		return int64(1)
   245  	}
   246  }
   247  
   248  func getTxSelectNonceFunc(nonces *[]Nonce, noncestr string, numTxSelectCalls *int) func(string, interface{}, string, ...interface{}) error {
   249  	return func(funcName string, dest interface{}, query string, args ...interface{}) error {
   250  		if *numTxSelectCalls == 0 {
   251  			*numTxSelectCalls = *numTxSelectCalls + 1
   252  			return errors.New("Getting a nonce from DB failed")
   253  		}
   254  		if *numTxSelectCalls == 1 {
   255  			*numTxSelectCalls = *numTxSelectCalls + 1
   256  			return nil
   257  		}
   258  
   259  		destNonces, _ := dest.(*[]Nonce)
   260  		if *numTxSelectCalls == 2 {
   261  			*destNonces = append(*destNonces, Nonce{
   262  				Val:    noncestr,
   263  				Expiry: time.Now().Add(-1 * time.Minute),
   264  			})
   265  		}
   266  		*destNonces = append(*destNonces, Nonce{
   267  			Val:    noncestr,
   268  			Expiry: time.Now().Add(time.Minute),
   269  		})
   270  		*numTxSelectCalls = *numTxSelectCalls + 1
   271  		return nil
   272  	}
   273  }
   274  
   275  func getTxRemoveNonceResultFunc(noncestr string, numTxRemoveResultCalls *int) func(string, string, ...interface{}) sql.Result {
   276  	return func(funcName, query string, args ...interface{}) sql.Result {
   277  		if *numTxRemoveResultCalls == 0 {
   278  			*numTxRemoveResultCalls = *numTxRemoveResultCalls + 1
   279  			return nil
   280  		}
   281  		result := new(dmocks.Result)
   282  		if *numTxRemoveResultCalls == 1 {
   283  			result.On("RowsAffected").Return(int64(2), nil)
   284  			*numTxRemoveResultCalls = *numTxRemoveResultCalls + 1
   285  			return result
   286  		}
   287  		result.On("RowsAffected").Return(int64(1), nil)
   288  		return result
   289  	}
   290  }
   291  
   292  func getTxRemoveNonceErrorFunc(numTxRemoveErrorCalls *int) func(string, string, ...interface{}) error {
   293  	return func(funcName, query string, args ...interface{}) error {
   294  		if *numTxRemoveErrorCalls == 0 {
   295  			*numTxRemoveErrorCalls = *numTxRemoveErrorCalls + 1
   296  			return errors.New("Removing nonce from DB failed")
   297  		}
   298  		return nil
   299  	}
   300  }
   301  
   302  func getRemoveExpiredNoncesErrorFunc(numRemoveExpiredNoncesErrorFuncCalls *int) func(string, string, ...interface{}) error {
   303  	return func(string, string, ...interface{}) error {
   304  		if *numRemoveExpiredNoncesErrorFuncCalls == 0 {
   305  			*numRemoveExpiredNoncesErrorFuncCalls = *numRemoveExpiredNoncesErrorFuncCalls + 1
   306  			return errors.New("Failed to remove expired nonces from DB")
   307  		}
   308  		return nil
   309  	}
   310  }