@Override public void onCoinsReceived(final Wallet wallet, final Transaction tx, final Coin prevBalance, final Coin newBalance) { transactionsReceived.incrementAndGet(); final int bestChainHeight = blockChain.getBestChainHeight(); final Address address = WalletUtils.getWalletAddressOfReceived(tx, wallet); final Coin amount = tx.getValue(wallet); final ConfidenceType confidenceType = tx.getConfidence().getConfidenceType(); final Sha256Hash hash = tx.getHash(); handler.post(new Runnable() { @Override public void run() { final boolean isReceived = amount.signum() > 0; final boolean replaying = bestChainHeight < config.getBestChainHeightEver(); final boolean isReplayedTx = confidenceType == ConfidenceType.BUILDING && replaying; if (isReceived && !isReplayedTx) notifyCoinsReceived(address, amount, hash); } }); }
@Override public byte[] serializeWalletExtension() { Protos.Electrum.Builder extension = Protos.Electrum.newBuilder(); for (Transaction tx : txs.values()) { TransactionConfidence confidence = tx.getConfidence(); Protos.TransactionConfidence.Builder confidenceBuilder = Protos.TransactionConfidence.newBuilder(); confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); confidenceBuilder.setDepth(confidence.getDepthInBlocks()); } Protos.Transaction.Builder transaction = Protos.Transaction.newBuilder() .setConfidence(confidenceBuilder) .setTransaction(ByteString.copyFrom(tx.bitcoinSerialize())); if (tx.getUpdateTime() != null) transaction.setUpdatedAt(tx.getUpdateTime().getTime()); extension.addTransactions(transaction.build()); } return extension.build().toByteArray(); }
@Test public void testPendingToConfirmed() throws Exception { ECKey key = new ECKey(); String address = key.toAddress(params).toString(); Transaction tx = new Transaction(params, Utils.HEX.decode(TEST_TX)); supplyUnconfirmedTransactionForAddress(address, tx); supplyTransactionForAddress(address, tx); control.replay(); multiWallet.retrieveAddressHistory(address); assertEquals(1, multiWallet.getTransactions().size()); TransactionConfidence confidence = multiWallet.getTransactions().iterator().next().getConfidence(); assertEquals(ConfidenceType.PENDING, confidence.getConfidenceType()); multiWallet.retrieveAddressHistory(address); assertEquals(1, multiWallet.getTransactions().size()); Transaction tx1 = multiWallet.getTransactions().iterator().next(); assertEquals(ConfidenceType.BUILDING, tx1.getConfidence().getConfidenceType()); assertEquals(params.getGenesisBlock().getTime(), tx1.getUpdateTime()); control.verify(); }
@Override public void onCoinsReceived(final Wallet wallet, final Transaction tx, final Coin prevBalance, final Coin newBalance) { transactionsReceived.incrementAndGet(); final int bestChainHeight = blockChain.getBestChainHeight(); final Address address = WalletUtils.getWalletAddressOfReceived(tx, wallet); final Coin amount = tx.getValue(wallet); final ConfidenceType confidenceType = tx.getConfidence().getConfidenceType(); handler.post(new Runnable() { @Override public void run() { final boolean isReceived = amount.signum() > 0; final boolean replaying = bestChainHeight < config.getBestChainHeightEver(); final boolean isReplayedTx = confidenceType == ConfidenceType.BUILDING && replaying; if (isReceived && !isReplayedTx) notifyCoinsReceived(address, amount); } }); }
/** * A transaction is mature if it is either a building coinbase tx that is as deep or deeper than the required coinbase depth, or a non-coinbase tx. */ public boolean isMature() { if (!isCoinBase()) return true; if (getConfidence().getConfidenceType() != ConfidenceType.BUILDING) return false; return getConfidence().getDepthInBlocks() >= params.getSpendableCoinbaseDepth(); }
@Test @SuppressWarnings("deprecation") // Having a test for deprecated method getFromAddress() is no evil so we suppress the warning here. public void customTransactionSpending() throws Exception { // We'll set up a wallet that receives a coin, then sends a coin of lesser value and keeps the change. Coin v1 = valueOf(3, 0); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, v1); assertEquals(v1, wallet.getBalance()); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getTransactions(true).size()); Coin v2 = valueOf(0, 50); Coin v3 = valueOf(0, 75); Coin v4 = valueOf(1, 25); Transaction t2 = new Transaction(PARAMS); t2.addOutput(v2, OTHER_ADDRESS); t2.addOutput(v3, OTHER_ADDRESS); t2.addOutput(v4, OTHER_ADDRESS); SendRequest req = SendRequest.forTx(t2); wallet.completeTx(req); // Do some basic sanity checks. assertEquals(1, t2.getInputs().size()); assertEquals(myAddress, t2.getInput(0).getScriptSig().getFromAddress(PARAMS)); assertEquals(TransactionConfidence.ConfidenceType.UNKNOWN, t2.getConfidence().getConfidenceType()); // We have NOT proven that the signature is correct! wallet.commitTx(t2); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT)); assertEquals(2, wallet.getTransactions(true).size()); }
@Test public void testForking3() throws Exception { // Check that we can handle our own spends being rolled back by a fork. Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinsTo); chain.add(b1); assertEquals(FIFTY_COINS, wallet.getBalance()); Address dest = new ECKey().toAddress(PARAMS); Transaction spend = wallet.createSend(dest, valueOf(10, 0)); wallet.commitTx(spend); // Waiting for confirmation ... make it eligible for selection. assertEquals(Coin.ZERO, wallet.getBalance()); spend.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByAddress(new byte[]{1, 2, 3, 4}))); spend.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByAddress(new byte[]{5,6,7,8}))); assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); assertEquals(valueOf(40, 0), wallet.getBalance()); Block b2 = b1.createNextBlock(someOtherGuy); b2.addTransaction(spend); b2.solve(); chain.add(roundtrip(b2)); // We have 40 coins in change. assertEquals(ConfidenceType.BUILDING, spend.getConfidence().getConfidenceType()); // genesis -> b1 (receive coins) -> b2 (spend coins) // \-> b3 -> b4 Block b3 = b1.createNextBlock(someOtherGuy); Block b4 = b3.createNextBlock(someOtherGuy); chain.add(b3); chain.add(b4); // b4 causes a re-org that should make our spend go pending again. assertEquals(valueOf(40, 0), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); }
@Test public void testForking4() throws Exception { // Check that we can handle external spends on an inactive chain becoming active. An external spend is where // we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when // keys are being shared between wallets. Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinsTo); chain.add(b1); assertEquals(FIFTY_COINS, wallet.getBalance()); Address dest = new ECKey().toAddress(PARAMS); Transaction spend = wallet.createSend(dest, FIFTY_COINS); // We do NOT confirm the spend here. That means it's not considered to be pending because createSend is // stateless. For our purposes it is as if some other program with our keys created the tx. // // genesis -> b1 (receive 50) --> b2 // \-> b3 (external spend) -> b4 Block b2 = b1.createNextBlock(someOtherGuy); chain.add(b2); Block b3 = b1.createNextBlock(someOtherGuy); b3.addTransaction(spend); b3.solve(); chain.add(roundtrip(b3)); // The external spend is now pending. assertEquals(ZERO, wallet.getBalance()); Transaction tx = wallet.getTransaction(spend.getHash()); assertEquals(ConfidenceType.PENDING, tx.getConfidence().getConfidenceType()); Block b4 = b3.createNextBlock(someOtherGuy); chain.add(b4); // The external spend is now active. assertEquals(ZERO, wallet.getBalance()); assertEquals(ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType()); }
private static void writeConfidence(Protos.Transaction.Builder txBuilder, TransactionConfidence confidence, Protos.TransactionConfidence.Builder confidenceBuilder) { synchronized (confidence) { confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); confidenceBuilder.setDepth(confidence.getDepthInBlocks()); } // TODO deprecate overriding transactions // if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.DEAD) { // // Copy in the overriding transaction, if available. // // (A dead coinbase transaction has no overriding transaction). // if (confidence.getOverridingTransaction() != null) { // Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash(); // confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash)); // } // } TransactionConfidence.Source source = confidence.getSource(); switch (source) { case SELF: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF); break; case NETWORK: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_NETWORK); break; case UNKNOWN: // Fall through. default: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_UNKNOWN); break; } } for (ListIterator<PeerAddress> it = confidence.getBroadcastBy(); it.hasNext();) { PeerAddress address = it.next(); Protos.PeerAddress proto = Protos.PeerAddress.newBuilder() .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress())) .setPort(address.getPort()) .setServices(address.getServices().longValue()) .build(); confidenceBuilder.addBroadcastBy(proto); } txBuilder.setConfidence(confidenceBuilder); }
private void readConfidence(Transaction tx, Protos.TransactionConfidence confidenceProto, TransactionConfidence confidence) throws UnreadableWalletException { // We are lenient here because tx confidence is not an essential part of the wallet. // If the tx has an unknown type of confidence, ignore. if (!confidenceProto.hasType()) { log.warn("Unknown confidence type for tx {}", tx.getHashAsString()); return; } ConfidenceType confidenceType; switch (confidenceProto.getType()) { case BUILDING: confidenceType = ConfidenceType.BUILDING; break; case DEAD: confidenceType = ConfidenceType.DEAD; break; // These two are equivalent (must be able to read old wallets). case NOT_IN_BEST_CHAIN: confidenceType = ConfidenceType.PENDING; break; case PENDING: confidenceType = ConfidenceType.PENDING; break; case UNKNOWN: // Fall through. default: confidenceType = ConfidenceType.UNKNOWN; break; } confidence.setConfidenceType(confidenceType); if (confidenceProto.hasAppearedAtHeight()) { if (confidence.getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING) { log.warn("Have appearedAtHeight but not BUILDING for tx {}", tx.getHashAsString()); return; } confidence.setAppearedAtChainHeight(confidenceProto.getAppearedAtHeight()); } if (confidenceProto.hasDepth()) { if (confidence.getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING) { log.warn("Have depth but not BUILDING for tx {}", tx.getHashAsString()); return; } confidence.setDepthInBlocks(confidenceProto.getDepth()); } }
void receive(Transaction tx, int height) { // This is also a workaround to Context issue at Fetcher TransactionConfidence confidence = tx.getConfidence(confidenceTable); log.info("got tx {}", tx.getHashAsString()); wallet.lock(); try { boolean isNewCoin = !txs.containsKey(tx.getHash()); if (height > 0) { Block block = store.get(height); if (block == null) { pendingBlock.add(new TransactionWithHeight(tx, height)); } else { log.info("have block for {}", tx.getHash()); pendingDownload.remove(tx.getHash()).set(tx); tx.setUpdateTime(block.getTime()); confidence.setAppearedAtChainHeight(height); txs.put(tx.getHash(), tx); saveLater(); notifyTransaction(tx, isNewCoin); } } else { log.info("unconfirmed {}", tx.getHash()); pendingDownload.remove(tx.getHash()).set(tx); // Check if already processed pending tx. If so, skip all processing. if (isNewCoin) { tx.setUpdateTime(new Date()); confidence.setConfidenceType(ConfidenceType.PENDING); tx.getConfidence().markBroadcastBy(getPeerAddress()); txs.put(tx.getHash(), tx); saveLater(); } notifyTransaction(tx, isNewCoin); } } finally { wallet.unlock(); } markKeysAsUsed(tx); }
/** * <p>Called by the {@link BlockChain} when a new block on the best chain is seen, AFTER relevant wallet * transactions are extracted and sent to us UNLESS the new block caused a re-org, in which case this will * not be called (the {@link Wallet#reorganize(StoredBlock, java.util.List, java.util.List)} method will * call this one in that case).</p> * <p/> * <p>Used to update confidence data in each transaction and last seen block hash. Triggers auto saving. * Invokes the onWalletChanged event listener if there were any affected transactions.</p> */ @Override public void notifyNewBestBlock(StoredBlock block) throws VerificationException { // Check to see if this block has been seen before. Sha256Hash newBlockHash = block.getHeader().getHash(); if (newBlockHash.equals(getLastBlockSeenHash())) return; lock.lock(); try { // Store the new block hash. setLastBlockSeenHash(newBlockHash); setLastBlockSeenHeight(block.getHeight()); setLastBlockSeenTimeSecs(block.getHeader().getTimeSeconds()); // TODO: Clarify the code below. // Notify all the BUILDING transactions of the new block. // This is so that they can update their depth. Set<Transaction> transactions = getTransactions(true); for (Transaction tx : transactions) { if (ignoreNextNewBlock.contains(tx.getHash())) { // tx was already processed in receive() due to it appearing in this block, so we don't want to // increment the tx confidence depth twice, it'd result in miscounting. ignoreNextNewBlock.remove(tx.getHash()); } else if (tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING) { tx.getConfidence().incrementDepthInBlocks(); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } } informConfidenceListenersIfNotReorganizing(); maybeQueueOnWalletChanged(); // Coalesce writes to avoid throttling on disk access when catching up with the chain. saveLater(); } finally { lock.unlock(); } }
/** * Subtract the supplied depth from the given transactions. */ private void subtractDepth(int depthToSubtract, Collection<Transaction> transactions) { for (Transaction tx : transactions) { if (tx.getConfidence().getConfidenceType() == ConfidenceType.BUILDING) { tx.getConfidence().setDepthInBlocks(tx.getConfidence().getDepthInBlocks() - depthToSubtract); confidenceChanged.put(tx, TransactionConfidence.Listener.ChangeReason.DEPTH); } } }
/** * <p>Specifies that the given {@link TransactionBroadcaster}, typically a {@link PeerGroup}, should be used for * sending transactions to the Bitcoin network by default. Some sendCoins methods let you specify a broadcaster * explicitly, in that case, they don't use this broadcaster. If null is specified then the wallet won't attempt * to broadcast transactions itself.</p> * * <p>You don't normally need to call this. A {@link PeerGroup} will automatically set itself as the wallets * broadcaster when you use {@link PeerGroup#addWallet(Wallet)}. A wallet can use the broadcaster when you ask * it to send money, but in future also at other times to implement various features that may require asynchronous * re-organisation of the wallet contents on the block chain. For instance, in future the wallet may choose to * optimise itself to reduce fees or improve privacy.</p> */ public void setTransactionBroadcaster(@Nullable org.bitcoinj.core.TransactionBroadcaster broadcaster) { Transaction[] toBroadcast = {}; lock.lock(); try { if (vTransactionBroadcaster == broadcaster) return; vTransactionBroadcaster = broadcaster; if (broadcaster == null) return; toBroadcast = pending.values().toArray(toBroadcast); } finally { lock.unlock(); } // Now use it to upload any pending transactions we have that are marked as not being seen by any peers yet. // Don't hold the wallet lock whilst doing this, so if the broadcaster accesses the wallet at some point there // is no inversion. for (Transaction tx : toBroadcast) { checkState(tx.getConfidence().getConfidenceType() == ConfidenceType.PENDING); // Re-broadcast even if it's marked as already seen for two reasons // 1) Old wallets may have transactions marked as broadcast by 1 peer when in reality the network // never saw it, due to bugs. // 2) It can't really hurt. log.info("New broadcaster so uploading waiting tx {}", tx.getHash()); broadcaster.broadcastTransaction(tx); } }
private static void writeConfidence(Protos.Transaction.Builder txBuilder, TransactionConfidence confidence, Protos.TransactionConfidence.Builder confidenceBuilder) { synchronized (confidence) { confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); if (confidence.getConfidenceType() == ConfidenceType.BUILDING) { confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); confidenceBuilder.setDepth(confidence.getDepthInBlocks()); } if (confidence.getConfidenceType() == ConfidenceType.DEAD) { // Copy in the overriding transaction, if available. // (A dead coinbase transaction has no overriding transaction). if (confidence.getOverridingTransaction() != null) { Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash(); confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash)); } } TransactionConfidence.Source source = confidence.getSource(); switch (source) { case SELF: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF); break; case NETWORK: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_NETWORK); break; case UNKNOWN: // Fall through. default: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_UNKNOWN); break; } } for (ListIterator<PeerAddress> it = confidence.getBroadcastBy(); it.hasNext();) { PeerAddress address = it.next(); Protos.PeerAddress proto = Protos.PeerAddress.newBuilder() .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress())) .setPort(address.getPort()) .setServices(address.getServices().longValue()) .build(); confidenceBuilder.addBroadcastBy(proto); } txBuilder.setConfidence(confidenceBuilder); }
@Test public void testForking3() throws Exception { // Check that we can handle our own spends being rolled back by a fork. Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); chain.add(b1); assertEquals(FIFTY_COINS, wallet.getBalance()); Address dest = new ECKey().toAddress(unitTestParams); Transaction spend = wallet.createSend(dest, valueOf(10, 0)); wallet.commitTx(spend); // Waiting for confirmation ... make it eligible for selection. assertEquals(Coin.ZERO, wallet.getBalance()); spend.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByAddress(new byte[]{1, 2, 3, 4}))); spend.getConfidence().markBroadcastBy(new PeerAddress(InetAddress.getByAddress(new byte[]{5,6,7,8}))); assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); assertEquals(valueOf(40, 0), wallet.getBalance()); Block b2 = b1.createNextBlock(someOtherGuy); b2.addTransaction(spend); b2.solve(); chain.add(roundtrip(b2)); // We have 40 coins in change. assertEquals(ConfidenceType.BUILDING, spend.getConfidence().getConfidenceType()); // genesis -> b1 (receive coins) -> b2 (spend coins) // \-> b3 -> b4 Block b3 = b1.createNextBlock(someOtherGuy); Block b4 = b3.createNextBlock(someOtherGuy); chain.add(b3); chain.add(b4); // b4 causes a re-org that should make our spend go pending again. assertEquals(valueOf(40, 0), wallet.getBalance()); assertEquals(ConfidenceType.PENDING, spend.getConfidence().getConfidenceType()); }
@Test public void testForking4() throws Exception { // Check that we can handle external spends on an inactive chain becoming active. An external spend is where // we see a transaction that spends our own coins but we did not broadcast it ourselves. This happens when // keys are being shared between wallets. Block b1 = unitTestParams.getGenesisBlock().createNextBlock(coinsTo); chain.add(b1); assertEquals(FIFTY_COINS, wallet.getBalance()); Address dest = new ECKey().toAddress(unitTestParams); Transaction spend = wallet.createSend(dest, FIFTY_COINS); // We do NOT confirm the spend here. That means it's not considered to be pending because createSend is // stateless. For our purposes it is as if some other program with our keys created the tx. // // genesis -> b1 (receive 50) --> b2 // \-> b3 (external spend) -> b4 Block b2 = b1.createNextBlock(someOtherGuy); chain.add(b2); Block b3 = b1.createNextBlock(someOtherGuy); b3.addTransaction(spend); b3.solve(); chain.add(roundtrip(b3)); // The external spend is now pending. assertEquals(ZERO, wallet.getBalance()); Transaction tx = wallet.getTransaction(spend.getHash()); assertEquals(ConfidenceType.PENDING, tx.getConfidence().getConfidenceType()); Block b4 = b3.createNextBlock(someOtherGuy); chain.add(b4); // The external spend is now active. assertEquals(ZERO, wallet.getBalance()); assertEquals(ConfidenceType.BUILDING, tx.getConfidence().getConfidenceType()); }
private static void writeConfidence(Protos.Transaction.Builder txBuilder, TransactionConfidence confidence, Protos.TransactionConfidence.Builder confidenceBuilder) { synchronized (confidence) { confidenceBuilder.setType(Protos.TransactionConfidence.Type.valueOf(confidence.getConfidenceType().getValue())); if (confidence.getConfidenceType() == ConfidenceType.BUILDING) { confidenceBuilder.setAppearedAtHeight(confidence.getAppearedAtChainHeight()); confidenceBuilder.setDepth(confidence.getDepthInBlocks()); } if (confidence.getConfidenceType() == ConfidenceType.DEAD) { // Copy in the overriding transaction, if available. // (A dead coinbase transaction has no overriding transaction). if (confidence.getOverridingTransaction() != null) { Sha256Hash overridingHash = confidence.getOverridingTransaction().getHash(); confidenceBuilder.setOverridingTransaction(hashToByteString(overridingHash)); } } TransactionConfidence.Source source = confidence.getSource(); switch (source) { case SELF: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_SELF); break; case NETWORK: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_NETWORK); break; case UNKNOWN: // Fall through. default: confidenceBuilder.setSource(Protos.TransactionConfidence.Source.SOURCE_UNKNOWN); break; } } for (PeerAddress address : confidence.getBroadcastBy()) { Protos.PeerAddress proto = Protos.PeerAddress.newBuilder() .setIpAddress(ByteString.copyFrom(address.getAddr().getAddress())) .setPort(address.getPort()) .setServices(address.getServices().longValue()) .build(); confidenceBuilder.addBroadcastBy(proto); } txBuilder.setConfidence(confidenceBuilder); }
public void sendNotificationToAuthenticatorWhenCoinsReceived() { if(eventsAdapterForCoinsReceivedNotificaiton == null) { eventsAdapterForCoinsReceivedNotificaiton = new BAGeneralEventsAdapter() { @Override public void onBalanceChanged(Transaction tx, HowBalanceChanged howBalanceChanged, ConfidenceType confidence) { if(CoinsReceivedNotificationSender.checkIfNotificationShouldBeSentToPairedDeviceOnReceivedCoins(WalletOperation.this, tx)) { // send CoinsReceivedNotificationSender.send(WalletOperation.this, tx, howBalanceChanged); } } }; Authenticator.addGeneralEventsListener(eventsAdapterForCoinsReceivedNotificaiton); } }