NonceRefreshingTxRequester implements TxRequester { @Override public TxHash request(final AergoClient aergoClient, final Signer signer, final TxRequestFunction requestFunction) throws Exception { assertNotNull(aergoClient, "AergoClient must not null"); assertNotNull(signer, "Signer must not null"); assertNotNull(requestFunction, "RequestFunction must not null"); logger.debug("Transaction try with signer: {}, requestFunction: {}", signer, requestFunction); TxHash txHash = null; Exception error = null; final long sleepInterval = tryCountAndInterval.getInterval().toMilliseconds(); int count = tryCountAndInterval.getCount(); while (0 <= count && null == txHash) { final long nonce = nonceProvider.incrementAndGetNonce(signer.getPrincipal()); try { txHash = requestFunction.apply(signer, nonce); } catch (Exception e) { error = e; if (isNonceRelatedException(e)) { final CommitException commitException = (CommitException) e; logger.debug("Request failed with {}. Refresh it (try left: {})", commitException.getCommitStatus(), count); syncNonce(aergoClient, signer.getPrincipal()); } else { throw e; } ThreadUtils.trySleep(sleepInterval); --count; } } if (null == txHash && null != error) { throw error; } return txHash; }  @Override TxHash request(final AergoClient aergoClient, final Signer signer,
      final TxRequestFunction requestFunction);  }
@Test public void testRequest() throws Exception { final TryCountAndInterval tryCountAndInterval = TryCountAndInterval.of(3, Time.of(100L)); final TxRequester txRequester = new NonceRefreshingTxRequester(tryCountAndInterval, new SimpleNonceProvider()); final AergoKey signer = new AergoKeyGenerator().create(); final TxHash expected = TxHash.of(BytesValue.of(randomUUID().toString().getBytes())); final TxHash actual = txRequester .request(mock(AergoClient.class), signer, new TxRequestFunction() { @Override public TxHash apply(Signer signer, Long aLong) { return expected; } }); assertEquals(expected, actual); }
@Test public void shouldRequestSuccessOnNonceRefresh() throws Exception { final AccountOperation mockAccountOperation = mock(AccountOperation.class); when(mockAccountOperation.getState(any(AccountAddress.class))) .thenReturn(AccountState.newBuilder().nonce(1L).build()); final AergoClient mockClient = mock(AergoClient.class); when(mockClient.getAccountOperation()).thenReturn(mockAccountOperation); final int count = 3; final AtomicInteger countDown = new AtomicInteger(count); final TryCountAndInterval tryCountAndInterval = TryCountAndInterval.of(count, Time.of(100L)); final TxRequester txRequester = new NonceRefreshingTxRequester(tryCountAndInterval, new SimpleNonceProvider()); final AergoKey signer = new AergoKeyGenerator().create(); final TxHash expected = TxHash.of(BytesValue.of(randomUUID().toString().getBytes())); final TxRequestFunction requestFunction = new TxRequestFunction() { @Override public TxHash apply(Signer signer, Long aLong) { if (countDown.get() <= 1) { return expected; } countDown.decrementAndGet(); throw new CommitException(types.Rpc.CommitStatus.TX_HAS_SAME_NONCE, ""); } }; final TxHash actual = txRequester.request(mockClient, signer, requestFunction); assertEquals(expected, actual); }
@Test public void shouldRequestFailEvenOnNonceRefresh() throws Exception { final AccountOperation mockAccountOperation = mock(AccountOperation.class); when(mockAccountOperation.getState(any(AccountAddress.class))) .thenReturn(AccountState.newBuilder().nonce(1L).build()); final AergoClient mockClient = mock(AergoClient.class); when(mockClient.getAccountOperation()).thenReturn(mockAccountOperation); final TryCountAndInterval tryCountAndInterval = TryCountAndInterval.of(3, Time.of(100L)); final TxRequester txRequester = new NonceRefreshingTxRequester(tryCountAndInterval, new SimpleNonceProvider()); final AergoKey signer = new AergoKeyGenerator().create(); final CommitException expected = new CommitException(CommitStatus.TX_HAS_SAME_NONCE, "Error"); final TxRequestFunction requestFunction = new TxRequestFunction() { @Override public TxHash apply(Signer signer, Long aLong) { throw expected; } }; try { txRequester.request(mockClient, signer, requestFunction); fail("Should throw CommitException"); } catch (Exception actual) { assertEquals(expected, actual); } }
@Test public void shouldRequestFailOnNotNonceRelatedError() { final TryCountAndInterval tryCountAndInterval = TryCountAndInterval.of(3, Time.of(100L)); final TxRequester txRequester = new NonceRefreshingTxRequester(tryCountAndInterval, new SimpleNonceProvider()); final AergoKey signer = new AergoKeyGenerator().create(); final UnsupportedOperationException expected = new UnsupportedOperationException( "Not nonce-related error"); final TxRequestFunction requestFunction = new TxRequestFunction() { @Override public TxHash apply(Signer signer, Long aLong) { throw expected; } }; try { txRequester.request(mock(AergoClient.class), signer, requestFunction); fail("Should throw UnsupportedOperationException"); } catch (Exception actual) { assertEquals(expected, actual); } }