diff --git a/core/src/main/java/org/bitcoinj/core/BloomFilter.java b/core/src/main/java/org/bitcoinj/core/BloomFilter.java index 92c796b0..b6305d81 100644 --- a/core/src/main/java/org/bitcoinj/core/BloomFilter.java +++ b/core/src/main/java/org/bitcoinj/core/BloomFilter.java @@ -93,7 +93,7 @@ public class BloomFilter extends Message { * *

In order for filtered block download to function efficiently, the number of matched transactions in any given * block should be less than (with some headroom) the maximum size of the MemoryPool used by the Peer - * doing the downloading (default is {@link TxConfidencePool#MAX_SIZE}). See the comment in processBlock(FilteredBlock) + * doing the downloading (default is {@link TxConfidenceTable#MAX_SIZE}). See the comment in processBlock(FilteredBlock) * for more information on this restriction.

* *

randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks. diff --git a/core/src/main/java/org/bitcoinj/core/Context.java b/core/src/main/java/org/bitcoinj/core/Context.java index 687cb0cb..c5401e39 100644 --- a/core/src/main/java/org/bitcoinj/core/Context.java +++ b/core/src/main/java/org/bitcoinj/core/Context.java @@ -6,19 +6,19 @@ package org.bitcoinj.core; * through {@link AbstractBlockChain#getContext()}. */ public class Context { - protected TxConfidencePool confidencePool; + protected TxConfidenceTable confidenceTable; protected Context() { - confidencePool = new TxConfidencePool(); + confidenceTable = new TxConfidenceTable(); } /** - * Returns the {@link TxConfidencePool} created by this context. The pool tracks advertised + * Returns the {@link TxConfidenceTable} created by this context. The pool tracks advertised * and downloaded transactions so their confidence can be measured as a proportion of how many peers announced it. * With an un-tampered with internet connection, the more peers announce a transaction the more confidence you can * have that it's really valid. */ - public TxConfidencePool getConfidencePool() { - return confidencePool; + public TxConfidenceTable getConfidenceTable() { + return confidenceTable; } } diff --git a/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java b/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java index a56d3ecf..c52a172f 100644 --- a/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java +++ b/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java @@ -22,7 +22,7 @@ import java.io.OutputStream; /** * The "mempool" message asks a remote peer to announce all transactions in its memory pool, possibly restricted by * any Bloom filter set on the connection. The list of transaction hashes comes back in an inv message. Note that - * this is different to the {@link TxConfidencePool} object which doesn't try to keep track of all pending transactions, + * this is different to the {@link TxConfidenceTable} object which doesn't try to keep track of all pending transactions, * it's just a holding area for transactions that a part of the app may find interesting. The mempool message has * no fields. */ diff --git a/core/src/main/java/org/bitcoinj/core/Peer.java b/core/src/main/java/org/bitcoinj/core/Peer.java index 04dab7af..6a92d0ae 100644 --- a/core/src/main/java/org/bitcoinj/core/Peer.java +++ b/core/src/main/java/org/bitcoinj/core/Peer.java @@ -89,7 +89,7 @@ public class Peer extends PeerSocketHandler { private final AtomicInteger blocksAnnounced = new AtomicInteger(); // A class that tracks recent transactions that have been broadcast across the network, counts how many // peers announced them and updates the transaction confidence data. It is passed to each Peer. - private final TxConfidencePool confidencePool; + private final TxConfidenceTable confidenceTable; // Each wallet added to the peer will be notified of downloaded transaction data. private final CopyOnWriteArrayList wallets; // A time before which we only download block headers, after that point we download block bodies. @@ -165,8 +165,8 @@ public class Peer extends PeerSocketHandler { } /** - *

Construct a peer that reads/writes from the given block chain and memory pool. Transactions stored in a memory - * pool will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that + *

Construct a peer that reads/writes from the given block chain. Transactions stored in a {@link org.bitcoinj.core.TxConfidenceTable} + * will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that * the transaction is valid.

* *

Note that this does NOT make a connection to the given remoteAddress, it only creates a handler for a @@ -184,8 +184,8 @@ public class Peer extends PeerSocketHandler { } /** - *

Construct a peer that reads/writes from the given block chain and memory pool. Transactions stored in a memory - * pool will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that + *

Construct a peer that reads/writes from the given block chain. Transactions stored in a {@link org.bitcoinj.core.TxConfidenceTable} + * will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that * the transaction is valid.

* *

Note that this does NOT make a connection to the given remoteAddress, it only creates a handler for a @@ -211,7 +211,7 @@ public class Peer extends PeerSocketHandler { this.isAcked = false; this.pendingPings = new CopyOnWriteArrayList(); this.wallets = new CopyOnWriteArrayList(); - this.confidencePool = chain.getContext().getConfidencePool(); + this.confidenceTable = chain.getContext().getConfidenceTable(); } /** @@ -583,9 +583,9 @@ public class Peer extends PeerSocketHandler { lock.lock(); try { log.debug("{}: Received tx {}", getAddress(), tx.getHashAsString()); - if (confidencePool != null) { + if (confidenceTable != null) { // We may get back a different transaction object. - tx = confidencePool.seen(tx, getAddress()); + tx = confidenceTable.seen(tx, getAddress()); } fTx = tx; // Label the transaction as coming in from the P2P network (as opposed to being created by us, direct import, @@ -686,7 +686,7 @@ public class Peer extends PeerSocketHandler { *

Note that dependencies downloaded this way will not trigger the onTransaction method of event listeners.

*/ public ListenableFuture> downloadDependencies(Transaction tx) { - checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies."); + checkNotNull(confidenceTable, "Must have a configured TxConfidenceTable object to download dependencies."); TransactionConfidence.ConfidenceType txConfidence = tx.getConfidence().getConfidenceType(); Preconditions.checkArgument(txConfidence != TransactionConfidence.ConfidenceType.BUILDING); log.info("{}: Downloading dependencies of {}", getAddress(), tx.getHashAsString()); @@ -712,7 +712,7 @@ public class Peer extends PeerSocketHandler { private ListenableFuture downloadDependenciesInternal(final Transaction tx, final Object marker, final List results) { - checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies."); + checkNotNull(confidenceTable, "Must have a configured TxConfidenceTable object to download dependencies."); final SettableFuture resultFuture = SettableFuture.create(); final Sha256Hash rootTxHash = tx.getHash(); // We want to recursively grab its dependencies. This is so listeners can learn important information like @@ -727,7 +727,7 @@ public class Peer extends PeerSocketHandler { for (TransactionInput input : tx.getInputs()) { // There may be multiple inputs that connect to the same transaction. Sha256Hash hash = input.getOutpoint().getHash(); - Transaction dep = confidencePool.get(hash); + Transaction dep = confidenceTable.get(hash); if (dep == null) { needToRequest.add(hash); } else { @@ -1043,7 +1043,7 @@ public class Peer extends PeerSocketHandler { Iterator it = transactions.iterator(); while (it.hasNext()) { InventoryItem item = it.next(); - if (confidencePool == null) { + if (confidenceTable == null) { if (downloadData) { // If there's no memory pool only download transactions if we're configured to. getdata.addItem(item); @@ -1055,7 +1055,7 @@ public class Peer extends PeerSocketHandler { // peers run at different speeds. However to conserve bandwidth on mobile devices we try to only download a // transaction once. This means we can miss broadcasts if the peer disconnects between sending us an inv and // sending us the transaction: currently we'll never try to re-fetch after a timeout. - if (confidencePool.maybeWasSeen(item.hash)) { + if (confidenceTable.maybeWasSeen(item.hash)) { // Some other peer already announced this so don't download. it.remove(); } else { @@ -1063,7 +1063,7 @@ public class Peer extends PeerSocketHandler { getdata.addItem(item); } // This can trigger transaction confidence listeners. - confidencePool.seen(item.hash, this.getAddress()); + confidenceTable.seen(item.hash, this.getAddress()); } } @@ -1528,7 +1528,7 @@ public class Peer extends PeerSocketHandler { * unset a filter, though the underlying p2p protocol does support it.

*/ public void setBloomFilter(BloomFilter filter) { - setBloomFilter(filter, confidencePool != null || vDownloadData); + setBloomFilter(filter, confidenceTable != null || vDownloadData); } /** diff --git a/core/src/main/java/org/bitcoinj/core/PeerGroup.java b/core/src/main/java/org/bitcoinj/core/PeerGroup.java index 79875d46..41c928e4 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerGroup.java +++ b/core/src/main/java/org/bitcoinj/core/PeerGroup.java @@ -121,7 +121,7 @@ public class PeerGroup implements TransactionBroadcaster { @GuardedBy("lock") private boolean downloadTxDependencies; // A class that tracks recent transactions that have been broadcast across the network, counts how many // peers announced them and updates the transaction confidence data. It is passed to each Peer. - private final TxConfidencePool confidencePool; + private final TxConfidenceTable confidenceTable; // How many connections we want to have open at the current time. If we lose connections, we'll try opening more // until we reach this count. @GuardedBy("lock") private int maxConnections; @@ -335,7 +335,7 @@ public class PeerGroup implements TransactionBroadcaster { downloadTxDependencies = true; - confidencePool = chain.getContext().getConfidencePool(); + confidenceTable = chain.getContext().getConfidenceTable(); inactives = new PriorityQueue(1, new Comparator() { @SuppressWarnings("FieldAccessNotGuarded") // only called when inactives is accessed, and lock is held then. @@ -516,7 +516,7 @@ public class PeerGroup implements TransactionBroadcaster { while (it.hasNext()) { InventoryItem item = it.next(); // Check the confidence pool first. - Transaction tx = confidencePool.get(item.hash); + Transaction tx = confidenceTable.get(item.hash); if (tx != null) { transactions.add(tx); it.remove(); @@ -1321,11 +1321,11 @@ public class PeerGroup implements TransactionBroadcaster { } /** - * Use {@link org.bitcoinj.core.Context#getConfidencePool()} instead. + * Use {@link org.bitcoinj.core.Context#getConfidenceTable()} instead. */ @Deprecated - public TxConfidencePool getConfidencePool() { - return confidencePool; + public TxConfidenceTable getConfidenceTable() { + return confidenceTable; } /** diff --git a/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java b/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java index ce33230a..30962956 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java @@ -16,9 +16,6 @@ package org.bitcoinj.core; -import org.bitcoinj.core.AbstractPeerEventListener; -import org.bitcoinj.core.RejectMessage; -import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.utils.Threading; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; @@ -101,7 +98,7 @@ public class TransactionBroadcast { // a big effect. List peers = peerGroup.getConnectedPeers(); // snapshots // We intern the tx here so we are using a canonical version of the object (as it's unfortunately mutable). - pinnedTx = peerGroup.getConfidencePool().intern(tx); + pinnedTx = peerGroup.getConfidenceTable().intern(tx); // Prepare to send the transaction by adding a listener that'll be called when confidence changes. // Only bother with this if we might actually hear back: if (minConnections > 1) diff --git a/core/src/main/java/org/bitcoinj/core/TxConfidencePool.java b/core/src/main/java/org/bitcoinj/core/TxConfidenceTable.java similarity index 89% rename from core/src/main/java/org/bitcoinj/core/TxConfidencePool.java rename to core/src/main/java/org/bitcoinj/core/TxConfidenceTable.java index 2a814f7c..56ff5528 100644 --- a/core/src/main/java/org/bitcoinj/core/TxConfidencePool.java +++ b/core/src/main/java/org/bitcoinj/core/TxConfidenceTable.java @@ -44,9 +44,9 @@ import static com.google.common.base.Preconditions.checkState; *

It is not at this time directly equivalent to the Satoshi clients memory pool, which tracks * all transactions not currently included in the best chain - it's simply a cache.

*/ -public class TxConfidencePool { - private static final Logger log = LoggerFactory.getLogger(TxConfidencePool.class); - protected ReentrantLock lock = Threading.lock("txconfidencepool"); +public class TxConfidenceTable { + private static final Logger log = LoggerFactory.getLogger(TxConfidenceTable.class); + protected ReentrantLock lock = Threading.lock("txconfidencetable"); // For each transaction we may have seen: // - only its hash in an inv packet @@ -72,27 +72,27 @@ public class TxConfidencePool { // allowing us to delete the associated entry (the tx itself has already gone away). WeakTransactionReference tx; } - private LinkedHashMap pool; + private LinkedHashMap table; - // This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the MemoryPool is the - // only thing that is tracking the transaction anymore. We check it from time to time and delete pool entries + // This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the TxConfidenceTable is the + // only thing that is tracking the transaction anymore. We check it from time to time and delete table entries // corresponding to expired transactions. In this way memory usage of the system is in line with however many // transactions you actually care to track the confidence of. We can still end up with lots of hashes being stored // if our peers flood us with invs but the MAX_SIZE param caps this. private ReferenceQueue referenceQueue; - /** The max size of a memory pool created with the no-args constructor. */ + /** The max size of a table created with the no-args constructor. */ public static final int MAX_SIZE = 1000; /** - * Creates a memory pool that will track at most the given number of transactions (allowing you to bound memory + * Creates a table that will track at most the given number of transactions (allowing you to bound memory * usage). - * @param size Max number of transactions to track. The pool will fill up to this size then stop growing. + * @param size Max number of transactions to track. The table will fill up to this size then stop growing. */ - public TxConfidencePool(final int size) { - pool = new LinkedHashMap() { + public TxConfidenceTable(final int size) { + table = new LinkedHashMap() { @Override - protected boolean removeEldestEntry(Map.Entry entry) { + protected boolean removeEldestEntry(Map.Entry entry) { // An arbitrary choice to stop the memory used by tracked transactions getting too huge in the event // of some kind of DoS attack. return size() > size; @@ -102,20 +102,20 @@ public class TxConfidencePool { } /** - * Creates a memory pool that will track at most {@link TxConfidencePool#MAX_SIZE} entries. You should normally use + * Creates a table that will track at most {@link TxConfidenceTable#MAX_SIZE} entries. You should normally use * this constructor. */ - public TxConfidencePool() { + public TxConfidenceTable() { this(MAX_SIZE); } /** * If any transactions have expired due to being only weakly reachable through us, go ahead and delete their - * pool entries - it means we downloaded the transaction and sent it to various event listeners, none of + * table entries - it means we downloaded the transaction and sent it to various event listeners, none of * which bothered to keep a reference. Typically, this is because the transaction does not involve any keys that * are relevant to any of our wallets. */ - private void cleanPool() { + private void cleanTable() { lock.lock(); try { Reference ref; @@ -123,7 +123,7 @@ public class TxConfidencePool { // Find which transaction got deleted by the GC. WeakTransactionReference txRef = (WeakTransactionReference) ref; // And remove the associated map entry so the other bits of memory can also be reclaimed. - pool.remove(txRef.hash); + table.remove(txRef.hash); } } finally { lock.unlock(); @@ -136,8 +136,8 @@ public class TxConfidencePool { public int numBroadcastPeers(Sha256Hash txHash) { lock.lock(); try { - cleanPool(); - Entry entry = pool.get(txHash); + cleanTable(); + Entry entry = table.get(txHash); if (entry == null) { // No such TX known. return 0; @@ -151,7 +151,7 @@ public class TxConfidencePool { // We previously downloaded this transaction, but nothing cared about it so the garbage collector threw // it away. We also deleted the set that tracked which peers had seen it. Treat this case as a zero and // just delete it from the map. - pool.remove(txHash); + table.remove(txHash); return 0; } else { checkState(entry.addresses == null); @@ -171,8 +171,8 @@ public class TxConfidencePool { public Transaction intern(Transaction tx) { lock.lock(); try { - cleanPool(); - Entry entry = pool.get(tx.getHash()); + cleanTable(); + Entry entry = table.get(tx.getHash()); if (entry != null) { // This TX or its hash have been previously interned. if (entry.tx != null) { @@ -193,7 +193,7 @@ public class TxConfidencePool { Set addrs = entry.addresses; entry.addresses = null; TransactionConfidence confidence = tx.getConfidence(); - log.debug("Adding tx [{}] {} to the memory pool", + log.debug("Adding tx [{}] {} to the confidence table", confidence.numBroadcastPeers(), tx.getHashAsString()); for (PeerAddress a : addrs) { markBroadcast(a, tx); @@ -206,7 +206,7 @@ public class TxConfidencePool { log.debug("Provided with a downloaded transaction we didn't see announced yet: {}", tx.getHashAsString()); entry = new Entry(); entry.tx = new WeakTransactionReference(tx, referenceQueue); - pool.put(tx.getHash(), entry); + table.put(tx.getHash(), entry); return tx; } } finally { @@ -238,8 +238,8 @@ public class TxConfidencePool { public void seen(Sha256Hash hash, PeerAddress byPeer) { lock.lock(); try { - cleanPool(); - Entry entry = pool.get(hash); + cleanTable(); + Entry entry = table.get(hash); if (entry != null) { // This TX or its hash have been previously announced. if (entry.tx != null) { @@ -265,7 +265,7 @@ public class TxConfidencePool { // TODO: Using hashsets here is inefficient compared to just having an array. entry.addresses = new HashSet(); entry.addresses.add(byPeer); - pool.put(hash, entry); + table.put(hash, entry); log.info("{}: Peer announced new transaction [1] {}", byPeer, hash); } } finally { @@ -289,7 +289,7 @@ public class TxConfidencePool { public Transaction get(Sha256Hash hash) { lock.lock(); try { - Entry entry = pool.get(hash); + Entry entry = table.get(hash); if (entry == null) return null; // Unknown. if (entry.tx == null) return null; // Seen but only in advertisements. if (entry.tx.get() == null) return null; // Was downloaded but garbage collected. @@ -309,7 +309,7 @@ public class TxConfidencePool { public boolean maybeWasSeen(Sha256Hash hash) { lock.lock(); try { - Entry entry = pool.get(hash); + Entry entry = table.get(hash); return entry != null; } finally { lock.unlock(); diff --git a/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java b/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java index 29c7fead..794c2b16 100644 --- a/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java +++ b/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java @@ -46,34 +46,34 @@ public class MemoryPoolTest { @Test public void canonicalInstance() throws Exception { - TxConfidencePool pool = new TxConfidencePool(); + TxConfidenceTable table = new TxConfidenceTable(); // Check that if we repeatedly send it the same transaction but with different objects, we get back the same // canonical instance with the confidences update. - assertEquals(0, pool.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, pool.seen(tx1, address1)); + assertEquals(0, table.numBroadcastPeers(tx1.getHash())); + assertEquals(tx1, table.seen(tx1, address1)); assertEquals(1, tx1.getConfidence().numBroadcastPeers()); - assertEquals(1, pool.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, pool.seen(tx2, address2)); + assertEquals(1, table.numBroadcastPeers(tx1.getHash())); + assertEquals(tx1, table.seen(tx2, address2)); assertEquals(2, tx1.getConfidence().numBroadcastPeers()); - assertEquals(2, pool.numBroadcastPeers(tx1.getHash())); - assertEquals(tx1, pool.get(tx1.getHash())); + assertEquals(2, table.numBroadcastPeers(tx1.getHash())); + assertEquals(tx1, table.get(tx1.getHash())); } @Test public void invAndDownload() throws Exception { - TxConfidencePool pool = new TxConfidencePool(); + TxConfidenceTable table = new TxConfidenceTable(); // Base case: we see a transaction announced twice and then download it. The count is in the confidence object. - assertEquals(0, pool.numBroadcastPeers(tx1.getHash())); - pool.seen(tx1.getHash(), address1); - assertEquals(1, pool.numBroadcastPeers(tx1.getHash())); - assertTrue(pool.maybeWasSeen(tx1.getHash())); - pool.seen(tx1.getHash(), address2); - assertEquals(2, pool.numBroadcastPeers(tx1.getHash())); - Transaction t = pool.seen(tx1, address1); + assertEquals(0, table.numBroadcastPeers(tx1.getHash())); + table.seen(tx1.getHash(), address1); + assertEquals(1, table.numBroadcastPeers(tx1.getHash())); + assertTrue(table.maybeWasSeen(tx1.getHash())); + table.seen(tx1.getHash(), address2); + assertEquals(2, table.numBroadcastPeers(tx1.getHash())); + Transaction t = table.seen(tx1, address1); assertEquals(2, t.getConfidence().numBroadcastPeers()); // And now we see another inv. - pool.seen(tx1.getHash(), address3); + table.seen(tx1.getHash(), address3); assertEquals(3, t.getConfidence().numBroadcastPeers()); - assertEquals(3, pool.numBroadcastPeers(tx1.getHash())); + assertEquals(3, table.numBroadcastPeers(tx1.getHash())); } } diff --git a/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java b/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java index 537f25ea..890818c2 100644 --- a/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java +++ b/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java @@ -376,7 +376,7 @@ public class PeerGroupTest extends TestWithPeerGroup { inbound(p2, inv); assertTrue(outbound(p2) instanceof GetDataMessage); assertEquals(0, tx.getConfidence().numBroadcastPeers()); - assertTrue(peerGroup.getConfidencePool().maybeWasSeen(tx.getHash())); + assertTrue(peerGroup.getConfidenceTable().maybeWasSeen(tx.getHash())); assertNull(event[0]); // Peer 1 advertises the tx, we don't do anything as it's already been requested. inbound(p1, inv); diff --git a/core/src/test/java/org/bitcoinj/core/PeerTest.java b/core/src/test/java/org/bitcoinj/core/PeerTest.java index 9980c1ec..55fece7c 100644 --- a/core/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/core/src/test/java/org/bitcoinj/core/PeerTest.java @@ -56,7 +56,7 @@ public class PeerTest extends TestWithNetworkConnections { private Peer peer; private InboundMessageQueuer writeTarget; private static final int OTHER_PEER_CHAIN_HEIGHT = 110; - private TxConfidencePool confidencePool; + private TxConfidenceTable confidenceTable; private final AtomicBoolean fail = new AtomicBoolean(false); @@ -77,7 +77,7 @@ public class PeerTest extends TestWithNetworkConnections { public void setUp() throws Exception { super.setUp(); - confidencePool = blockChain.getContext().getConfidencePool(); + confidenceTable = blockChain.getContext().getConfidenceTable(); VersionMessage ver = new VersionMessage(unitTestParams, 100); InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4000); peer = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain); @@ -291,7 +291,7 @@ public class PeerTest extends TestWithNetworkConnections { GetDataMessage message = (GetDataMessage)outbound(writeTarget); assertEquals(1, message.getItems().size()); assertEquals(tx.getHash(), message.getItems().get(0).hash); - assertTrue(confidencePool.maybeWasSeen(tx.getHash())); + assertTrue(confidenceTable.maybeWasSeen(tx.getHash())); // Advertising to peer2 results in no getdata message. inbound(writeTarget2, inv);