Here are the examples of the java api org.bitcoinj.store.BlockStore taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
59 Examples
19
Source : VersionTallyTest.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
@Test
public void testInitialize() throws BlockStoreException {
final BlockStore blockStore = new MemoryBlockStore(PARAMS);
final BlockChain chain = new BlockChain(PARAMS, blockStore);
// Build a historical chain of version 2 blocks
long timeSeconds = 1231006505;
StoredBlock chainHead = null;
for (int height = 0; height < PARAMS.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds, height).storedBlock;
replacedertEquals(2, chainHead.getHeader().getVersion());
timeSeconds += 60;
}
VersionTally instance = new VersionTally(PARAMS);
instance.initialize(blockStore, chainHead);
replacedertEquals(PARAMS.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue());
}
19
Source : TestWithWallet.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
// TODO: This needs to be somewhat rewritten - the "sendMoneyToWallet" methods aren't sending via the block chain object
/**
* A utility clreplaced that you can derive from in your unit tests. TestWithWallet sets up an empty wallet,
* an in-memory block store and a block chain object. It also provides helper methods for filling the wallet
* with money in whatever ways you wish. Note that for simplicity with amounts, this clreplaced sets the default
* fee per kilobyte to zero in setUp.
*/
public clreplaced TestWithWallet {
protected static final NetworkParameters PARAMS = UnitTestParams.get();
protected ECKey myKey;
protected Address myAddress;
protected Wallet wallet;
protected BlockChain chain;
protected BlockStore blockStore;
public void setUp() throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
wallet = new Wallet(PARAMS);
myKey = wallet.currentReceiveKey();
myAddress = myKey.toAddress(PARAMS);
blockStore = new MemoryBlockStore(PARAMS);
chain = new BlockChain(PARAMS, wallet, blockStore);
}
public void tearDown() throws Exception {
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
if (type == null) {
// Pending transaction
for (Transaction tx : transactions) if (wallet.isPendingTransactionRelevant(tx))
wallet.receivePending(tx, null);
} else {
FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, transactions);
for (Transaction tx : transactions) wallet.receiveFromBlock(tx, bp.storedBlock, type, 0);
if (type == AbstractBlockChain.NewBlockType.BEST_CHAIN)
wallet.notifyNewBestBlock(bp.storedBlock);
}
if (transactions.length == 1)
// Can be null if tx is a double spend that's otherwise irrelevant.
return wallet.getTransaction(transactions[0].getHash());
else
return null;
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
return sendMoneyToWallet(wallet, type, createFakeTx(PARAMS, value, toAddress));
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
return sendMoneyToWallet(wallet, type, createFakeTx(PARAMS, value, toPubKey));
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, transactions);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, myAddress);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, toAddress);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, toPubKey);
}
}
19
Source : TestWithNetworkConnections.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Utility clreplaced that makes it easy to work with mock NetworkConnections.
*/
public clreplaced TestWithNetworkConnections {
public static final int PEER_SERVERS = 5;
protected static final NetworkParameters PARAMS = UnitTestParams.get();
protected Context context;
protected BlockStore blockStore;
protected BlockChain blockChain;
protected Wallet wallet;
protected ECKey key;
protected Address address;
protected SocketAddress socketAddress;
private NioServer[] peerServers = new NioServer[PEER_SERVERS];
private final ClientConnectionManager channels;
protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<>();
public enum ClientType {
NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
}
private final ClientType clientType;
public TestWithNetworkConnections(ClientType clientType) {
this.clientType = clientType;
if (clientType == ClientType.NIO_CLIENT_MANAGER)
channels = new NioClientManager();
else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels = new BlockingClientManager();
else
channels = null;
}
public void setUp() throws Exception {
setUp(new MemoryBlockStore(UnitTestParams.get()));
}
public void setUp(BlockStore blockStore) throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
this.blockStore = blockStore;
// Allow subclreplacedes to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(PARAMS);
key = wallet.freshReceiveKey();
address = key.toAddress(PARAMS);
}
blockChain = new BlockChain(PARAMS, wallet, blockStore);
startPeerServers();
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
channels.startAsync();
channels.awaitRunning();
}
socketAddress = new InetSocketAddress("127.0.0.1", 1111);
}
protected void startPeerServers() throws IOException {
for (int i = 0; i < PEER_SERVERS; i++) {
startPeerServer(i);
}
}
protected void startPeerServer(int i) throws IOException {
peerServers[i] = new NioServer(new StreamConnectionFactory() {
@Nullable
@Override
public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
return new InboundMessageQueuer(PARAMS) {
@Override
public void connectionClosed() {
}
@Override
public void connectionOpened() {
newPeerWriteTargetQueue.offer(this);
}
};
}
}, new InetSocketAddress("127.0.0.1", 2000 + i));
peerServers[i].startAsync();
peerServers[i].awaitRunning();
}
public void tearDown() throws Exception {
stopPeerServers();
}
protected void stopPeerServers() {
for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
}
protected void stopPeerServer(int i) {
peerServers[i].stopAsync();
peerServers[i].awaitTerminated();
}
protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
checkArgument(versionMessage.hasBlockChain());
final AtomicBoolean doneConnecting = new AtomicBoolean(false);
final Thread thisThread = Thread.currentThread();
peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {
@Override
public void onPeerDisconnected(Peer p, int peerCount) {
synchronized (doneConnecting) {
if (!doneConnecting.get())
thisThread.interrupt();
}
}
});
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels.openConnection(new InetSocketAddress("127.0.0.1", 2000), peer);
else if (clientType == ClientType.NIO_CLIENT)
new NioClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100);
else if (clientType == ClientType.BLOCKING_CLIENT)
new BlockingClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100, SocketFactory.getDefault(), null);
else
throw new RuntimeException();
// Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
writeTarget.peer = peer;
// Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
checkState(!peer.getVersionHandshakeFuture().isDone());
writeTarget.sendMessage(versionMessage);
writeTarget.sendMessage(new VersionAck());
try {
checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
peer.getVersionHandshakeFuture().get();
synchronized (doneConnecting) {
doneConnecting.set(true);
}
// Clear interrupted bit in case it was set before we got into the CS
Thread.interrupted();
} catch (InterruptedException e) {
// We were disconnected before we got back version/verack
}
return writeTarget;
}
protected void closePeer(Peer peer) throws Exception {
peer.close();
}
protected void inbound(InboundMessageQueuer peerChannel, Message message) {
peerChannel.sendMessage(message);
}
private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
// Send a ping and wait for it to get to the other side
SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
p.mapPingFutures.put(nonce, pingReceivedFuture);
p.peer.sendMessage(new Ping(nonce));
pingReceivedFuture.get();
p.mapPingFutures.remove(nonce);
}
private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
// Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {
@Override
public Message onPreMessageReceived(Peer p, Message m) {
if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
pongReceivedFuture.set(null);
return null;
}
return m;
}
};
p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
inbound(p, new Pong(nonce));
pongReceivedFuture.get();
p.peer.removePreMessageReceivedEventListener(listener);
}
protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
final long nonce = (long) (Math.random() * Long.MAX_VALUE);
// Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
// to wait on an outbound message, so we do it in the same order or we see race conditions
inboundPongAndWait(p, nonce);
outboundPingAndWait(p, nonce);
}
protected Message outbound(InboundMessageQueuer p1) throws Exception {
pingAndWait(p1);
return p1.nextMessage();
}
protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
return ch.nextMessageBlocking();
}
protected Peer peerOf(InboundMessageQueuer ch) {
return ch.peer;
}
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Emulates receiving a valid block that builds on top of the chain.
*/
public static BlockPair createFakeBlock(BlockStore blockStore, long version, long timeSeconds, int height, Transaction... transactions) {
try {
return createFakeBlock(blockStore, blockStore.getChainHead(), version, timeSeconds, height, transactions);
} catch (BlockStoreException e) {
// Cannot happen.
throw new RuntimeException(e);
}
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
public static BlockPair createFakeBlock(BlockStore blockStore, StoredBlock previousStoredBlock, int height, Transaction... transactions) {
return createFakeBlock(blockStore, previousStoredBlock, Block.BLOCK_VERSION_BIP66, Utils.currentTimeSeconds(), height, transactions);
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Emulates receiving a valid block that builds on top of the chain.
*/
public static BlockPair createFakeBlock(BlockStore blockStore, int height, Transaction... transactions) {
return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), height, transactions);
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
public static Block makeSolvedTestBlock(BlockStore blockStore, Address coinsTo) throws BlockStoreException {
Block b = blockStore.getChainHead().getHeader().createNextBlock(coinsTo);
b.solve();
return b;
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Emulates receiving a valid block that builds on top of the chain.
*/
public static BlockPair createFakeBlock(BlockStore blockStore, long version, long timeSeconds, Transaction... transactions) {
return createFakeBlock(blockStore, version, timeSeconds, 0, transactions);
}
19
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Emulates receiving a valid block that builds on top of the chain.
*/
public static BlockPair createFakeBlock(BlockStore blockStore, Transaction... transactions) {
return createFakeBlock(blockStore, Block.BLOCK_VERSION_GENESIS, Utils.currentTimeSeconds(), 0, transactions);
}
19
Source : ParseByteCacheTest.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
public clreplaced ParseByteCacheTest {
private static final int BLOCK_HEIGHT_GENESIS = 0;
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode("f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" + "02 01 00 00 e2 93 cd be 01 00 00 00 01 6d bd db" + "08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a" + "59 23 3f 45 bc 57 83 80 02 20 59 af 01 ca 17 d0" + "0e 41 83 7a 1d 58 e9 7a a3 1b ae 58 4e de c2 8d" + "35 bd 96 92 36 90 91 3b ae 9a 01 41 04 9c 02 bf" + "c9 7e f2 36 ce 6d 8f e5 d9 40 13 c7 21 e9 15 98" + "2a cd 2b 12 b6 5d 9b 7d 59 e2 0a 84 20 05 f8 fc" + "4e 02 53 2e 87 3d 37 b9 6f 09 d6 d4 51 1a da 8f" + "14 04 2f 46 61 4a 4c 70 c0 f1 4b ef f5 ff ff ff" + "ff 02 40 4b 4c 00 00 00 00 00 19 76 a9 14 1a a0" + "cd 1c be a6 e7 45 8a 7a ba d5 12 a9 d9 ea 1a fb" + "22 5e 88 ac 80 fa e9 c7 00 00 00 00 19 76 a9 14" + "0e ab 5b ea 43 6a 04 84 cf ab 12 48 5e fd a0 b7" + "8b 4e cc 52 88 ac 00 00 00 00");
private final byte[] txMessagePart = HEX.withSeparator(" ", 2).decode("08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a");
private BlockStore blockStore;
private static final NetworkParameters PARAMS = UnitTestParams.get();
private byte[] b1Bytes;
private byte[] b1BytesWithHeader;
private byte[] tx1Bytes;
private byte[] tx1BytesWithHeader;
private byte[] tx2Bytes;
private byte[] tx2BytesWithHeader;
private void resetBlockStore() {
blockStore = new MemoryBlockStore(PARAMS);
}
@Before
public void setUp() throws Exception {
Context context = new Context(PARAMS);
Wallet wallet = new Wallet(context);
wallet.freshReceiveKey();
resetBlockStore();
Transaction tx1 = createFakeTx(PARAMS, valueOf(2, 0), wallet.currentReceiveKey().toAddress(PARAMS));
// add a second input so can test granularity of byte cache.
Transaction prevTx = new Transaction(PARAMS);
TransactionOutput prevOut = new TransactionOutput(PARAMS, prevTx, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
prevTx.addOutput(prevOut);
// Connect it.
tx1.addInput(prevOut);
Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
Block b1 = createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS, tx1, tx2).block;
MessageSerializer bs = PARAMS.getDefaultSerializer();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(tx1, bos);
tx1BytesWithHeader = bos.toByteArray();
tx1Bytes = tx1.bitcoinSerialize();
bos.reset();
bs.serialize(tx2, bos);
tx2BytesWithHeader = bos.toByteArray();
tx2Bytes = tx2.bitcoinSerialize();
bos.reset();
bs.serialize(b1, bos);
b1BytesWithHeader = bos.toByteArray();
b1Bytes = b1.bitcoinSerialize();
}
@Test
public void validateSetup() {
byte[] b1 = { 1, 1, 1, 2, 3, 4, 5, 6, 7 };
byte[] b2 = { 1, 2, 3 };
replacedertTrue(arrayContains(b1, b2));
replacedertTrue(arrayContains(txMessage, txMessagePart));
replacedertTrue(arrayContains(tx1BytesWithHeader, tx1Bytes));
replacedertTrue(arrayContains(tx2BytesWithHeader, tx2Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, b1Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, tx1Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, tx2Bytes));
replacedertFalse(arrayContains(tx1BytesWithHeader, b1Bytes));
}
@Test
public void testTransactionsRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, true);
testTransaction(PARAMS, tx1BytesWithHeader, false, true);
testTransaction(PARAMS, tx2BytesWithHeader, false, true);
}
@Test
public void testTransactionsNoRetain() throws Exception {
testTransaction(MainNetParams.get(), txMessage, false, false);
testTransaction(PARAMS, tx1BytesWithHeader, false, false);
testTransaction(PARAMS, tx2BytesWithHeader, false, false);
}
@Test
public void testBlockAll() throws Exception {
testBlock(b1BytesWithHeader, false, false);
testBlock(b1BytesWithHeader, false, true);
}
public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = PARAMS.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = PARAMS.getSerializer(retain);
Block b1;
Block bRef;
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// verify our reference BitcoinSerializer produces matching byte array.
bos.reset();
bsRef.serialize(bRef, bos);
replacedertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
// check retain status survive both before and after a serialization
replacedertEquals(retain, b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
serDeser(bs, b1, blockBytes, null, null);
replacedertEquals(retain, b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
// compare to ref block
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// retrieve a value from a child
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
// this will always be true for all children of a block once they are retrieved.
// the tx child inputs/outputs may not be parsed however.
replacedertEquals(retain, tx1.isCached());
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from header
b1.getDifficultyTarget();
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from a child and header
b1.getDifficultyTarget();
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
replacedertEquals(retain, tx1.isCached());
}
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// change a value in header
b1.setNonce(23);
bRef.setNonce(23);
replacedertFalse(b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
// does it still match ref block?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from a child of a child
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
TransactionInput tin = tx1.getInputs().get(0);
replacedertEquals(retain, tin.isCached());
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// add an input
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
if (tx1.getInputs().size() > 0) {
tx1.addInput(tx1.getInputs().get(0));
// replicate on reference tx
bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0));
replacedertFalse(tx1.isCached());
replacedertFalse(b1.isTransactionBytesValid());
// confirm sibling cache status was unaffected
if (tx1.getInputs().size() > 1) {
replacedertEquals(retain, tx1.getInputs().get(1).isCached());
}
// this has to be false. Altering a tx invalidates the merkle root.
// when we have seperate merkle caching then the entire header won't need to be
// invalidated.
replacedertFalse(b1.isHeaderBytesValid());
bos.reset();
bsRef.serialize(bRef, bos);
byte[] source = bos.toByteArray();
// confirm we still match the reference tx.
serDeser(bs, b1, source, null, null);
}
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// reparent an input
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
Transaction tx2 = b2.getTransactions().get(0);
if (tx1.getInputs().size() > 0) {
TransactionInput fromTx1 = tx1.getInputs().get(0);
tx2.addInput(fromTx1);
// replicate on reference tx
TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0);
bRef2.getTransactions().get(0).addInput(fromTxRef);
// b1 hasn't changed but it's no longer in the parent
// chain of fromTx1 so has to have been uncached since it won't be
// notified of changes throught the parent chain anymore.
replacedertFalse(b1.isTransactionBytesValid());
// b2 should have it's cache invalidated because it has changed.
replacedertFalse(b2.isTransactionBytesValid());
bos.reset();
bsRef.serialize(bRef2, bos);
byte[] source = bos.toByteArray();
// confirm altered block matches altered ref block.
serDeser(bs, b2, source, null, null);
}
// does unaltered block still match ref block?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// how about if we refresh it?
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
}
public void testTransaction(NetworkParameters params, byte[] txBytes, boolean isChild, boolean retain) throws Exception {
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = params.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = params.getSerializer(retain);
Transaction t1;
Transaction tRef;
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
// verify our reference BitcoinSerializer produces matching byte array.
bos.reset();
bsRef.serialize(tRef, bos);
replacedertTrue(Arrays.equals(bos.toByteArray(), txBytes));
// check and retain status survive both before and after a serialization
replacedertEquals(retain, t1.isCached());
serDeser(bs, t1, txBytes, null, null);
replacedertEquals(retain, t1.isCached());
// compare to ref tx
bos.reset();
bsRef.serialize(tRef, bos);
serDeser(bs, t1, bos.toByteArray(), null, null);
// retrieve a value from a child
t1.getInputs();
if (t1.getInputs().size() > 0) {
TransactionInput tin = t1.getInputs().get(0);
replacedertEquals(retain, tin.isCached());
// does it still match ref tx?
serDeser(bs, t1, bos.toByteArray(), null, null);
}
// refresh tx
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
// add an input
if (t1.getInputs().size() > 0) {
t1.addInput(t1.getInputs().get(0));
// replicate on reference tx
tRef.addInput(tRef.getInputs().get(0));
replacedertFalse(t1.isCached());
bos.reset();
bsRef.serialize(tRef, bos);
byte[] source = bos.toByteArray();
// confirm we still match the reference tx.
serDeser(bs, t1, source, null, null);
}
}
private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(message, bos);
byte[] b1 = bos.toByteArray();
Message m2 = bs.deserialize(ByteBuffer.wrap(b1));
replacedertEquals(message, m2);
bos.reset();
bs.serialize(m2, bos);
byte[] b2 = bos.toByteArray();
replacedertTrue(Arrays.equals(b1, b2));
if (sourceBytes != null) {
replacedertTrue(arrayContains(sourceBytes, b1));
replacedertTrue(arrayContains(sourceBytes, b2));
}
if (containedBytes != null) {
replacedertTrue(arrayContains(b1, containedBytes));
}
if (containingBytes != null) {
replacedertTrue(arrayContains(containingBytes, b1));
}
}
public static boolean arrayContains(byte[] sup, byte[] sub) {
if (sup.length < sub.length)
return false;
String superstring = Utils.HEX.encode(sup);
String substring = Utils.HEX.encode(sub);
int ind = superstring.indexOf(substring);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < superstring.indexOf(substring); i++) sb.append(" ");
// System.out.println(superstring);
// System.out.println(sb.append(substring).toString());
// System.out.println();
return ind > -1;
}
}
19
Source : BlockChainTest.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private BlockChain testNetChain;
private Wallet wallet;
private BlockChain chain;
private BlockStore blockStore;
private Address coinbaseTo;
private static final NetworkParameters PARAMS = UnitTestParams.get();
private final StoredBlock[] block = new StoredBlock[1];
private Transaction coinbaseTransaction;
private static clreplaced TweakableTestNet2Params extends TestNet2Params {
public void setMaxTarget(BigInteger limit) {
maxTarget = limit;
}
}
private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params();
private void resetBlockStore() {
blockStore = new MemoryBlockStore(PARAMS);
}
@Before
public void setUp() throws Exception {
BriefLogFormatter.initVerbose();
Context.propagate(new Context(testNet, 100, Coin.ZERO, false));
testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
wallet = new Wallet(PARAMS) {
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException {
super.receiveFromBlock(tx, block, blockType, relativityOffset);
BlockChainTest.this.block[0] = block;
if (isTransactionRelevant(tx) && tx.isCoinBase()) {
BlockChainTest.this.coinbaseTransaction = tx;
}
}
};
wallet.freshReceiveKey();
resetBlockStore();
chain = new BlockChain(PARAMS, wallet, blockStore);
coinbaseTo = wallet.currentReceiveKey().toAddress(PARAMS);
}
@Test
public void testBasicChaining() throws Exception {
// Check that we can plug a few blocks together and the futures work.
ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
// Block 1 from the testnet.
Block b1 = getBlock1();
replacedertTrue(testNetChain.add(b1));
replacedertFalse(future.isDone());
// Block 2 from the testnet.
Block b2 = getBlock2();
// Let's try adding an invalid block.
long n = b2.getNonce();
try {
b2.setNonce(12345);
testNetChain.add(b2);
fail();
} catch (VerificationException e) {
b2.setNonce(n);
}
// Now it works because we reset the nonce.
replacedertTrue(testNetChain.add(b2));
replacedertTrue(future.isDone());
replacedertEquals(2, future.get().getHeight());
}
@Test
public void receiveCoins() throws Exception {
int height = 1;
// Quick check that we can actually receive coins.
Transaction tx1 = createFakeTx(PARAMS, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
Block b1 = createFakeBlock(blockStore, height, tx1).block;
chain.add(b1);
replacedertTrue(wallet.getBalance().signum() > 0);
}
@Test
public void unconnectedBlocks() throws Exception {
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
// Connected.
replacedertTrue(chain.add(b1));
// Unconnected but stored. The head of the chain is still b1.
replacedertFalse(chain.add(b3));
replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
// Add in the middle block.
replacedertTrue(chain.add(b2));
replacedertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader());
}
@Test
public void difficultyTransitions() throws Exception {
// Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an
// artificially shortened period.
Block prev = PARAMS.getGenesisBlock();
Utils.setMockClock(System.currentTimeMillis() / 1000);
for (int height = 0; height < PARAMS.getInterval() - 1; height++) {
Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
replacedertTrue(chain.add(newBlock));
prev = newBlock;
// The fake chain should seem to be "fast" for the purposes of difficulty calculations.
Utils.rollMockClock(2);
}
// Now add another block that has no difficulty adjustment, it should be rejected.
try {
chain.add(prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), PARAMS.getInterval()));
fail();
} catch (VerificationException e) {
}
// Create a new block with the right difficulty target given our blistering speed relative to the huge amount
// of time it's supposed to take (set in the unit test network parameters).
Block b = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), PARAMS.getInterval() + 1);
b.setDifficultyTarget(0x201fFFFFL);
b.solve();
replacedertTrue(chain.add(b));
// Successfully traversed a difficulty transition period.
}
@Test
public void badDifficulty() throws Exception {
replacedertTrue(testNetChain.add(getBlock1()));
Block b2 = getBlock2();
replacedertTrue(testNetChain.add(b2));
Block bad = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
// Merkle root can be anything here, doesn't matter.
bad.setMerkleRoot(Sha256Hash.wrap("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
bad.setNonce(140548933);
bad.setTime(1279242649);
bad.setPrevBlockHash(b2.getHash());
// We're going to make this block so easy 50% of solutions will preplaced, and check it gets rejected for having a
// bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all
// solutions.
bad.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
try {
testNetChain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
// allowable difficulty.
fail();
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Difficulty target is bad"));
}
// Accept any level of difficulty now.
BigInteger oldVal = testNet.getMaxTarget();
testNet.setMaxTarget(new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16));
try {
testNetChain.add(bad);
// We should not get here as the difficulty target should not be changing at this point.
fail();
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Unexpected change in difficulty"));
}
testNet.setMaxTarget(oldVal);
// TODO: Test difficulty change is not out of range when a transition period becomes valid.
}
/**
* Test that version 2 blocks are rejected once version 3 blocks are a super
* majority.
*/
@Test
public void badBip66Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
}
/**
* Test that version 3 blocks are rejected once version 4 blocks are a super
* majority.
*/
@Test
public void badBip65Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
}
private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; height < PARAMS.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
try {
versionChain.add(chainHead.block);
} catch (final VerificationException ex) {
throw (Exception) ex.getCause();
}
}
@Test
public void duplicates() throws Exception {
// Adding a block twice should not have any effect, in particular it should not send the block to the wallet.
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
replacedertTrue(chain.add(b1));
replacedertEquals(b1, block[0].getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b2, block[0].getHeader());
replacedertTrue(chain.add(b3));
replacedertEquals(b3, block[0].getHeader());
replacedertEquals(b3, chain.getChainHead().getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b3, chain.getChainHead().getHeader());
// Wallet was NOT called with the new block because the duplicate add was spotted.
replacedertEquals(b3, block[0].getHeader());
}
@Test
public void intraBlockDependencies() throws Exception {
// Covers issue 166 in which transactions that depend on each other inside a block were not always being
// considered relevant.
Address somebodyElse = new ECKey().toAddress(PARAMS);
Block b1 = PARAMS.getGenesisBlock().createNextBlock(somebodyElse);
ECKey key = wallet.freshReceiveKey();
Address addr = key.toAddress(PARAMS);
// Create a tx that gives us some coins, and another that spends it to someone else in the same block.
Transaction t1 = FakeTxBuilder.createFakeTx(PARAMS, COIN, addr);
Transaction t2 = new Transaction(PARAMS);
t2.addInput(t1.getOutputs().get(0));
t2.addOutput(valueOf(2, 0), somebodyElse);
b1.addTransaction(t1);
b1.addTransaction(t2);
b1.solve();
chain.add(b1);
replacedertEquals(Coin.ZERO, wallet.getBalance());
}
@Test
public void coinbaseTransactionAvailability() throws Exception {
// Check that a coinbase transaction is only available to spend after NetworkParameters.getSpendableCoinbaseDepth() blocks.
// Create a second wallet to receive the coinbase spend.
Wallet wallet2 = new Wallet(PARAMS);
ECKey receiveKey = wallet2.freshReceiveKey();
int height = 1;
chain.addWallet(wallet2);
Address addressToSendTo = receiveKey.toAddress(PARAMS);
// Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
Block b1 = PARAMS.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
chain.add(b1);
// Check a transaction has been received.
replacedertNotNull(coinbaseTransaction);
// The coinbase tx is not yet available to spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail as the coinbase is not mature yet.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
// Check that the coinbase is unavailable to spend for the next spendableCoinbaseDepth - 2 blocks.
for (int i = 0; i < PARAMS.getSpendableCoinbaseDepth() - 2; i++) {
// Non relevant tx - just for fake block creation.
Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
Block b2 = createFakeBlock(blockStore, height++, tx2).block;
chain.add(b2);
// Wallet still does not have the coinbase transaction available for spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
// The coinbase transaction is still not mature.
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
}
// Give it one more block - should now be able to spend coinbase transaction. Non relevant tx.
Transaction tx3 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
Block b3 = createFakeBlock(blockStore, height++, tx3).block;
chain.add(b3);
// Wallet now has the coinbase transaction available for spend.
replacedertEquals(wallet.getBalance(), FIFTY_COINS);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(coinbaseTransaction.isMature());
// Create a spend with the coinbase BTC to the address in the second wallet - this should now succeed.
Transaction coinbaseSend2 = wallet.createSend(addressToSendTo, valueOf(49, 0));
replacedertNotNull(coinbaseSend2);
// Commit the coinbaseSpend to the first wallet and check the balances decrement.
wallet.commitTx(coinbaseSend2);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
// Available balance is zero as change has not been received from a block yet.
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), ZERO);
// Give it one more block - change from coinbaseSpend should now be available in the first wallet.
Block b4 = createFakeBlock(blockStore, height++, coinbaseSend2).block;
chain.add(b4);
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
// Check the balances in the second wallet.
replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
}
// Some blocks from the test net.
private static Block getBlock2() throws Exception {
Block b2 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
b2.setMerkleRoot(Sha256Hash.wrap("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
b2.setNonce(2642058077L);
b2.setTime(1296734343L);
b2.setPrevBlockHash(Sha256Hash.wrap("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
replacedertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashreplacedtring());
b2.verifyHeader();
return b2;
}
private static Block getBlock1() throws Exception {
Block b1 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
b1.setMerkleRoot(Sha256Hash.wrap("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
b1.setNonce(236038445);
b1.setTime(1296734340);
b1.setPrevBlockHash(Sha256Hash.wrap("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
replacedertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashreplacedtring());
b1.verifyHeader();
return b1;
}
@Test
public void estimatedBlockTime() throws Exception {
NetworkParameters params = MainNetParams.get();
BlockChain prod = new BlockChain(new Context(params), new MemoryBlockStore(params));
Date d = prod.estimateBlockTime(200000);
// The actual date of block 200,000 was 2012-09-22 10:47:00
replacedertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US).parse("2012-10-23T08:35:05.000-0700"), d);
}
@Test
public void falsePositives() throws Exception {
double decay = AbstractBlockChain.FP_ESTIMATOR_ALPHA;
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
double rate1 = chain.getFalsePositiveRate();
// Run this scenario a few more time for the filter to converge
for (int i = 1; i < 10; i++) {
chain.trackFalsePositives(55);
chain.trackFilteredTransactions(550);
}
// Ensure we are within 10%
replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
// Check that we get repeatable results after a reset
chain.resetFalsePositiveEstimate();
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
replacedertEquals(rate1, chain.getFalsePositiveRate(), 1e-4);
}
@Test
public void rollbackBlockStore() throws Exception {
// This test simulates an issue on Android, that causes the VM to crash while receiving a block, so that the
// block store is persisted but the wallet is not.
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
// Add block 1, no frills.
replacedertTrue(chain.add(b1));
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add block 2 while wallet is disconnected, to simulate crash.
chain.removeWallet(wallet);
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add wallet back. This will detect the height mismatch and repair the damage done.
chain.addWallet(wallet);
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Now add block 2 correctly.
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(2, wallet.getLastBlockSeenHeight());
}
}
19
Source : VersionTally.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Initialize the version tally from the block store. Note this does not
* search backwards past the start of the block store, so if starting from
* a checkpoint this may not fill the window.
*
* @param blockStore block store to load blocks from.
* @param chainHead current chain tip.
*/
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
StoredBlock versionBlock = chainHead;
final Stack<Long> versions = new Stack<>();
// We don't know how many blocks back we can go, so load what we can first
versions.push(versionBlock.getHeader().getVersion());
for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
versionBlock = versionBlock.getPrev(blockStore);
if (null == versionBlock) {
break;
}
versions.push(versionBlock.getHeader().getVersion());
}
// Replay the versions into the tally
while (!versions.isEmpty()) {
add(versions.pop());
}
}
19
Source : TestNet3Params.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore) throws VerificationException, BlockStoreException {
if (!isDifficultyTransitionPoint(storedPrev.getHeight()) && nextBlock.getTime().after(testnetDiffDate)) {
Block prev = storedPrev.getHeader();
// After 15th February 2012 the rules on the testnet change to avoid people running up the difficulty
// and then leaving, making it too hard to mine a block. On non-difficulty transition points, easy
// blocks are allowed if there has been a span of 20 minutes without one.
final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
// There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time
// goes backwards.
if (timeDelta >= 0 && timeDelta <= NetworkParameters.TARGET_SPACING * 2) {
// Walk backwards until we find a block that doesn't have the easiest proof of work, then check
// that difficulty is equal to that one.
StoredBlock cursor = storedPrev;
while (!cursor.getHeader().equals(getGenesisBlock()) && cursor.getHeight() % getInterval() != 0 && cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) cursor = cursor.getPrev(blockStore);
BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger();
BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger();
if (!cursorTarget.equals(newTarget))
throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
}
} else {
super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore);
}
}
19
Source : StoredBlock.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Given a block store, looks up the previous block in this chain. Convenience method for doing
* <tt>store.get(this.getHeader().getPrevBlockHash())</tt>.
*
* @return the previous block in the chain or null if it was not found in the store.
*/
public StoredBlock getPrev(BlockStore store) throws BlockStoreException {
return store.get(getHeader().getPrevBlockHash());
}
19
Source : BlockChain.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
* A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
* choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
* all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
*/
public clreplaced BlockChain extends AbstractBlockChain {
/**
* Keeps a map of block hashes to StoredBlocks.
*/
protected final BlockStore blockStore;
/**
* <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
* one from scratch, or you can deserialize a saved wallet from disk using
* {@link Wallet#loadFromFile(java.io.File, WalletExtension...)}</p>
*
* <p>For the store, you should use {@link org.bitcoinj.store.SPVBlockStore} or you could also try a
* {@link org.bitcoinj.store.MemoryBlockStore} if you want to hold all headers in RAM and don't care about
* disk serialization (this is rare).</p>
*/
public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
/**
* See {@link #BlockChain(Context, Wallet, BlockStore)}}
*/
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallet, blockStore);
}
/**
* Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
* and receiving coins but rather, just want to explore the network data structures.
*/
public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
}
/**
* See {@link #BlockChain(Context, BlockStore)}
*/
public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
this(params, new ArrayList<Wallet>(), blockStore);
}
/**
* Constructs a BlockChain connected to the given list of listeners and a store.
*/
public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
super(params, wallets, blockStore);
this.blockStore = blockStore;
}
/**
* See {@link #BlockChain(Context, List, BlockStore)}
*/
public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallets, blockStore);
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected void rollbackBlockStore(int height) throws BlockStoreException {
lock.lock();
try {
int currentHeight = getBestChainHeight();
checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
if (height == currentHeight)
// nothing to do
return;
// Look for the block we want to be the new chain head
StoredBlock newChainHead = blockStore.getChainHead();
while (newChainHead.getHeight() > height) {
newChainHead = newChainHead.getPrev(blockStore);
if (newChainHead == null)
throw new BlockStoreException("Unreachable height");
}
// Modify store directly
blockStore.put(newChainHead);
this.setChainHead(newChainHead);
} finally {
lock.unlock();
}
}
@Override
protected boolean shouldVerifyTransactions() {
return false;
}
@Override
protected TransactionOutputChanges connectTransactions(int height, Block block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void disconnectTransactions(StoredBlock block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
blockStore.setChainHead(chainHead);
}
@Override
protected void notSettingChainHead() throws BlockStoreException {
// We don't use DB transactions here, so we don't need to do anything
}
@Override
protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
return blockStore.get(hash);
}
@Override
public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
boolean success = super.add(block);
if (success) {
trackFilteredTransactions(block.getTransactionCount());
}
return success;
}
}
19
Source : Blocks.java
with MIT License
from ppkpub
with MIT License
from ppkpub
public clreplaced Blocks implements Runnable {
public static NetworkParameters params;
public Logger logger = LoggerFactory.getLogger(Blocks.clreplaced);
public static boolean bRemoteWalletMode = false;
public Wallet wallet = null;
public PeerGroup peerGroup;
public BlockChain blockChain;
public BlockStore blockStore;
public Boolean working = false;
public Boolean parsing = false;
public Boolean initializing = false;
public Boolean initialized = false;
public Integer parsingBlock = 0;
public Integer versionCheck = 0;
public Integer bitcoinBlock = 0;
public Integer ppkBlock = 0;
public String statusMessage = "";
private static Blocks instance = null;
private static HashMap<String, List<UnspentOutput>> cachedLastUnspentList = new HashMap<String, List<UnspentOutput>>();
public static Blocks getInstanceSkipVersionCheck() {
System.out.println("aaaaa");
if (instance == null) {
System.out.println("bbbbb");
instance = new Blocks();
}
System.out.println("ccccc");
return instance;
}
public static Blocks getInstanceFresh() {
if (instance == null) {
instance = new Blocks();
instance.versionCheck();
}
return instance;
}
public static Blocks getInstanceAndWait() {
if (instance == null) {
instance = new Blocks();
instance.versionCheck();
new Thread() {
public void run() {
instance.init();
}
}.start();
}
instance.follow();
return instance;
}
public static Blocks getInstance() {
if (instance == null) {
instance = new Blocks();
instance.versionCheck();
new Thread() {
public void run() {
instance.init();
}
}.start();
}
if (!instance.working && instance.initialized) {
new Thread() {
public void run() {
instance.follow();
}
}.start();
}
return instance;
}
public void versionCheck() {
versionCheck(false);
}
public void versionCheck(Boolean autoUpdate) {
Integer minMajorVersion = Util.getMinMajorVersion();
Integer minMinorVersion = Util.getMinMinorVersion();
if (Config.majorVersion < minMajorVersion || (Config.majorVersion.equals(minMajorVersion) && Config.minorVersion < minMinorVersion)) {
if (autoUpdate) {
statusMessage = "Version is out of date, updating now";
logger.info(statusMessage);
try {
Runtime.getRuntime().exec("java -jar update/update.jar");
} catch (Exception ex) {
ex.printStackTrace();
}
} else {
logger.info("Version is out of date. Please upgrade to version " + Util.getMinVersion() + ".");
}
System.exit(0);
}
}
@Override
public void run() {
while (true) {
logger.info("Looping blocks");
Blocks.getInstance();
try {
// once a minute, we run blocks.follow()
Thread.sleep(1000 * 60);
} catch (InterruptedException e) {
logger.error("Error during loop: " + e.toString());
}
}
}
public void init() {
if (!initializing) {
initializing = true;
Locale.setDefault(new Locale("en", "US"));
params = MainNetParams.get();
try {
if (!bRemoteWalletMode) {
// 本地钱包模式
File localWalletFile = new File(Config.walletFile);
if (localWalletFile.exists()) {
statusMessage = Language.getLangLabel("Found wallet file");
logger.info(statusMessage);
wallet = Wallet.loadFromFile(localWalletFile);
} else {
statusMessage = Language.getLangLabel("Creating new wallet file");
logger.info(statusMessage);
wallet = new Wallet(params);
// ForTest,20190616
String testBatchImportPrvFileName = "resources/db/batch_prv_list.txt";
File testBatchImportPrvFile = new File(testBatchImportPrvFileName);
if (testBatchImportPrvFile.exists()) {
// 存在指定的批量私钥文件,则批量导入初始化钱包
try {
InputStreamReader read = new InputStreamReader(new FileInputStream(testBatchImportPrvFileName), "ISO-8859-1");
BufferedReader reader = new BufferedReader(read);
String line;
int imported_counter = 0;
while ((line = reader.readLine()) != null) {
if (line.startsWith("L") || line.startsWith("K") || line.startsWith("5")) {
try {
importPrivateKey(line);
imported_counter++;
} catch (Exception e) {
logger.error("Blocks.init() testBatchImportPrvFile meet invalid prvkey:" + line);
}
}
}
reader.close();
read.close();
logger.info("Batch imported " + imported_counter + " addresses. ");
} catch (Exception e) {
logger.error("Blocks.init() testBatchImportPrvFile failed:" + e.toString());
}
} else {
// 创建默认的单个地址初始钱包
ECKey newKey = new ECKey();
importPrivateKey(newKey);
}
// newKey.setCreationTimeSeconds(Config.ppkToolCreationTime);
// wallet.addKey(newKey);
// wallet.saveToFile(localWalletFile);
}
}
String fileBTCdb = Config.dbDirPrefix + Config.appName.toLowerCase() + ".h2.db";
String fileODINdb = Config.defaultSqliteFile;
if (!new File(fileODINdb).exists()) {
statusMessage = "Downloading ODIN database";
logger.info(statusMessage);
Util.downloadToFile(Config.downloadURL + Config.appName.toLowerCase() + "-" + Config.majorVersionDB.toString() + ".db", fileODINdb);
}
statusMessage = Language.getLangLabel("Downloading Bitcoin blocks");
blockStore = new H2FullPrunedBlockStore(params, Config.dbDirPrefix + Config.appName.toLowerCase(), 2000);
if (wallet == null) {
blockChain = new BlockChain(params, blockStore);
peerGroup = new PeerGroup(params, blockChain);
peerGroup.setFastCatchupTimeSecs(Config.ppkToolCreationTime);
} else {
blockChain = new BlockChain(params, wallet, blockStore);
peerGroup = new PeerGroup(params, blockChain);
peerGroup.setFastCatchupTimeSecs(Config.ppkToolCreationTime);
// peerGroup.addWallet(wallet); //不需要同步保存历史交易到wallet文件中,减少损坏可能
// wallet.autosaveToFile(new File(Config.walletFile), 1, TimeUnit.MINUTES, null);
}
peerGroup.addPeerDiscovery(new DnsDiscovery(params));
// peerGroup.startAndWait(); //for bitcoinj0.14
peerGroup.start();
peerGroup.addEventListener(new PPkPeerEventListener());
peerGroup.downloadBlockChain();
while (!hasChainHead()) {
try {
logger.info("Blockstore doesn't yet have a chain head, so we are sleeping.");
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
Database db = Database.getInstance();
try {
Integer lastParsedBlock = Util.getLastParsedBlock();
if (lastParsedBlock.equals(0)) {
db.executeUpdate("CREATE TABLE IF NOT EXISTS sys_parameters (para_name VARCHAR(32) PRIMARY KEY, para_value TEXT )");
lastParsedBlock = Util.getLastBlock();
Util.updateLastParsedBlock(lastParsedBlock);
}
} catch (Exception e) {
logger.error(e.toString());
}
ODIN.init();
} catch (Exception e) {
logger.error("Error during init: " + e.toString());
// e.printStackTrace();
System.exit(-1);
}
initialized = true;
initializing = false;
}
}
public void deleteDatabases() {
logger.info("Deleting Bitcoin and ODIN databases");
String fileBTCdb = Config.dbDirPrefix + Config.appName.toLowerCase() + ".h2.db";
new File(fileBTCdb).delete();
String fileODINdb = Config.defaultSqliteFile;
new File(fileODINdb).delete();
}
public Boolean hasChainHead() {
try {
Integer blockHeight = blockStore.getChainHead().getHeight();
return true;
} catch (Exception e) {
return false;
}
}
public void follow() {
follow(false);
}
public void follow(Boolean force) {
logger.info("Working status: " + working);
logger.info("initialized: " + initialized);
if ((!working && initialized) || force) {
statusMessage = "Checking block height";
logger.info(statusMessage);
if (!force) {
working = true;
}
try {
// catch ODIN up to Bitcoin
Integer blockHeight = blockStore.getChainHead().getHeight();
Integer lastBlock = Util.getLastBlock();
Integer lastBlockTime = Util.getLastBlockTimestamp();
bitcoinBlock = blockHeight;
ppkBlock = lastBlock;
if (lastBlock == 0) {
lastBlock = Config.firstBlock - 1;
}
Integer nextBlock = lastBlock + 1;
logger.info("Bitcoin block height: " + blockHeight);
logger.info("PPk block height: " + lastBlock);
if (lastBlock < blockHeight) {
// traverse new blocks
parsing = true;
Integer blocksToScan = blockHeight - lastBlock;
List<Sha256Hash> blockHashes = new ArrayList<Sha256Hash>();
Block block = peerGroup.getDownloadPeer().getBlock(blockStore.getChainHead().getHeader().getHash()).get(59, TimeUnit.SECONDS);
while (blockStore.get(block.getHash()).getHeight() > lastBlock) {
blockHashes.add(block.getHash());
block = blockStore.get(block.getPrevBlockHash()).getHeader();
}
for (int i = blockHashes.size() - 1; i >= 0; i--) {
// traverse blocks in reverse order
block = peerGroup.getDownloadPeer().getBlock(blockHashes.get(i)).get(59, TimeUnit.SECONDS);
blockHeight = blockStore.get(block.getHash()).getHeight();
ppkBlock = blockHeight;
statusMessage = "Catching ODIN up to Bitcoin " + Util.format((blockHashes.size() - i) / ((double) blockHashes.size()) * 100.0) + "%";
logger.info("Catching ODIN up to Bitcoin (block " + blockHeight.toString() + "): " + Util.format((blockHashes.size() - i) / ((double) blockHashes.size()) * 100.0) + "%");
importBlock(block, blockHeight);
}
parsing = false;
}
} catch (Exception e) {
logger.error("Error during follow: " + e.toString());
e.printStackTrace();
}
// Ensure to parse new imported blocks while follow finished or failed
try {
Integer lastImportedBlock = Util.getLastBlock();
Integer lastImportedBlockTime = Util.getLastBlockTimestamp();
Integer lastParsedBlock = Util.getLastParsedBlock();
if (lastParsedBlock < lastImportedBlock) {
parsing = true;
parseFrom(lastParsedBlock + 1, true);
parsing = false;
}
} catch (Exception e) {
logger.error("Error during parse: " + e.toString());
e.printStackTrace();
}
if (!force) {
working = false;
}
}
}
public void reDownloadBlockTransactions(Integer blockHeight) {
Database db = Database.getInstance();
ResultSet rs = db.executeQuery("select * from blocks where block_index='" + blockHeight.toString() + "';");
try {
if (rs.next()) {
Block block = peerGroup.getDownloadPeer().getBlock(new Sha256Hash(rs.getString("block_hash"))).get();
db.executeUpdate("delete from transactions where block_index='" + blockHeight.toString() + "';");
Integer txSnInBlock = 0;
for (Transaction tx : block.getTransactions()) {
importPPkTransaction(tx, txSnInBlock, block, blockHeight);
txSnInBlock++;
}
}
} catch (Exception e) {
}
}
public void importBlock(Block block, Integer blockHeight) {
statusMessage = "Importing block " + blockHeight;
logger.info(statusMessage);
Database db = Database.getInstance();
ResultSet rs = db.executeQuery("select * from blocks where block_hash='" + block.getHashreplacedtring() + "';");
try {
if (!rs.next()) {
db.executeUpdate("INSERT INTO blocks(block_index,block_hash,block_time,block_nonce) VALUES('" + blockHeight.toString() + "','" + block.getHashreplacedtring() + "','" + block.getTimeSeconds() + "','" + block.getNonce() + "')");
}
Integer txSnInBlock = 0;
for (Transaction tx : block.getTransactions()) {
importPPkTransaction(tx, txSnInBlock, block, blockHeight);
txSnInBlock++;
}
} catch (SQLException e) {
}
}
public void reparse() {
reparse(false);
}
public void reparse(final Boolean force) {
Database db = Database.getInstance();
db.executeUpdate("delete from odins;");
db.executeUpdate("delete from odin_update_logs;");
db.executeUpdate("delete from balances;");
db.executeUpdate("delete from sends;");
db.executeUpdate("delete from messages;");
db.executeUpdate("delete from sys_parameters;");
new Thread() {
public void run() {
parseFrom(Config.firstBlock, force);
}
}.start();
}
public void parseFrom(Integer blockNumber) {
parseFrom(blockNumber, false);
}
public void parseFrom(Integer blockNumber, Boolean force) {
if (!working || force) {
parsing = true;
if (!force) {
working = true;
}
Database db = Database.getInstance();
ResultSet rs = db.executeQuery("select * from blocks where block_index>=" + blockNumber.toString() + " order by block_index asc;");
try {
while (rs.next()) {
Integer blockIndex = rs.getInt("block_index");
Integer blockTime = rs.getInt("block_time");
parseBlock(blockIndex, blockTime);
Util.updateLastParsedBlock(blockIndex);
}
} catch (SQLException e) {
e.printStackTrace();
}
if (!force) {
working = false;
}
parsing = false;
}
}
public void parseBlock(Integer blockIndex, Integer blockTime) {
Database db = Database.getInstance();
ResultSet rsTx = db.executeQuery("select * from transactions where block_index=" + blockIndex.toString() + " order by tx_index asc;");
parsingBlock = blockIndex;
statusMessage = "\n++++++++++++++++++++++++++++++++++\n Parsing block " + blockIndex.toString() + "\n++++++++++++++++++++++++++++++++++\n";
logger.info(statusMessage);
try {
while (rsTx.next()) {
Integer txIndex = rsTx.getInt("tx_index");
String source = rsTx.getString("source");
String destination = rsTx.getString("destination");
BigInteger amount_satoshi = BigInteger.valueOf(rsTx.getInt("btc_amount"));
byte[] odin_data = Util.hexStringToBytes(rsTx.getString("data"));
Integer prefix_type = rsTx.getInt("prefix_type");
if (1 == prefix_type) {
// PPk ODIN
Byte messageType = getPPkMessageTypeFromTransaction(odin_data);
List<Byte> message = getPPkMessageFromTransaction(odin_data);
logger.info("\n--------------------\n Parsing PPk txIndex " + txIndex.toString() + "\n------------\n");
if (messageType != null && message != null) {
logger.info("\n--------------------\n Parsing PPk messageType " + messageType.toString() + "\n------------\n");
if (messageType == ODIN.id) {
ODIN.parse(txIndex, message);
} else if (messageType == OdinUpdate.id) {
OdinUpdate.parse(txIndex, message);
}
}
} else {
// normal bitcoin operation
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
public Integer getDBMinorVersion() {
Database db = Database.getInstance();
ResultSet rs = db.executeQuery("PRAGMA user_version;");
try {
while(rs.next()) {
return rs.getInt("user_version");
}
} catch (SQLException e) {
}
return 0;
}
public void updateMinorVersion() {
// Update minor version
Database db = Database.getInstance();
db.executeUpdate("PRAGMA user_version = "+Config.minorVersionDB.toString());
}
*/
public Integer getHeight() {
try {
Integer height = blockStore.getChainHead().getHeight();
return height;
} catch (BlockStoreException e) {
}
return 0;
}
public String importPrivateKey(ECKey key) throws Exception {
String address = "";
logger.info("Importing private key");
address = key.toAddress(params).toString();
logger.info("Importing address " + address);
if (wallet.getImportedKeys().contains(key)) {
wallet.removeKey(key);
}
wallet.addKey(key);
wallet.saveToFile(new File(Config.walletFile));
/*
try {
importTransactionsFromAddress(address);
} catch (Exception e) {
throw new Exception(e.getMessage());
}
*/
return address;
}
public String importPrivateKey(String privateKey) throws Exception {
DumpedPrivateKey dumpedPrivateKey;
String address = "";
ECKey key = null;
try {
dumpedPrivateKey = new DumpedPrivateKey(params, privateKey);
key = dumpedPrivateKey.getKey();
return importPrivateKey(key);
} catch (AddressFormatException e) {
throw new Exception("Blocks.importPrivateKey() failed:" + e.getMessage());
}
}
/*
public void importTransactionsFromAddress(String address) throws Exception {
logger.info("Importing transactions");
try {
wallet.addWatchedAddress(new Address(params, address));
} catch (AddressFormatException e) {
}
List<Map.Entry<String,String>> txsInfo = Util.getTransactions(address);
BigInteger balance = BigInteger.ZERO;
BigInteger balanceSent = BigInteger.ZERO;
BigInteger balanceReceived = BigInteger.ZERO;
Integer transactionCount = 0;
for (Map.Entry<String,String> txHashBlockHash : txsInfo) {
String txHash = txHashBlockHash.getKey();
String blockHash = txHashBlockHash.getValue();
try {
Block block = peerGroup.getDownloadPeer().getBlock(new Sha256Hash(blockHash)).get();
List<Transaction> txs = block.getTransactions();
for (Transaction tx : txs) {
if (tx.getHashreplacedtring().equals(txHash)){// && wallet.isPendingTransactionRelevant(tx)) {
transactionCount ++;
wallet.receivePending(tx, peerGroup.getDownloadPeer().downloadDependencies(tx).get());
balanceReceived = balanceReceived.add(tx.getValueSentToMe(wallet));
balanceSent = balanceSent.add(tx.getValueSentFromMe(wallet));
balance = balance.add(tx.getValueSentToMe(wallet));
balance = balance.subtract(tx.getValueSentFromMe(wallet));
}
}
} catch (InterruptedException e) {
throw new Exception(e.getMessage());
} catch (ExecutionException e) {
throw new Exception(e.getMessage());
}
}
logger.info("Address balance: "+balance);
}
*/
public Transaction transaction(OdinTransctionData odin_tx_data) throws Exception {
byte[] data = Util.hexStringToBytes(odin_tx_data.data_hex);
return transaction(odin_tx_data.source, odin_tx_data.destination, odin_tx_data.amount_satoshi, odin_tx_data.fee_satoshi, odin_tx_data.mark_hex, data);
}
public Transaction transaction(String source, String destination, BigInteger amount_satoshi, BigInteger fee, String markPubkeyHexStr, byte[] data) throws Exception {
Transaction tx = new Transaction(params);
if (!destination.equals("") && amount_satoshi.compareTo(BigInteger.valueOf(Config.dustSize)) < 0) {
tx.verify();
return tx;
}
List<Byte> dataArrayList = (data == null) ? new ArrayList<Byte>() : Util.toByteArrayList(data);
int odin_data_length = dataArrayList.size();
boolean isOdinTransaction = odin_data_length > 0;
BigInteger totalOutput = fee;
BigInteger totalInput = BigInteger.ZERO;
try {
if (!destination.equals("") && amount_satoshi.compareTo(BigInteger.ZERO) > 0) {
totalOutput = totalOutput.add(amount_satoshi);
tx.addOutput(Coin.valueOf(amount_satoshi.longValue()), new Address(params, destination));
}
} catch (AddressFormatException e) {
throw new Exception("Failed to generate output for " + destination);
}
ECKey source_key = null;
if (bRemoteWalletMode) {
// source_key=new ECKey(null,Util.hexStringToBytes(source_pubkey_hex));
} else {
for (ECKey key : wallet.getImportedKeys()) {
try {
if (key.toAddress(params).equals(new Address(params, source))) {
source_key = key;
break;
}
} catch (AddressFormatException e) {
}
}
}
if (null == source_key)
return null;
// 组织多重交易来嵌入所需存放的数据
if (isOdinTransaction) {
int max_tx_num = Config.MAX_MULTISIG_TX_NUM;
int max_multisig_n = Config.MAX_N;
int from = 0;
for (int tt = 0; tt == 0 || (tt < max_tx_num && from < odin_data_length - Config.MAX_OP_RETURN_LENGTH); tt++) {
List<ECKey> keys = new ArrayList<ECKey>();
keys.add(source_key);
if (tt == 0) {
// 第一条多重交易的第二个公钥固定为指定特征公钥
keys.add(new ECKey(null, Util.hexStringToBytes(markPubkeyHexStr)));
}
for (int mm = keys.size(); mm < max_multisig_n && ((tt == 0 && from < odin_data_length) || (tt > 0 && from < odin_data_length - Config.MAX_OP_RETURN_LENGTH)); mm++, from += Config.PPK_PUBKEY_EMBED_DATA_MAX_LENGTH) {
int embed_data_length = Math.min(Config.PPK_PUBKEY_EMBED_DATA_MAX_LENGTH, odin_data_length - from);
List<Byte> chunk = new ArrayList<Byte>(dataArrayList.subList(from, from + embed_data_length));
byte[] tmp_pub_key = Util.generateValidPubkey(Util.toByteArray(chunk));
if (tmp_pub_key == null) {
throw new Exception("Unable to generate valid pubkey for embedding data.Please change your request contents!");
}
keys.add(new ECKey(null, tmp_pub_key));
}
Script script = ScriptBuilder.createMultiSigOutputScript(1, keys);
tx.addOutput(Coin.valueOf(BigInteger.valueOf(Config.dustSize).longValue()), script);
totalOutput = totalOutput.add(BigInteger.valueOf(Config.dustSize));
}
// 使用op_return对应的备注脚本空间来嵌入剩余ODIN数据
int last_data_length = odin_data_length - from;
if (last_data_length > Config.MAX_OP_RETURN_LENGTH) {
throw new Exception("Too big embed data.(Should be less than " + Config.MAX_ODIN_DATA_LENGTH + " bytes)");
} else if (last_data_length > 0) {
List<Byte> chunk = new ArrayList<Byte>(dataArrayList.subList(from, odin_data_length));
chunk.add(0, (byte) last_data_length);
chunk.add(0, (byte) 0x6a);
Script script = new Script(Util.toByteArray(chunk));
tx.addOutput(Coin.valueOf(BigInteger.valueOf(0).longValue()), script);
}
}
List<UnspentOutput> unspents = Util.getUnspents(source, isOdinTransaction);
List<Script> inputScripts = new ArrayList<Script>();
Boolean hadOneRegularInput = false;
Integer usedUnspents = 0;
for (UnspentOutput unspent : unspents) {
String txHash = unspent.txid;
// byte[] scriptBytes = Hex.decode(unspent.scriptPubKeyHex.getBytes(Charset.forName(Config.BINARY_DATA_CHARSET)));
byte[] scriptBytes = Util.hexStringToBytes(unspent.scriptPubKeyHex);
Script script = new Script(scriptBytes);
// if it's sent to an address and we don't yet have enough inputs or we don't yet have at least one regular input, or if it's sent to a multisig
// in other words, we sweep up any unused multisig inputs with every transaction
try {
if (// Avoid TX data exception,20200307
unspent.amt_satoshi.compareTo(BigInteger.valueOf(0)) > 0 && ((script.isSentToAddress() && (totalOutput.compareTo(totalInput) > 0 || !hadOneRegularInput)) || (script.isSentToMultiSig() && ((!isOdinTransaction && totalOutput.compareTo(totalInput) > 0) || (isOdinTransaction && usedUnspents < 2 && !hadOneRegularInput) || (isOdinTransaction && usedUnspents < 3 && hadOneRegularInput))))) {
if (script.isSentToAddress()) {
hadOneRegularInput = true;
}
/*
//严格检查输入是否符合比特币协议标准要求 ,待生效
boolean scriptIsStandard=true;
if (script.isSentToMultiSig()) {
System.out.println("Check UTXO script:"+script.toString());
try{
List<ECKey> tmp_pubkeys=script.getPubKeys();
for (ECKey tmp_key : tmp_pubkeys){
byte[] tmp_byte_array= tmp_key.getPubKey();
if( tmp_byte_array[0]!=2 && tmp_byte_array[0]!=3 && tmp_byte_array[0]!=4 ){
//not standard pubkey
scriptIsStandard=false;
System.out.println("Ignore an UTXO for invalid script:"+script.toString());
break;
}
}
}catch(Exception e){
scriptIsStandard=false;
System.out.println("Found invalid script :"+script.toString());
}
}
if(!scriptIsStandard)
continue;
*/
Sha256Hash sha256Hash = new Sha256Hash(txHash);
TransactionOutPoint txOutPt = new TransactionOutPoint(params, unspent.vout, sha256Hash);
System.out.println("Spending " + sha256Hash + " " + unspent.vout);
totalInput = totalInput.add(unspent.amt_satoshi);
TransactionInput input = new TransactionInput(params, tx, new byte[] {}, txOutPt);
tx.addInput(input);
inputScripts.add(script);
// 20200307
usedUnspents++;
}
if (isOdinTransaction && usedUnspents >= 3 && totalInput.compareTo(totalOutput) >= 0) {
// use max 3 unspents to lower odin transaction size if possible
break;
}
} catch (Exception e) {
logger.error("Error during transaction creation: " + e.toString());
e.printStackTrace();
throw new Exception("Error during transaction creation: " + e.toString());
}
}
if (!hadOneRegularInput && odin_data_length > 0) {
throw new Exception(Language.getLangLabel("Not enough standard unspent outputs to cover odin transaction."));
}
if (totalInput.compareTo(totalOutput) < 0) {
logger.info("Not enough inputs. Output: " + totalOutput.toString() + ", input: " + totalInput.toString());
throw new Exception("Not enough BTC to cover transaction of " + String.format("%.8f", totalOutput.doubleValue() / Config.btc_unit) + " BTC. Usable amount is " + String.format("%.8f", totalInput.doubleValue() / Config.btc_unit));
}
BigInteger totalChange = totalInput.subtract(totalOutput);
try {
if (totalChange.compareTo(BigInteger.ZERO) > 0) {
tx.addOutput(Coin.valueOf(totalChange.longValue()), new Address(params, source));
}
} catch (AddressFormatException e) {
throw new Exception("Failed to generate charge output for " + source);
}
if (!bRemoteWalletMode) {
// sign inputs while use local wallet
for (int i = 0; i < tx.getInputs().size(); i++) {
Script script = inputScripts.get(i);
TransactionInput input = tx.getInput(i);
TransactionSignature txSig = tx.calculateSignature(i, source_key, script, SigHash.ALL, false);
if (script.isSentToAddress()) {
input.setScriptSig(ScriptBuilder.createInputScript(txSig, source_key));
} else if (script.isSentToMultiSig()) {
// input.setScriptSig(ScriptBuilder.createMultiSigInputScript(txSig));
ScriptBuilder builder = new ScriptBuilder();
builder.smallNum(0);
builder.data(txSig.encodeToBitcoin());
input.setScriptSig(builder.build());
}
}
tx.verify();
}
// Util.exportTextToFile(tx.toString(), "resources/db/last_transaction.log");
// System.exit(0);
return tx;
}
public Boolean sendTransaction(String source, Transaction tx) throws Exception {
try {
System.out.println("Try to send (" + source + ") transaction:");
System.out.println(tx.toString());
// for debug
/*
byte[] rawTxBytes = tx.bitcoinSerialize();
System.out.println("The Raw TX:");
for(int kk=0;kk<rawTxBytes.length;kk++){
System.out.printf("%02x",rawTxBytes[kk]);
}
*/
Blocks blocks = Blocks.getInstance();
TransactionBroadcast future = null;
try {
logger.info("Broadcasting transaction: " + tx.getHashreplacedtring());
future = peerGroup.broadcastTransaction(tx);
if (source != null)
cacheLastUnspentTransaction(source, tx);
} catch (Exception e) {
throw new Exception(Language.getLangLabel("Transaction timed out. Please try again.") + "[2]");
}
logger.info("Importing transaction (replacedigning block number -1)");
blocks.importPPkTransaction(tx, null, null, null);
return true;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
// 缓存指定地址的最新未花费交易,优先作为下一次交易输入使用,20181220
public static boolean cacheLastUnspentTransaction(String source, Transaction tx) {
List<UnspentOutput> lastUnspents = new ArrayList<UnspentOutput>();
try {
int vout = 0;
for (TransactionOutput out : tx.getOutputs()) {
Script script = out.getScriptPubKey();
List<ScriptChunk> asm = script.getChunks();
int asm_num = asm.size();
BigInteger amount_satoshi = BigInteger.valueOf(out.getValue().getValue());
// System.out.println("vout:"+vout+"\nasm:"+asm.toString()+"\nbtcAmount:"+amount_satoshi);
// 如果金额大于0,且是多重签名输出或者是包含指定地址的普通输出,则是有效的UTXO
boolean isValidOut = false;
if (amount_satoshi != BigInteger.ZERO) {
if (asm_num >= 5 && asm.get(0).equalsOpCode(0x51) && asm.get(asm_num - 2).isOpCode() && asm.get(asm_num - 1).equalsOpCode(0xAE)) {
// MULTISIG
isValidOut = true;
} else {
Address dest_address = script.getToAddress(params);
String destination = dest_address.toString();
if (source.equalsIgnoreCase(destination))
isValidOut = true;
}
}
if (isValidOut) {
UnspentOutput tempUnspentObj = new UnspentOutput();
tempUnspentObj.amt_satoshi = amount_satoshi;
tempUnspentObj.txid = tx.getHashreplacedtring();
tempUnspentObj.vout = vout;
tempUnspentObj.scriptPubKeyHex = Util.bytesToHexString(script.getProgram());
System.out.println("Cache[" + source + "]'s utxo: " + tempUnspentObj.toString());
if (tempUnspentObj.scriptPubKeyHex.length() > 0) {
lastUnspents.add(tempUnspentObj);
}
}
vout++;
}
cachedLastUnspentList.put(source, lastUnspents);
return true;
} catch (Exception e) {
return false;
}
}
// 获取缓存的最近一次指定地址的未花费交易输出
public static List<UnspentOutput> getCachedLastUnspents(String source) {
try {
return (List<UnspentOutput>) cachedLastUnspentList.get(source);
} catch (Exception e) {
}
return null;
}
// 清空缓存的未花费交易输出
public static boolean clearCachedLastUnspents() {
try {
cachedLastUnspentList.clear();
return true;
} catch (Exception e) {
return false;
}
}
public boolean importPPkTransaction(Transaction tx, Integer txSnInBlock, Block block, Integer blockHeight) {
BigInteger fee = BigInteger.ZERO;
String destination = "";
BigInteger amount_satoshi = BigInteger.ZERO;
List<Byte> dataArrayList = new ArrayList<Byte>();
byte[] data = null;
String source = "";
Database db = Database.getInstance();
boolean matched_ppk_odin_prefix = false;
for (TransactionOutput out : tx.getOutputs()) {
try {
Script script = out.getScriptPubKey();
List<ScriptChunk> asm = script.getChunks();
int asm_num = asm.size();
boolean isFirstMultiSigTx = false;
if (asm_num >= 5 && asm.get(0).equalsOpCode(0x51) && asm.get(asm_num - 2).isOpCode() && asm.get(asm_num - 1).equalsOpCode(0xAE)) {
// MULTISIG
int multisig_n = asm.get(asm_num - 2).decodeOpN();
if (!matched_ppk_odin_prefix) {
if (asm.get(2).data.length == Config.PPK_ODIN_MARK_PUBKEY_HEX.length() / 2) {
String tmp_pubkey_hex = Util.bytesToHexString(asm.get(2).data);
if (Config.PPK_ODIN_MARK_PUBKEY_HEX.equals(tmp_pubkey_hex)) {
matched_ppk_odin_prefix = true;
isFirstMultiSigTx = true;
}
}
}
if (matched_ppk_odin_prefix) {
int from = isFirstMultiSigTx ? 3 : 2;
for (; from < multisig_n + 1; from++) {
byte[] tmp_data = asm.get(from).data;
byte embed_data_len = tmp_data[1];
if (embed_data_len > 0 && embed_data_len <= tmp_data.length - 2)
for (byte i = 0; i < embed_data_len; i++) dataArrayList.add(tmp_data[2 + i]);
}
}
} else if (matched_ppk_odin_prefix && asm.get(0).equalsOpCode(0x6A)) {
// OP_RETURN
// System.out.println("asm_num="+asm_num+" "+asm.toString());
for (int i = 0; i < asm.get(1).data.length; i++) dataArrayList.add(asm.get(1).data[i]);
}
if (destination.equals("") && amount_satoshi == BigInteger.ZERO && dataArrayList.size() == 0) {
Address address = script.getToAddress(params);
destination = address.toString();
amount_satoshi = BigInteger.valueOf(out.getValue().getValue());
}
} catch (ScriptException e) {
}
}
if (dataArrayList.size() > 0) {
// 截取特征前缀后的有效字节数据
data = Util.toByteArray(dataArrayList);
} else {
return false;
}
for (TransactionInput in : tx.getInputs()) {
if (in.isCoinBase())
return false;
try {
Script script = in.getScriptSig();
Address address = script.getFromAddress(params);
if (address != null && source.equals("")) {
String tmp_str = address.toString();
if (tmp_str.startsWith("1")) {
source = tmp_str;
break;
}
}
} catch (ScriptException e) {
}
}
logger.info("Incoming PPk transaction from " + source + " to " + destination + " (" + tx.getHashreplacedtring() + ")");
if (!source.equals("") && dataArrayList.size() > 0) {
String str_data_hex = "";
str_data_hex = Util.bytesToHexString(data);
// logger.info("PPk str_data_hex : ["+str_data_hex+"] length="+str_data_hex.length());
db.executeUpdate("delete from transactions where tx_hash='" + tx.getHashreplacedtring() + "' and block_index<0");
ResultSet rs = db.executeQuery("select * from transactions where tx_hash='" + tx.getHashreplacedtring() + "';");
try {
if (!rs.next()) {
if (block != null) {
Integer newTxIndex = Util.getLastTxIndex() + 1;
PreparedStatement ps = db.connection.prepareStatement("INSERT INTO transactions(tx_index, tx_hash, block_index, block_time, source, destination, btc_amount, fee, data,prefix_type,sn_in_block) VALUES('" + newTxIndex + "','" + tx.getHashreplacedtring() + "','" + blockHeight + "','" + block.getTimeSeconds() + "','" + source + "','" + destination + "','" + amount_satoshi.toString() + "','" + fee.toString() + "',?,1,'" + txSnInBlock.toString() + "')");
ps.setString(1, str_data_hex);
ps.execute();
} else {
PreparedStatement ps = db.connection.prepareStatement("INSERT INTO transactions(tx_index, tx_hash, block_index, block_time, source, destination, btc_amount, fee, data,prefix_type,sn_in_block) VALUES('" + (Util.getLastTxIndex() + 1) + "','" + tx.getHashreplacedtring() + "','-1','" + Util.getNowTimestamp() + "','" + source + "','" + destination + "','" + amount_satoshi.toString() + "','" + fee.toString() + "',?,1,-1)");
ps.setString(1, str_data_hex);
ps.execute();
}
}
} catch (SQLException e) {
logger.error(e.toString());
}
}
return true;
}
public static Byte getPPkMessageTypeFromTransaction(byte[] odin_data) {
if (odin_data == null || odin_data.length == 0)
return null;
else
return odin_data[0];
}
public static List<Byte> getPPkMessageFromTransaction(byte[] odin_data) {
List<Byte> message = null;
try {
List<Byte> dataArrayList = Util.toByteArrayList(odin_data);
message = dataArrayList.subList(1, dataArrayList.size());
return message;
} catch (Exception e) {
}
return message;
}
// 打开远程钱包模式 2019-01-15
public static void enableRemoteWalletMode() {
bRemoteWalletMode = true;
}
public static boolean isRemoteWalletMode() {
return bRemoteWalletMode;
}
// 获取当前地址列表 2019-01-15
public List<String> getAddresses() {
List<String> addresses = new ArrayList<String>();
if (!bRemoteWalletMode) {
// 本地钱包模式
List<ECKey> keys = wallet.getImportedKeys();
for (ECKey key : keys) {
addresses.add(key.toAddress(params).toString());
}
}
return addresses;
}
}
19
Source : BitcoinTransactionAdapter.java
with Apache License 2.0
from ICOnator
with Apache License 2.0
from ICOnator
/**
* Bitcoin-sepcific transaction clreplaced.
*/
public clreplaced BitcoinTransactionAdapter extends BaseTransactionAdapter {
private static final String BLOCKCHAIN_INFO_LINK = "https://blockchain.info/tx/";
private TransactionOutput bitcoinjTxOutput;
private NetworkParameters bitcoinjNetworkParameters;
private BlockStore bitcoinjBlockStore;
/**
* Remembering block height after first time getter is called. This is to save
* computation time since without this the bitcoinj block store would be
* searched every time the block height is requested.
*/
private Long blockHeight;
/**
* Remembering block time after first time getter is called. This is to save
* computation time since without this the bitcoinj block store would be
* searched every time the block time is requested.
*/
private Date blockTime;
public BitcoinTransactionAdapter(@NotNull TransactionOutput bitcoinjTxOutput, @NotNull NetworkParameters bitcoinjNetworkParameters, @NotNull BlockStore bitcoinjBlockStore, @NotNull InvestorService investorService) {
super(investorService);
this.bitcoinjTxOutput = bitcoinjTxOutput;
this.bitcoinjNetworkParameters = bitcoinjNetworkParameters;
this.bitcoinjBlockStore = bitcoinjBlockStore;
this.blockHeight = null;
this.blockTime = null;
}
@Override
public String getTransactionId() throws MissingTransactionInformationException {
try {
Transaction transaction = bitcoinjTxOutput.getParentTransaction();
return transaction.getHashreplacedtring() + "_" + String.valueOf(bitcoinjTxOutput.getIndex());
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch transaction id.", e);
}
}
@Override
public BigInteger getTransactionValue() throws MissingTransactionInformationException {
try {
return BigInteger.valueOf(bitcoinjTxOutput.getValue().getValue());
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch transaction value.", e);
}
}
@Override
public String getReceivingAddress() throws MissingTransactionInformationException {
try {
return bitcoinjTxOutput.getAddressFromP2PKHScript(this.bitcoinjNetworkParameters).toBase58();
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch receiving address.", e);
}
}
@Override
public Investor getreplacedociatedInvestor() throws MissingTransactionInformationException {
try {
return getInvestorService().getInvestorByBitcoinAddress(getReceivingAddress());
} catch (MissingTransactionInformationException e) {
throw e;
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch replacedociated investor.", e);
}
}
@Override
public Long getBlockHeight() throws MissingTransactionInformationException {
if (this.blockHeight == null) {
try {
this.blockHeight = bitcoinjTxOutput.getParentTransaction().getAppearsInHashes().keySet().stream().map((blockHash) -> {
try {
return bitcoinjBlockStore.get(blockHash);
} catch (BlockStoreException e) {
// This can happen if the transaction was seen in a side-chain
return null;
}
}).filter(Objects::nonNull).map(StoredBlock::getHeight).min(Integer::compareTo).get().longValue();
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch block height.", e);
}
}
return this.blockHeight;
}
@Override
public CurrencyType getCurrencyType() {
return CurrencyType.BTC;
}
/*
* Retrieve the timestamp from the first block that the transaction was seen in.
*/
@Override
public Date getBlockTime() throws MissingTransactionInformationException {
if (this.blockTime == null) {
try {
this.blockTime = bitcoinjTxOutput.getParentTransaction().getAppearsInHashes().keySet().stream().map((blockHash) -> {
try {
return bitcoinjBlockStore.get(blockHash);
} catch (BlockStoreException e) {
// This can happen if the transaction was seen in a side-chain
return null;
}
}).filter(Objects::nonNull).map(StoredBlock::getHeader).map(Block::getTime).min(Date::compareTo).get();
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch block time.", e);
}
}
return this.blockTime;
}
@Override
public String getTransactionUrl() throws MissingTransactionInformationException {
try {
return BLOCKCHAIN_INFO_LINK + bitcoinjTxOutput.getParentTransaction().getHashreplacedtring();
} catch (Exception e) {
throw new MissingTransactionInformationException("Couldn't fetch transaction Id.", e);
}
}
/**
* @return the bitcoinj transaction object.
*/
public Transaction getBitcoinjTransaction() {
return bitcoinjTxOutput.getParentTransaction();
}
}
19
Source : BlockChain.java
with Apache License 2.0
from btcontract
with Apache License 2.0
from btcontract
// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
* A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
* choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
* all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
*/
public clreplaced BlockChain extends AbstractBlockChain {
/**
* Keeps a map of block hashes to StoredBlocks.
*/
protected final BlockStore blockStore;
/**
* <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
* one from scratch, or you can deserialize a saved wallet from disk using
* {@link Wallet#loadFromFile(File, WalletExtension...)}</p>
*
* <p>For the store, you should use {@link SPVBlockStore} or you could also try a
* {@link MemoryBlockStore} if you want to hold all headers in RAM and don't care about
* disk serialization (this is rare).</p>
*/
public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
/**
* See {@link #BlockChain(Context, Wallet, BlockStore)}}
*/
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallet, blockStore);
}
/**
* Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
* and receiving coins but rather, just want to explore the network data structures.
*/
public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
}
/**
* See {@link #BlockChain(Context, BlockStore)}
*/
public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
this(params, new ArrayList<Wallet>(), blockStore);
}
/**
* Constructs a BlockChain connected to the given list of listeners and a store.
*/
public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
super(params, wallets, blockStore);
this.blockStore = blockStore;
}
/**
* See {@link #BlockChain(Context, List, BlockStore)}
*/
public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallets, blockStore);
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected void rollbackBlockStore(int height) throws BlockStoreException {
lock.lock();
try {
int currentHeight = getBestChainHeight();
checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
if (height == currentHeight)
// nothing to do
return;
// Look for the block we want to be the new chain head
StoredBlock newChainHead = blockStore.getChainHead();
while (newChainHead.getHeight() > height) {
newChainHead = newChainHead.getPrev(blockStore);
if (newChainHead == null)
throw new BlockStoreException("Unreachable height");
}
// Modify store directly
blockStore.put(newChainHead);
this.setChainHead(newChainHead);
} finally {
lock.unlock();
}
}
@Override
protected boolean shouldVerifyTransactions() {
return false;
}
@Override
protected TransactionOutputChanges connectTransactions(int height, Block block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void disconnectTransactions(StoredBlock block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
blockStore.setChainHead(chainHead);
}
@Override
protected void notSettingChainHead() throws BlockStoreException {
// We don't use DB transactions here, so we don't need to do anything
}
@Override
protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
return blockStore.get(hash);
}
@Override
public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
boolean success = super.add(block);
if (success) {
trackFilteredTransactions(block.getTransactionCount());
}
return success;
}
}
19
Source : FetchTransactions.java
with GNU General Public License v3.0
from Blockstream
with GNU General Public License v3.0
from Blockstream
public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
System.out.println("Connecting to node");
final NetworkParameters params = TestNet3Params.get();
BlockStore blockStore = new MemoryBlockStore(params);
BlockChain chain = new BlockChain(params, blockStore);
PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.start();
peerGroup.addAddress(new PeerAddress(params, InetAddress.getLocalHost()));
peerGroup.waitForPeers(1).get();
Peer peer = peerGroup.getConnectedPeers().get(0);
Sha256Hash txHash = Sha256Hash.wrap(args[0]);
ListenableFuture<Transaction> future = peer.getPeerMempoolTransaction(txHash);
System.out.println("Waiting for node to send us the requested transaction: " + txHash);
Transaction tx = future.get();
System.out.println(tx);
System.out.println("Waiting for node to send us the dependencies ...");
List<Transaction> deps = peer.downloadDependencies(tx).get();
for (Transaction dep : deps) {
System.out.println("Got dependency " + dep.getHashreplacedtring());
}
System.out.println("Done.");
peerGroup.stop();
}
19
Source : FetchBlock.java
with GNU General Public License v3.0
from Blockstream
with GNU General Public License v3.0
from Blockstream
public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
System.out.println("Connecting to node");
final NetworkParameters params = TestNet3Params.get();
BlockStore blockStore = new MemoryBlockStore(params);
BlockChain chain = new BlockChain(params, blockStore);
PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.start();
PeerAddress addr = new PeerAddress(params, InetAddress.getLocalHost());
peerGroup.addAddress(addr);
peerGroup.waitForPeers(1).get();
Peer peer = peerGroup.getConnectedPeers().get(0);
Sha256Hash blockHash = Sha256Hash.wrap(args[0]);
Future<Block> future = peer.getBlock(blockHash);
System.out.println("Waiting for node to send us the requested block: " + blockHash);
Block block = future.get();
System.out.println(block);
peerGroup.stopAsync();
}
19
Source : SPV.java
with GNU General Public License v3.0
from Blockstream
with GNU General Public License v3.0
from Blockstream
public clreplaced SPV {
private final static String TAG = SPV.clreplaced.getSimpleName();
private final Map<TransactionOutPoint, Coin> mCountedUtxoValues = new HashMap<>();
static clreplaced AccountInfo extends Pair<Integer, Integer> {
AccountInfo(final Integer subAccount, final Integer pointer) {
super(subAccount, pointer);
}
Integer getSubAccount() {
return first;
}
public Integer getPointer() {
return second;
}
}
public NetworkParameters getNetworkParameters(NetworkData network) {
switch(network.getNetwork() == null ? "mainnet" : network.getNetwork()) {
case "mainnet":
return MainNetParams.get();
case "testnet":
return TestNet3Params.get();
case "regtest":
case "localtest":
return RegTestParams.get();
default:
return null;
}
}
// We use a single threaded executor to serialise config changes
// without forcing callers to block.
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final SparseArray<Coin> mVerifiedCoinBalances = new SparseArray<>();
private final Map<Sha256Hash, List<Integer>> mUnspentOutpoints = new HashMap<>();
private final Map<TransactionOutPoint, AccountInfo> mUnspentDetails = new HashMap<>();
private GaService mService;
private BlockStore mBlockStore;
private BlockChain mBlockChain;
private PeerGroup mPeerGroup;
private final PeerFilterProvider mPeerFilter = new PeerFilterProvider(this);
private NotificationManager mNotifyManager;
private Builder mNotificationBuilder;
private final static int mNotificationId = 1;
private int mNetWorkType;
private final Object mStateLock = new Object();
public final SettableFuture<Void> onServiceAttached = SettableFuture.create();
private List<SubaccountData> mSubaccounts = new ArrayList<>();
public SPV() {
mNetWorkType = ConnectivityManager.TYPE_DUMMY;
}
public void startService(final Context ctx) throws Exception {
GaService.createNotificationChannel(ctx);
// Provide bitcoinj with Mnemonics. These are used if we need to create a fake
// wallet during SPV_SYNCRONIZATION syncing to prevent an exception.
final ArrayList<String> words = new ArrayList<>(Wally.BIP39_WORDLIST_LEN);
MnemonicHelper.initWordList(words, null);
MnemonicCode.INSTANCE = new MnemonicCode(words, null);
Log.d(TAG, "onCreate: binding service");
final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName clreplacedName, final IBinder service) {
Log.d(TAG, "onServiceConnected: dispatching onServiceAttached callbacks");
mService = ((GaService.GaBinder) service).getService();
mService.onBound(ctx);
startAsync();
onServiceAttached.set(null);
}
@Override
public void onServiceDisconnected(final ComponentName name) {
Log.d(TAG, "onServiceDisconnected: dispatching onServiceAttached exception");
onServiceAttached.setException(new GAException(name.toString()));
}
};
final Intent intent = new Intent(ctx, GaService.clreplaced);
ctx.bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
public GaService getService() {
return mService;
}
private <T> String Var(final String name, final T value) {
return name + " => " + value.toString() + ' ';
}
public boolean isEnabled() {
final NetworkData networkData = mService.getNetwork();
return !getSession().isWatchOnly() && cfg().getBoolean(PrefKeys.SPV_ENABLED, false) && !networkData.getLiquid();
}
public void setEnabledAsync(final boolean enabled) {
mExecutor.execute(() -> setEnabled(enabled));
}
private void setEnabled(final boolean enabled) {
synchronized (mStateLock) {
final boolean current = isEnabled();
Log.d(TAG, "setEnabled: " + Var("enabled", enabled) + Var("current", current));
if (enabled == current)
return;
cfgEdit().putBoolean(PrefKeys.SPV_ENABLED, enabled).apply();
// FIXME: Should we delete unspent here?
reset(false, /* deleteAllData */
false);
}
}
public boolean isSyncOnMobileEnabled() {
return cfg().getBoolean(PrefKeys.SPV_MOBILE_SYNC_ENABLED, false);
}
public void setSyncOnMobileEnabledAsync(final boolean enabled) {
mExecutor.execute(() -> setSyncOnMobileEnabled(enabled));
}
private void setSyncOnMobileEnabled(final boolean enabled) {
synchronized (mStateLock) {
final boolean current = isSyncOnMobileEnabled();
final boolean currentlyEnabled = isEnabled();
Log.d(TAG, "setSyncOnMobileEnabled: " + Var("enabled", enabled) + Var("current", current));
if (enabled == current)
// Setting hasn't changed
return;
cfgEdit().putBoolean(PrefKeys.SPV_MOBILE_SYNC_ENABLED, enabled).apply();
if (getNetworkType() != ConnectivityManager.TYPE_MOBILE)
// Any change doesn't affect us since we aren't currently on mobile
return;
if (enabled && currentlyEnabled) {
if (mPeerGroup == null)
setup();
startSync();
} else
stopSync();
}
}
public String getTrustedPeers() {
return cfg().getString(PrefKeys.TRUSTED_ADDRESS, cfg().getString("trusted_peer", "")).trim();
}
public void setTrustedPeersAsync(final String peers) {
mExecutor.execute(() -> setTrustedPeers(peers));
}
private void setTrustedPeers(final String peers) {
synchronized (mStateLock) {
// FIXME: We should check if the peers differ here, instead of in the caller
final String current = getTrustedPeers();
Log.d(TAG, "setTrustedPeers: " + Var("peers", peers) + Var("current", current));
cfgEdit().putString(PrefKeys.TRUSTED_ADDRESS, peers).apply();
reset(false, /* deleteAllData */
false);
}
}
public PeerGroup getPeerGroup() {
return mPeerGroup;
}
public boolean isVerified(final String txHash) {
return cfg().getBoolean(PrefKeys.VERIFIED_HASH_ + txHash, false);
}
public void startAsync() {
mExecutor.execute(() -> start());
}
private void start() {
synchronized (mStateLock) {
Log.d(TAG, "start");
reset(false, /* deleteAllData */
true);
}
}
public Coin getVerifiedBalance(final int subAccount) {
return mVerifiedCoinBalances.get(subAccount);
}
private boolean isUnspentOutpoint(final Sha256Hash txHash) {
return mUnspentOutpoints.containsKey(txHash);
}
private TransactionOutPoint createOutPoint(final Integer index, final Sha256Hash txHash, final NetworkParameters params) {
return new TransactionOutPoint(params, index, txHash);
}
void updateUnspentOutputs() {
final NetworkData networkData = mService.getNetwork();
final boolean currentlyEnabled = isEnabled();
Log.d(TAG, "updateUnspentOutputs: " + Var("currentlyEnabled", currentlyEnabled));
final List<TransactionData> utxos = new ArrayList<>();
for (final SubaccountData subaccountData : mSubaccounts) {
try {
List<TransactionData> transactionDataList = getUtxos(subaccountData.getPointer());
utxos.addAll(transactionDataList);
} catch (final Exception e) {
e.printStackTrace();
}
}
final Set<TransactionOutPoint> newUtxos = new HashSet<>();
boolean recalculateBloom = false;
Log.d(TAG, Var("number of utxos", utxos.size()));
for (final TransactionData utxo : utxos) {
final Integer prevIndex = utxo.getPtIdx();
final Integer subaccount = utxo.getSubaccount();
final Integer pointer = utxo.getPointer();
final Sha256Hash txHash = utxo.getTxhashreplacedha256Hash();
if (isVerified(txHash.toString())) {
addToUtxo(txHash, prevIndex, subaccount, pointer);
addUtxoToValues(txHash, false);
} else {
recalculateBloom = true;
addToBloomFilter(utxo.getBlockHeight(), txHash, prevIndex, subaccount, pointer);
}
newUtxos.add(createOutPoint(prevIndex, txHash, getNetworkParameters(networkData)));
}
// GA inception
mPeerGroup.setFastCatchupTimeSecs(1393545600);
final List<Integer> changedSubaccounts = new ArrayList<>();
for (final TransactionOutPoint oldUtxo : new HashSet<>(mCountedUtxoValues.keySet())) {
if (!newUtxos.contains(oldUtxo)) {
recalculateBloom = true;
final int subAccount = mUnspentDetails.get(oldUtxo).getSubAccount();
final Coin verifiedBalance = getVerifiedBalance(subAccount);
mVerifiedCoinBalances.put(subAccount, verifiedBalance.subtract(mCountedUtxoValues.get(oldUtxo)));
changedSubaccounts.add(subAccount);
mCountedUtxoValues.remove(oldUtxo);
mUnspentDetails.remove(oldUtxo);
mUnspentOutpoints.get(oldUtxo.getHash()).remove(((int) oldUtxo.getIndex()));
}
}
if (recalculateBloom && mPeerGroup != null)
mPeerGroup.recalculateFastCatchupAndFilter(PeerGroup.FilterRecalculateMode.SEND_IF_CHANGED);
}
private void addUtxoToValues(final Sha256Hash txHash, final boolean updateVerified) {
final String txHashHex = txHash.toString();
if (updateVerified)
cfgEdit().putBoolean(PrefKeys.VERIFIED_HASH_ + txHashHex, true).apply();
for (final SubaccountData subaccountData : mSubaccounts) {
final Integer pointer = subaccountData.getPointer();
try {
final List<TransactionData> txs = getTransactions(pointer);
// TODO called too many times
for (final TransactionData tx : txs) {
if (txHashHex.equals(tx.getTxhash())) {
// transactionDataObservable.fire();
}
}
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private List<SubaccountData> getSubAccounts() throws Exception {
final ObjectMapper mObjectMapper = new ObjectMapper();
final GDKTwoFactorCall call = getSession().getSubAccounts();
final ObjectNode accounts = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
final List<SubaccountData> subAccounts = mObjectMapper.readValue(mObjectMapper.treeAsTokens(accounts.get("subaccounts")), new TypeReference<List<SubaccountData>>() {
});
return subAccounts;
}
private List<TransactionData> getTransactions(final int subaccount) throws Exception {
final GDKTwoFactorCall call = getSession().getTransactionsRaw(subaccount, 0, 100);
final ObjectNode txListObject = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
final List<TransactionData> transactions = getSession().parseTransactions((ArrayNode) txListObject.get("transactions"));
return transactions;
}
private List<TransactionData> getUtxos(final int subaccount) throws Exception {
final GDKTwoFactorCall call = getSession().getUTXO(subaccount, 0);
final ObjectNode txListObject = call.resolve(null, new HardwareCodeResolver(null, getSession().getHWWallet()));
final JsonNode unspentOutputs = txListObject.get("unspent_outputs");
if (!unspentOutputs.has("btc"))
return new ArrayList<>();
final ArrayNode arrayNode = (ArrayNode) unspentOutputs.get("btc");
for (int i = 0; i < arrayNode.size(); i++) {
((ObjectNode) arrayNode.get(i)).remove("satoshi");
}
final List<TransactionData> transactions = getSession().parseTransactions((ArrayNode) arrayNode);
return transactions;
}
int getBloomFilterElementCount() {
final int count = mUnspentOutpoints.size();
return count == 0 ? 1 : count;
}
BloomFilter getBloomFilter(final int size, final double falsePositiveRate, final long nTweak) {
final Set<Sha256Hash> keys = mUnspentOutpoints.keySet();
Log.d(TAG, "getBloomFilter returning " + keys.size() + " items");
final BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak);
for (final Sha256Hash hash : keys) filter.insert(hash.getReversedBytes());
if (keys.isEmpty()) {
// Add a fake entry to avoid downloading blocks when filter is empty,
// as empty bloom filters are ignored by bitcoinj.
// FIXME: This results in a constant filter that peers can use to identify
// us as a Green client. That is undesirable.
filter.insert(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef });
}
return filter;
}
private void addToBloomFilter(final Integer blockHeight, final Sha256Hash txHash, final int prevIndex, final int subAccount, final int pointer) {
if (mBlockChain == null)
// can happen before login (onNewBlock)
return;
if (txHash != null)
addToUtxo(txHash, prevIndex, subAccount, pointer);
if (blockHeight != null && blockHeight <= mBlockChain.getBestChainHeight() && (txHash == null || !mUnspentOutpoints.containsKey(txHash))) {
// new tx or block notification with blockHeight <= current blockHeight means we might've [1]
// synced the height already while we haven't seen the tx, so we need to re-sync to be able
// to verify it.
// [1] - "might've" in case of txHash == null (block height notification),
// because it depends on the order of notifications
// - "must've" in case of txHash != null, because this means the tx arrived only after
// requesting it manually and we already had higher blockHeight
//
// We do it using the special case in bitcoinj for VM crashed because of
// a transaction received.
try {
Log.d(TAG, "Creating fake wallet for re-sync");
final NetworkData networkData = mService.getNetwork();
final Wallet fakeWallet = new Wallet(getNetworkParameters(networkData)) {
@Override
public int getLastBlockSeenHeight() {
return blockHeight - 1;
}
};
mBlockChain.addWallet(fakeWallet);
// can be removed, because the call above
mBlockChain.removeWallet(fakeWallet);
// should rollback already
} catch (final Exception e) {
e.printStackTrace();
Log.w(TAG, "fakeWallet exception: " + e.toString());
}
}
}
private void addToUtxo(final Sha256Hash txHash, final Integer prevIndex, final int subAccount, final int pointer) {
final NetworkData networkData = mService.getNetwork();
mUnspentDetails.put(createOutPoint(prevIndex, txHash, getNetworkParameters(networkData)), new AccountInfo(subAccount, pointer));
if (mUnspentOutpoints.get(txHash) == null)
mUnspentOutpoints.put(txHash, Lists.newArrayList(prevIndex));
else
mUnspentOutpoints.get(txHash).add(prevIndex);
}
public int getSPVHeight() {
if (mBlockChain != null && isEnabled())
return mBlockChain.getBestChainHeight();
return 0;
}
@TargetApi(Build.VERSION_CODES.M)
private PendingIntent getNotificationIntent() {
final Context service = getService();
final Intent intent = new Intent(service, TabbedMainActivity.clreplaced);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
return PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private void startSync() {
synchronized (mStateLock) {
final boolean isRunning = mPeerGroup != null && mPeerGroup.isRunning();
Log.d(TAG, "startSync: " + Var("isRunning", isRunning));
if (isRunning)
// Already started to sync
return;
if (mPeerGroup == null) {
// FIXME: Thi should not be possible but it happens in the wild.
Log.d(TAG, "startSync: mPeerGroup is null");
return;
}
if (mNotifyManager == null) {
mNotifyManager = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationBuilder = new NotificationCompat.Builder(mService, "spv_channel");
mNotificationBuilder.setContentreplacedle("SPV Synchronization").setSmallIcon(R.drawable.ic_home);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
mNotificationBuilder.setContentIntent(getNotificationIntent());
}
mNotificationBuilder.setContentText("Connecting to peer(s)...");
updateNotification(0, 0);
CB.after(mPeerGroup.startAsync(), new FutureCallback<Object>() {
@Override
public void onSuccess(final Object result) {
mPeerGroup.startBlockChainDownload(new DownloadProgressTracker() {
@Override
public void onChainDownloadStarted(final Peer peer, final int blocksLeft) {
// Note that this method may be called multiple times if syncing
// switches peers while downloading.
Log.d(TAG, "onChainDownloadStarted: " + Var("blocksLeft", blocksLeft));
super.onChainDownloadStarted(peer, blocksLeft);
}
@Override
public void onBlocksDownloaded(final Peer peer, final Block block, final FilteredBlock filteredBlock, final int blocksLeft) {
// Log.d(TAG, "onBlocksDownloaded: " + Var("blocksLeft", blocksLeft));
super.onBlocksDownloaded(peer, block, filteredBlock, blocksLeft);
}
@Override
protected void startDownload(final int blocks) {
Log.d(TAG, "startDownload");
updateNotification(100, 0);
}
@Override
protected void progress(final double percent, final int blocksSoFar, final Date date) {
// Log.d(TAG, "progress: " + Var("percent", percent));
String progressString = String.format("Block %d", getSPVHeight());
mNotificationBuilder.setContentText(progressString);
updateNotification(100, (int) percent);
}
@Override
protected void doneDownload() {
Log.d(TAG, "doneDownLoad");
mNotifyManager.cancel(mNotificationId);
}
});
}
@Override
public void onFailure(final Throwable t) {
t.printStackTrace();
mNotifyManager.cancel(mNotificationId);
}
});
}
}
private void updateNotification(final int total, final int soFar) {
mNotificationBuilder.setProgress(total, soFar, false);
mNotifyManager.notify(mNotificationId, mNotificationBuilder.build());
}
private PeerAddress getPeerAddress(final String address) throws URISyntaxException, UnknownHostException {
final URI uri = new URI("btc://" + address);
final String host = uri.getHost();
if (host == null)
throw new UnknownHostException(address);
final NetworkData networkData = mService.getNetwork();
final int port = uri.getPort() == -1 ? getNetworkParameters(networkData).getPort() : uri.getPort();
if (!isProxyEnabled() && !getTorEnabled())
return new PeerAddress(getNetworkParameters(networkData), InetAddress.getByName(host), port);
return new PeerAddress(getNetworkParameters(networkData), host, port) {
@Override
public InetSocketAddress toSocketAddress() {
return InetSocketAddress.createUnresolved(host, port);
}
@Override
public String toString() {
return String.format("%s:%s", host, port);
}
@Override
public int hashCode() {
return uri.hashCode();
}
};
}
private void addPeer(final String address, final NetworkParameters params) throws URISyntaxException {
try {
mPeerGroup.addAddress(getPeerAddress(address));
} catch (final UnknownHostException e) {
// FIXME: Should report this error: one the host here couldn't be resolved
e.printStackTrace();
}
}
private void setPingInterval(final long interval) {
synchronized (mStateLock) {
if (mPeerGroup != null)
mPeerGroup.setPingIntervalMsec(interval);
}
}
public void enablePingMonitoring() {
setPingInterval(PeerGroup.DEFAULT_PING_INTERVAL_MSEC);
}
public void disablePingMonitoring() {
setPingInterval(-1);
}
private void setup() {
synchronized (mStateLock) {
Log.d(TAG, "setup: " + Var("mPeerGroup != null", mPeerGroup != null));
if (mPeerGroup != null) {
// FIXME: Make sure this can never happen
Log.e(TAG, "Must stop and tear down SPV_SYNCRONIZATION before setting up again!");
return;
}
final NetworkData networkData = mService.getNetwork();
try {
Log.d(TAG, "Creating block store");
mBlockStore = new SPVBlockStore(getNetworkParameters(networkData), mService.getSPVChainFile());
// detect corruptions as early as possible
final StoredBlock storedBlock = mBlockStore.getChainHead();
if (storedBlock.getHeight() == 0 && networkData.isRegtest()) {
InputStream is = null;
try {
is = mService.getreplacedets().open(networkData.getMainnet() ? "production/checkpoints" : "btctestnet/checkpoints");
// mService.getLoginData().get("earliest_key_creation_time"); // TODO gdk
final int keyTime = 0;
CheckpointManager.checkpoint(getNetworkParameters(networkData), is, mBlockStore, keyTime);
Log.d(TAG, "checkpoints loaded");
} catch (final Exception e) {
// couldn't load checkpoints, log & skip
Log.d(TAG, "couldn't load checkpoints, log & skip");
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (final IOException e) {
// do nothing
}
}
}
Log.d(TAG, "Creating block chain");
org.bitcoinj.core.Context context = new org.bitcoinj.core.Context(getNetworkParameters(networkData));
mBlockChain = new BlockChain(context, mBlockStore);
mBlockChain.addTransactionReceivedListener(mTxListner);
System.setProperty("user.home", mService.getFilesDir().toString());
Log.d(TAG, "Creating peer group");
if (!isProxyEnabled() && !getTorEnabled()) {
mPeerGroup = new PeerGroup(getNetworkParameters(networkData), mBlockChain);
} else {
String proxyHost, proxyPort;
if (!isProxyEnabled()) {
if (getSession() == null || getSession().getTorSocks5() == null) {
throw new URISyntaxException("", "null session or TorSocks5");
}
final String fullUrl = getSession().getTorSocks5().replaceFirst("socks5://", "");
if (fullUrl.split(":").length != 2) {
throw new URISyntaxException(getSession().getTorSocks5(), "Invalid Tor SOCKS5 string");
}
proxyHost = fullUrl.split(":")[0];
proxyPort = fullUrl.split(":")[1];
} else {
proxyHost = getProxyHost();
proxyPort = getProxyPort();
}
final Socks5SocketFactory sf = new Socks5SocketFactory(proxyHost, proxyPort);
final BlockingClientManager bcm = new BlockingClientManager(sf);
bcm.setConnectTimeoutMillis(60000);
mPeerGroup = new PeerGroup(getNetworkParameters(networkData), mBlockChain, bcm);
mPeerGroup.setConnectTimeoutMillis(60000);
}
disablePingMonitoring();
mSubaccounts = getSubAccounts();
updateUnspentOutputs();
mPeerGroup.addPeerFilterProvider(mPeerFilter);
Log.d(TAG, "Adding peers");
final String peers = getTrustedPeers();
final List<String> addresses;
if (peers.isEmpty()) {
// DEFAULT_PEER is only set for regtest. For other networks
// it is empty and so will cause us to use DNS discovery.
addresses = mService.getNetwork().getDefaultPeers();
} else
addresses = new ArrayList<>(Arrays.asList(peers.split(",")));
for (final String address : addresses) addPeer(address, getNetworkParameters(networkData));
if (addresses.isEmpty() && !isProxyEnabled() && !getTorEnabled() && getNetworkParameters(networkData).getDnsSeeds() != null) {
// Blank w/o proxy: Use the built in resolving via DNS
mPeerGroup.addPeerDiscovery(new DnsDiscovery(getNetworkParameters(networkData)));
}
} catch (final Exception e) {
e.printStackTrace();
}
}
}
private final TransactionReceivedInBlockListener mTxListner = new TransactionReceivedInBlockListener() {
@Override
public void receiveFromBlock(final Transaction tx, final StoredBlock block, final BlockChain.NewBlockType blockType, final int relativityOffset) throws VerificationException {
if (tx == null)
throw new RuntimeException("receiveFromBlock got null tx");
Log.d(TAG, "receiveFromBlock " + tx.getHash().toString());
addUtxoToValues(tx.getHash(), true);
}
@Override
public boolean notifyTransactionIsInBlock(final Sha256Hash txHash, final StoredBlock block, final BlockChain.NewBlockType blockType, final int relativityOffset) throws VerificationException {
Log.d(TAG, "notifyTransactionIsInBlock " + txHash.toString());
return isUnspentOutpoint(txHash);
}
};
private void stopSync() {
synchronized (mStateLock) {
Log.d(TAG, "stopSync: " + Var("isEnabled", isEnabled()));
if (mPeerGroup != null && mPeerGroup.isRunning()) {
Log.d(TAG, "Stopping peer group");
final Intent i = new Intent("PEERGROUP_UPDATED");
i.putExtra("peergroup", "stopSPVSync");
mService.sendBroadcast(i);
mPeerGroup.stop();
}
if (mNotifyManager != null)
mNotifyManager.cancel(mNotificationId);
if (mBlockChain != null) {
Log.d(TAG, "Disposing of block chain");
// mBlockChain.removeTransactionReceivedListener(mTxListner);
mBlockChain = null;
}
if (mPeerGroup != null) {
Log.d(TAG, "Deleting peer group");
mPeerGroup.removePeerFilterProvider(mPeerFilter);
mPeerGroup = null;
}
if (mBlockStore != null) {
Log.d(TAG, "Closing block store");
try {
mBlockStore.close();
mBlockStore = null;
} catch (final BlockStoreException x) {
throw new RuntimeException(x);
}
}
}
}
// We only care about mobile vs non-mobile so treat others as ethernet
private int getNetworkType(final NetworkInfo info) {
if (info == null)
return ConnectivityManager.TYPE_DUMMY;
final int type = info.getType();
return type == ConnectivityManager.TYPE_MOBILE ? type : ConnectivityManager.TYPE_ETHERNET;
}
private int getNetworkType() {
return getNetworkType(mService.getNetworkInfo());
}
// Handle changes to network connectivity.
// Note that this only handles mobile/non-mobile transitions
public void onNetConnectivityChangedAsync(final NetworkInfo info) {
mExecutor.execute(() -> onNetConnectivityChanged(info));
}
private void onNetConnectivityChanged(final NetworkInfo info) {
synchronized (mStateLock) {
final int oldType = mNetWorkType;
final int newType = getNetworkType(info);
mNetWorkType = newType;
if (!isEnabled() || newType == oldType)
// No change
return;
Log.d(TAG, "onNetConnectivityChanged: " + Var("newType", newType) + Var("oldType", oldType) + Var("isSyncOnMobileEnabled", isSyncOnMobileEnabled()));
// FIXME: - It seems network connectivity changes can happen when
// mPeerGroup is null (i.e. setup hasn't been called),
// but its not clear what path leads to this happening.
if (newType == ConnectivityManager.TYPE_MOBILE) {
if (!isSyncOnMobileEnabled())
// Mobile network and we have sync mobile disabled
stopSync();
} else if (oldType == ConnectivityManager.TYPE_MOBILE) {
if (isSyncOnMobileEnabled())
// Non-Mobile network and we have sync mobile enabled
startSync();
}
}
}
public void resetAsync() {
mExecutor.execute(() -> reset(true, /* deleteAllData */
true));
}
private void reset(final boolean deleteAllData, final boolean deleteUnspent) {
synchronized (mStateLock) {
Log.d(TAG, "reset: " + Var("deleteAllData", deleteAllData) + Var("deleteUnspent", deleteUnspent));
stopSync();
if (deleteAllData) {
Log.d(TAG, "Deleting chain file");
mService.getSPVChainFile().delete();
try {
Log.d(TAG, "Clearing verified and spendable transactions");
// TODO not a namespace anymore, delete all preferences starting with?
// mService.cfgInEdit(SPENDABLE).clear().commit();
// mService.cfgInEdit(VERIFIED).clear().commit();
} catch (final NullPointerException e) {
// ignore
}
}
if (deleteUnspent) {
Log.d(TAG, "Resetting unspent outputs");
mUnspentDetails.clear();
mUnspentOutpoints.clear();
mCountedUtxoValues.clear();
mVerifiedCoinBalances.clear();
}
if (isEnabled()) {
setup();
// We might race with our network callbacks, so fetch the network type
// if its unknown.
if (mNetWorkType == ConnectivityManager.TYPE_DUMMY)
mNetWorkType = getNetworkType();
if (isSyncOnMobileEnabled() || mNetWorkType != ConnectivityManager.TYPE_MOBILE)
startSync();
}
Log.d(TAG, "Finished reset");
}
}
public SharedPreferences cfg() {
final String network = PreferenceManager.getDefaultSharedPreferences(mService).getString(PrefKeys.NETWORK_ID_ACTIVE, "mainnet");
return mService.getSharedPreferences(network, MODE_PRIVATE);
}
public SharedPreferences.Editor cfgEdit() {
return cfg().edit();
}
public String getProxyHost() {
return cfg().getString(PrefKeys.PROXY_HOST, "");
}
public String getProxyPort() {
return cfg().getString(PrefKeys.PROXY_PORT, "");
}
public boolean getProxyEnabled() {
return cfg().getBoolean(PrefKeys.PROXY_ENABLED, false);
}
public boolean getTorEnabled() {
return cfg().getBoolean(PrefKeys.TOR_ENABLED, false);
}
public boolean isProxyEnabled() {
return !TextUtils.isEmpty(getProxyHost()) && !TextUtils.isEmpty(getProxyPort());
}
public String getSPVTrustedPeers() {
return getTrustedPeers();
}
public void setSPVTrustedPeersAsync(final String peers) {
setTrustedPeersAsync(peers);
}
public boolean isSPVEnabled() {
return isEnabled();
}
public void setSPVEnabledAsync(final boolean enabled) {
setEnabledAsync(enabled);
}
public boolean isSPVSyncOnMobileEnabled() {
return isSyncOnMobileEnabled();
}
public void setSPVSyncOnMobileEnabledAsync(final boolean enabled) {
setSyncOnMobileEnabledAsync(enabled);
}
public void resetSPVAsync() {
resetAsync();
}
public PeerGroup getSPVPeerGroup() {
return getPeerGroup();
}
public boolean isSPVVerified(final String txHash) {
return isVerified(txHash);
}
public void enableSPVPingMonitoring() {
enablePingMonitoring();
}
public void disableSPVPingMonitoring() {
disablePingMonitoring();
}
}
19
Source : FetchTransactions.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
System.out.println("Connecting to node");
final NetworkParameters params = TestNet3Params.get();
BlockStore blockStore = new MemoryBlockStore(params);
BlockChain chain = new BlockChain(params, blockStore);
PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.start();
peerGroup.addAddress(new PeerAddress(InetAddress.getLocalHost(), params.getPort()));
peerGroup.waitForPeers(1).get();
Peer peer = peerGroup.getConnectedPeers().get(0);
Sha256Hash txHash = Sha256Hash.wrap(args[0]);
ListenableFuture<Transaction> future = peer.getPeerMempoolTransaction(txHash);
System.out.println("Waiting for node to send us the requested transaction: " + txHash);
Transaction tx = future.get();
System.out.println(tx);
System.out.println("Waiting for node to send us the dependencies ...");
List<Transaction> deps = peer.downloadDependencies(tx).get();
for (Transaction dep : deps) {
System.out.println("Got dependency " + dep.getHashreplacedtring());
}
System.out.println("Done.");
peerGroup.stop();
}
19
Source : FetchBlock.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
public static void main(String[] args) throws Exception {
BriefLogFormatter.init();
System.out.println("Connecting to node");
final NetworkParameters params = TestNet3Params.get();
BlockStore blockStore = new MemoryBlockStore(params);
BlockChain chain = new BlockChain(params, blockStore);
PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.start();
PeerAddress addr = new PeerAddress(InetAddress.getLocalHost(), params.getPort());
peerGroup.addAddress(addr);
peerGroup.waitForPeers(1).get();
Peer peer = peerGroup.getConnectedPeers().get(0);
Sha256Hash blockHash = Sha256Hash.wrap(args[0]);
Future<Block> future = peer.getBlock(blockHash);
System.out.println("Waiting for node to send us the requested block: " + blockHash);
Block block = future.get();
System.out.println(block);
peerGroup.stopAsync();
}
19
Source : TestWithNetworkConnections.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
/**
* Utility clreplaced that makes it easy to work with mock NetworkConnections.
*/
public clreplaced TestWithNetworkConnections {
public static final int PEER_SERVERS = 5;
protected static final NetworkParameters PARAMS = UnitTestParams.get();
protected Context context;
protected BlockStore blockStore;
protected BlockChain blockChain;
protected Wallet wallet;
protected ECKey key;
protected Address address;
protected SocketAddress socketAddress;
private NioServer[] peerServers = new NioServer[PEER_SERVERS];
private final ClientConnectionManager channels;
protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<InboundMessageQueuer>();
public enum ClientType {
NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
}
private final ClientType clientType;
public TestWithNetworkConnections(ClientType clientType) {
this.clientType = clientType;
if (clientType == ClientType.NIO_CLIENT_MANAGER)
channels = new NioClientManager();
else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels = new BlockingClientManager();
else
channels = null;
}
public void setUp() throws Exception {
setUp(new MemoryBlockStore(UnitTestParams.get()));
}
public void setUp(BlockStore blockStore) throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
this.blockStore = blockStore;
// Allow subclreplacedes to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(PARAMS);
key = wallet.freshReceiveKey();
address = key.toAddress(PARAMS);
}
blockChain = new BlockChain(PARAMS, wallet, blockStore);
startPeerServers();
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
channels.startAsync();
channels.awaitRunning();
}
socketAddress = new InetSocketAddress("127.0.0.1", 1111);
}
protected void startPeerServers() throws IOException {
for (int i = 0; i < PEER_SERVERS; i++) {
startPeerServer(i);
}
}
protected void startPeerServer(int i) throws IOException {
peerServers[i] = new NioServer(new StreamConnectionFactory() {
@Nullable
@Override
public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
return new InboundMessageQueuer(PARAMS) {
@Override
public void connectionClosed() {
}
@Override
public void connectionOpened() {
newPeerWriteTargetQueue.offer(this);
}
};
}
}, new InetSocketAddress("127.0.0.1", 2000 + i));
peerServers[i].startAsync();
peerServers[i].awaitRunning();
}
public void tearDown() throws Exception {
stopPeerServers();
}
protected void stopPeerServers() {
for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
}
protected void stopPeerServer(int i) {
peerServers[i].stopAsync();
peerServers[i].awaitTerminated();
}
protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
checkArgument(versionMessage.hasBlockChain());
final AtomicBoolean doneConnecting = new AtomicBoolean(false);
final Thread thisThread = Thread.currentThread();
peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {
@Override
public void onPeerDisconnected(Peer p, int peerCount) {
synchronized (doneConnecting) {
if (!doneConnecting.get())
thisThread.interrupt();
}
}
});
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels.openConnection(new InetSocketAddress("127.0.0.1", 2000), peer);
else if (clientType == ClientType.NIO_CLIENT)
new NioClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100);
else if (clientType == ClientType.BLOCKING_CLIENT)
new BlockingClient(new InetSocketAddress("127.0.0.1", 2000), peer, 100, SocketFactory.getDefault(), null);
else
throw new RuntimeException();
// Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
writeTarget.peer = peer;
// Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
checkState(!peer.getVersionHandshakeFuture().isDone());
writeTarget.sendMessage(versionMessage);
writeTarget.sendMessage(new VersionAck());
try {
checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
peer.getVersionHandshakeFuture().get();
synchronized (doneConnecting) {
doneConnecting.set(true);
}
// Clear interrupted bit in case it was set before we got into the CS
Thread.interrupted();
} catch (InterruptedException e) {
// We were disconnected before we got back version/verack
}
return writeTarget;
}
protected void closePeer(Peer peer) throws Exception {
peer.close();
}
protected void inbound(InboundMessageQueuer peerChannel, Message message) {
peerChannel.sendMessage(message);
}
private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
// Send a ping and wait for it to get to the other side
SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
p.mapPingFutures.put(nonce, pingReceivedFuture);
p.peer.sendMessage(new Ping(nonce));
pingReceivedFuture.get();
p.mapPingFutures.remove(nonce);
}
private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
// Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {
@Override
public Message onPreMessageReceived(Peer p, Message m) {
if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
pongReceivedFuture.set(null);
return null;
}
return m;
}
};
p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
inbound(p, new Pong(nonce));
pongReceivedFuture.get();
p.peer.removePreMessageReceivedEventListener(listener);
}
protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
final long nonce = (long) (Math.random() * Long.MAX_VALUE);
// Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
// to wait on an outbound message, so we do it in the same order or we see race conditions
inboundPongAndWait(p, nonce);
outboundPingAndWait(p, nonce);
}
protected Message outbound(InboundMessageQueuer p1) throws Exception {
pingAndWait(p1);
return p1.nextMessage();
}
protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
return ch.nextMessageBlocking();
}
protected Peer peerOf(InboundMessageQueuer ch) {
return ch.peer;
}
}
19
Source : BlockChainTest.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private BlockChain testNetChain;
private Wallet wallet;
private BlockChain chain;
private BlockStore blockStore;
private Address coinbaseTo;
private static final NetworkParameters PARAMS = UnitTestParams.get();
private final StoredBlock[] block = new StoredBlock[1];
private Transaction coinbaseTransaction;
private static clreplaced TweakableTestNet2Params extends TestNet2Params {
public TweakableTestNet2Params() {
super();
this.cashHardForkActivationTime = (System.currentTimeMillis() / 1000) + 24 * 60 * 60;
}
public void setMaxTarget(BigInteger limit) {
maxTarget = limit;
}
}
private static final TweakableTestNet2Params testNet = new TweakableTestNet2Params();
private void resetBlockStore() {
blockStore = new MemoryBlockStore(PARAMS);
}
@Before
public void setUp() throws Exception {
BriefLogFormatter.initVerbose();
Context.propagate(new Context(testNet, 100, Coin.ZERO, false));
testNetChain = new BlockChain(testNet, new Wallet(testNet), new MemoryBlockStore(testNet));
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
wallet = new Wallet(PARAMS) {
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException {
super.receiveFromBlock(tx, block, blockType, relativityOffset);
BlockChainTest.this.block[0] = block;
if (isTransactionRelevant(tx) && tx.isCoinBase()) {
BlockChainTest.this.coinbaseTransaction = tx;
}
}
};
wallet.freshReceiveKey();
resetBlockStore();
chain = new BlockChain(PARAMS, wallet, blockStore);
coinbaseTo = wallet.currentReceiveKey().toAddress(PARAMS);
}
@Test
public void testBasicChaining() throws Exception {
// Check that we can plug a few blocks together and the futures work.
ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
// Block 1 from the testnet.
Block b1 = getBlock1();
replacedertTrue(testNetChain.add(b1));
replacedertFalse(future.isDone());
// Block 2 from the testnet.
Block b2 = getBlock2();
// Let's try adding an invalid block.
long n = b2.getNonce();
try {
b2.setNonce(12345);
testNetChain.add(b2);
fail();
} catch (VerificationException e) {
b2.setNonce(n);
}
// Now it works because we reset the nonce.
replacedertTrue(testNetChain.add(b2));
replacedertTrue(future.isDone());
replacedertEquals(2, future.get().getHeight());
}
@Test
public void receiveCoins() throws Exception {
int height = 1;
// Quick check that we can actually receive coins.
Transaction tx1 = createFakeTx(PARAMS, COIN, wallet.currentReceiveKey().toAddress(PARAMS));
Block b1 = createFakeBlock(blockStore, height, tx1).block;
chain.add(b1);
replacedertTrue(wallet.getBalance().signum() > 0);
}
@Test
public void unconnectedBlocks() throws Exception {
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
// Connected.
replacedertTrue(chain.add(b1));
// Unconnected but stored. The head of the chain is still b1.
replacedertFalse(chain.add(b3));
replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
// Add in the middle block.
replacedertTrue(chain.add(b2));
replacedertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader());
}
@Test
public void difficultyTransitions() throws Exception {
// Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an
// artificially shortened period.
Block prev = PARAMS.getGenesisBlock();
Utils.setMockClock(System.currentTimeMillis() / 1000);
for (int height = 0; height < PARAMS.getInterval() - 1; height++) {
Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
replacedertTrue(chain.add(newBlock));
prev = newBlock;
// The fake chain should seem to be "fast" for the purposes of difficulty calculations.
Utils.rollMockClock(2);
}
// Now add another block that has no difficulty adjustment, it should be rejected.
try {
chain.add(prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), PARAMS.getInterval()));
fail();
} catch (VerificationException e) {
}
// Create a new block with the right difficulty target given our blistering speed relative to the huge amount
// of time it's supposed to take (set in the unit test network parameters).
Block b = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), PARAMS.getInterval() + 1);
b.setDifficultyTarget(0x201fFFFFL);
b.solve();
replacedertTrue(chain.add(b));
// Successfully traversed a difficulty transition period.
}
@Test
public void badDifficulty() throws Exception {
replacedertTrue(testNetChain.add(getBlock1()));
Block b2 = getBlock2();
replacedertTrue(testNetChain.add(b2));
Block bad = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
// Merkle root can be anything here, doesn't matter.
bad.setMerkleRoot(Sha256Hash.wrap("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
bad.setNonce(140548933);
bad.setTime(1279242649);
bad.setPrevBlockHash(b2.getHash());
// We're going to make this block so easy 50% of solutions will preplaced, and check it gets rejected for having a
// bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all
// solutions.
bad.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
try {
testNetChain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
// allowable difficulty.
fail();
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Difficulty target is bad"));
}
// Accept any level of difficulty now.
BigInteger oldVal = testNet.getMaxTarget();
testNet.setMaxTarget(new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16));
try {
testNetChain.add(bad);
// We should not get here as the difficulty target should not be changing at this point.
// fail();
// TODO: Bitcoin Cash difficulty algorithm returns successfully if there are less than 6 blocks in the blockchain. This test only has 3. We will let the test finish for now.
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Unexpected change in difficulty"));
}
testNet.setMaxTarget(oldVal);
// TODO: Test difficulty change is not out of range when a transition period becomes valid.
}
/**
* Test that version 2 blocks are rejected once version 3 blocks are a super
* majority.
*/
@Test
public void badBip66Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
}
/**
* Test that version 3 blocks are rejected once version 4 blocks are a super
* majority.
*/
@Test
public void badBip65Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
}
private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; height < PARAMS.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
try {
versionChain.add(chainHead.block);
} catch (final VerificationException ex) {
throw (Exception) ex.getCause();
}
}
@Test
public void duplicates() throws Exception {
// Adding a block twice should not have any effect, in particular it should not send the block to the wallet.
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
replacedertTrue(chain.add(b1));
replacedertEquals(b1, block[0].getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b2, block[0].getHeader());
replacedertTrue(chain.add(b3));
replacedertEquals(b3, block[0].getHeader());
replacedertEquals(b3, chain.getChainHead().getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b3, chain.getChainHead().getHeader());
// Wallet was NOT called with the new block because the duplicate add was spotted.
replacedertEquals(b3, block[0].getHeader());
}
@Test
public void intraBlockDependencies() throws Exception {
// Covers issue 166 in which transactions that depend on each other inside a block were not always being
// considered relevant.
Address somebodyElse = new ECKey().toAddress(PARAMS);
Block b1 = PARAMS.getGenesisBlock().createNextBlock(somebodyElse);
ECKey key = wallet.freshReceiveKey();
Address addr = key.toAddress(PARAMS);
// Create a tx that gives us some coins, and another that spends it to someone else in the same block.
Transaction t1 = FakeTxBuilder.createFakeTx(PARAMS, COIN, addr);
Transaction t2 = new Transaction(PARAMS);
t2.addInput(t1.getOutputs().get(0));
t2.addOutput(valueOf(2, 0), somebodyElse);
b1.addTransaction(t1);
b1.addTransaction(t2);
b1.solve();
chain.add(b1);
replacedertEquals(Coin.ZERO, wallet.getBalance());
}
@Test
public void coinbaseTransactionAvailability() throws Exception {
// Check that a coinbase transaction is only available to spend after NetworkParameters.getSpendableCoinbaseDepth() blocks.
// Create a second wallet to receive the coinbase spend.
Wallet wallet2 = new Wallet(PARAMS);
ECKey receiveKey = wallet2.freshReceiveKey();
int height = 1;
chain.addWallet(wallet2);
Address addressToSendTo = receiveKey.toAddress(PARAMS);
// Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
Block b1 = PARAMS.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
chain.add(b1);
// Check a transaction has been received.
replacedertNotNull(coinbaseTransaction);
// The coinbase tx is not yet available to spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail as the coinbase is not mature yet.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
// Check that the coinbase is unavailable to spend for the next spendableCoinbaseDepth - 2 blocks.
for (int i = 0; i < PARAMS.getSpendableCoinbaseDepth() - 2; i++) {
// Non relevant tx - just for fake block creation.
Transaction tx2 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
Block b2 = createFakeBlock(blockStore, height++, tx2).block;
chain.add(b2);
// Wallet still does not have the coinbase transaction available for spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
// The coinbase transaction is still not mature.
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
}
// Give it one more block - should now be able to spend coinbase transaction. Non relevant tx.
Transaction tx3 = createFakeTx(PARAMS, COIN, new ECKey().toAddress(PARAMS));
Block b3 = createFakeBlock(blockStore, height++, tx3).block;
chain.add(b3);
// Wallet now has the coinbase transaction available for spend.
replacedertEquals(wallet.getBalance(), FIFTY_COINS);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(coinbaseTransaction.isMature());
// Create a spend with the coinbase BTC to the address in the second wallet - this should now succeed.
Transaction coinbaseSend2 = wallet.createSend(addressToSendTo, valueOf(49, 0));
replacedertNotNull(coinbaseSend2);
// Commit the coinbaseSpend to the first wallet and check the balances decrement.
wallet.commitTx(coinbaseSend2);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
// Available balance is zero as change has not been received from a block yet.
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), ZERO);
// Give it one more block - change from coinbaseSpend should now be available in the first wallet.
Block b4 = createFakeBlock(blockStore, height++, coinbaseSend2).block;
chain.add(b4);
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
// Check the balances in the second wallet.
replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
}
// Some blocks from the test net.
private static Block getBlock2() throws Exception {
Block b2 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
b2.setMerkleRoot(Sha256Hash.wrap("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b"));
b2.setNonce(2642058077L);
b2.setTime(1296734343L);
b2.setPrevBlockHash(Sha256Hash.wrap("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604"));
replacedertEquals("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.getHashreplacedtring());
b2.verifyHeader();
return b2;
}
private static Block getBlock1() throws Exception {
Block b1 = new Block(testNet, Block.BLOCK_VERSION_GENESIS);
b1.setMerkleRoot(Sha256Hash.wrap("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10"));
b1.setNonce(236038445);
b1.setTime(1296734340);
b1.setPrevBlockHash(Sha256Hash.wrap("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008"));
replacedertEquals("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.getHashreplacedtring());
b1.verifyHeader();
return b1;
}
@Test
public void estimatedBlockTime() throws Exception {
NetworkParameters params = MainNetParams.get();
BlockChain prod = new BlockChain(new Context(params), new MemoryBlockStore(params));
Date d = prod.estimateBlockTime(200000);
// The actual date of block 200,000 was 2012-09-22 10:47:00
replacedertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US).parse("2012-10-23T08:35:05.000-0700"), d);
}
@Test
public void falsePositives() throws Exception {
double decay = AbstractBlockChain.FP_ESTIMATOR_ALPHA;
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
double rate1 = chain.getFalsePositiveRate();
// Run this scenario a few more time for the filter to converge
for (int i = 1; i < 10; i++) {
chain.trackFalsePositives(55);
chain.trackFilteredTransactions(550);
}
// Ensure we are within 10%
replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
// Check that we get repeatable results after a reset
chain.resetFalsePositiveEstimate();
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
replacedertEquals(rate1, chain.getFalsePositiveRate(), 1e-4);
}
@Test
public void rollbackBlockStore() throws Exception {
// This test simulates an issue on Android, that causes the VM to crash while receiving a block, so that the
// block store is persisted but the wallet is not.
Block b1 = PARAMS.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
// Add block 1, no frills.
replacedertTrue(chain.add(b1));
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add block 2 while wallet is disconnected, to simulate crash.
chain.removeWallet(wallet);
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add wallet back. This will detect the height mismatch and repair the damage done.
chain.addWallet(wallet);
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Now add block 2 correctly.
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(2, wallet.getLastBlockSeenHeight());
}
}
19
Source : VersionTally.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
/**
* Initialize the version tally from the block store. Note this does not
* search backwards past the start of the block store, so if starting from
* a checkpoint this may not fill the window.
*
* @param blockStore block store to load blocks from.
* @param chainHead current chain tip.
*/
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
StoredBlock versionBlock = chainHead;
final Stack<Long> versions = new Stack<Long>();
// We don't know how many blocks back we can go, so load what we can first
versions.push(versionBlock.getHeader().getVersion());
for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
versionBlock = versionBlock.getPrev(blockStore);
if (null == versionBlock) {
break;
}
versions.push(versionBlock.getHeader().getVersion());
}
// Replay the versions into the tally
while (!versions.isEmpty()) {
add(versions.pop());
}
}
19
Source : TestNet3Params.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
@Override
protected void checkNextCashWorkRequired(StoredBlock storedPrev, Block nextBlock, BlockStore blockStore) {
// This cannot handle the genesis block and early blocks in general.
// replacedert(pindexPrev);
// Compute the difficulty based on the full adjustement interval.
int nHeight = storedPrev.getHeight();
Preconditions.checkState(nHeight >= this.interval);
// Get the last suitable block of the difficulty interval.
try {
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes then allow
// mining of a min-difficulty block.
Block prev = storedPrev.getHeader();
final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
if (timeDelta >= 0 && timeDelta > NetworkParameters.TARGET_SPACING * 2) {
if (!maxTarget.equals(nextBlock.getDifficultyTargetAsInteger()))
throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(Utils.encodeCompactBits(maxTarget)) + " (required min difficulty) vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
return;
}
StoredBlock pindexLast = GetSuitableBlock(storedPrev, blockStore);
// replacedert (pindexLast);
// Get the first suitable block of the difficulty interval.
int nHeightFirst = nHeight - 144;
StoredBlock pindexFirst = storedPrev;
for (int i = 144; i > 0; --i) {
pindexFirst = pindexFirst.getPrev(blockStore);
if (pindexFirst == null)
return;
}
pindexFirst = GetSuitableBlock(pindexFirst, blockStore);
// replacedert (pindexFirst);
// Compute the target based on time and work done during the interval.
BigInteger nextTarget = ComputeTarget(pindexFirst, pindexLast);
verifyDifficulty(nextTarget, nextBlock);
} catch (BlockStoreException x) {
// this means we don't have enough blocks, yet. let it go until we do.
return;
}
}
19
Source : TestNet3Params.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore, AbstractBlockChain blockChain) throws VerificationException, BlockStoreException {
if (storedPrev.getHeight() < daaHeight && !isDifficultyTransitionPoint(storedPrev) && nextBlock.getTime().after(testnetDiffDate)) {
Block prev = storedPrev.getHeader();
// After 15th February 2012 the rules on the testnet change to avoid people running up the difficulty
// and then leaving, making it too hard to mine a block. On non-difficulty transition points, easy
// blocks are allowed if there has been a span of 20 minutes without one.
final long timeDelta = nextBlock.getTimeSeconds() - prev.getTimeSeconds();
// There is an integer underflow bug in bitcoin-qt that means mindiff blocks are accepted when time
// goes backwards.
if (timeDelta >= 0 && timeDelta <= NetworkParameters.TARGET_SPACING * 2) {
// Walk backwards until we find a block that doesn't have the easiest proof of work, then check
// that difficulty is equal to that one.
StoredBlock cursor = storedPrev;
while (!cursor.getHeader().equals(getGenesisBlock()) && cursor.getHeight() % getInterval() != 0 && cursor.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) cursor = cursor.getPrev(blockStore);
BigInteger cursorTarget = cursor.getHeader().getDifficultyTargetAsInteger();
BigInteger newTarget = nextBlock.getDifficultyTargetAsInteger();
if (!cursorTarget.equals(newTarget))
throw new VerificationException("Testnet block transition that is not allowed: " + Long.toHexString(cursor.getHeader().getDifficultyTarget()) + " vs " + Long.toHexString(nextBlock.getDifficultyTarget()));
}
} else {
super.checkDifficultyTransitions(storedPrev, nextBlock, blockStore, blockChain);
}
}
19
Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
/**
* To reduce the impact of timestamp manipulation, we select the block we are
* basing our computation on via a median of 3.
*/
StoredBlock GetSuitableBlock(StoredBlock pindex, BlockStore blockStore) throws BlockStoreException {
// replacedert(pindex->nHeight >= 3);
/**
* In order to avoid a block is a very skewed timestamp to have too much
* influence, we select the median of the 3 top most blocks as a starting
* point.
*/
StoredBlock[] blocks = new StoredBlock[3];
blocks[2] = pindex;
blocks[1] = pindex.getPrev(blockStore);
blocks[0] = blocks[1].getPrev(blockStore);
// Sorting network.
if (blocks[0].getHeader().getTimeSeconds() > blocks[2].getHeader().getTimeSeconds()) {
// std::swap(blocks[0], blocks[2]);
StoredBlock temp = blocks[0];
blocks[0] = blocks[2];
blocks[2] = temp;
}
if (blocks[0].getHeader().getTimeSeconds() > blocks[1].getHeader().getTimeSeconds()) {
// std::swap(blocks[0], blocks[1]);
StoredBlock temp = blocks[0];
blocks[0] = blocks[1];
blocks[1] = temp;
}
if (blocks[1].getHeader().getTimeSeconds() > blocks[2].getHeader().getTimeSeconds()) {
// std::swap(blocks[1], blocks[2]);
StoredBlock temp = blocks[1];
blocks[1] = blocks[2];
blocks[2] = temp;
}
// We should have our candidate in the middle now.
return blocks[1];
}
19
Source : BlockchainExtended.java
with MIT License
from Bitcoin-com
with MIT License
from Bitcoin-com
public clreplaced BlockchainExtended extends AbstractBlockChain {
private static final Logger logger = LoggerFactory.getLogger(BlockchainExtended.clreplaced);
/**
* Keeps a map of block hashes to StoredBlocks.
*/
protected final BlockStore blockStore;
private BitcoinJListener bitcoinJListener;
private String lastHandledBlockHash = "";
/**
* <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
* one from scratch, or you can deserialize a saved wallet from disk using
* </p>
*
* <p>For the store, you should use {@link org.bitcoinj.store.SPVBlockStore} or you could also try a
* {@link org.bitcoinj.store.MemoryBlockStore} if you want to hold all headers in RAM and don't care about
* disk serialization (this is rare).</p>
*/
public BlockchainExtended(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
/**
* See {@link #BlockchainExtended(Context, Wallet, BlockStore)}}
*/
public BlockchainExtended(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallet, blockStore);
}
/**
* Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
* and receiving coins but rather, just want to explore the network data structures.
*/
public BlockchainExtended(Context context, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
}
/**
* See {@link #BlockchainExtended(Context, BlockStore)}
*/
public BlockchainExtended(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
this(params, new ArrayList<Wallet>(), blockStore);
}
/**
* Constructs a BlockChain connected to the given list of listeners and a store.
*/
public BlockchainExtended(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
super(params, wallets, blockStore);
this.blockStore = blockStore;
}
/**
* See {@link #BlockchainExtended(Context, List, BlockStore)}
*/
public BlockchainExtended(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallets, blockStore);
}
public BlockchainExtended(Context context, BlockStore blockStore, BitcoinJListener bitcoinJListener) throws BlockStoreException {
this(context, new ArrayList<>(), blockStore, bitcoinJListener);
}
public BlockchainExtended(Context context, ArrayList<Wallet> wallets, BlockStore blockStore, BitcoinJListener bitcoinJListener) throws BlockStoreException {
super(context, wallets, blockStore);
this.bitcoinJListener = bitcoinJListener;
this.blockStore = blockStore;
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected void connectBlock(Block block, StoredBlock storedPrev, boolean expensiveChecks, @Nullable List<Sha256Hash> filteredTxHashList, @Nullable Map<Sha256Hash, Transaction> filteredTxn) throws BlockStoreException, VerificationException, PrunedException {
checkState(lock.isHeldByCurrentThread());
boolean filtered = filteredTxHashList != null && filteredTxn != null;
// Check that we aren't connecting a block that fails a checkpoint check
if (!params.preplacedesCheckpoint(storedPrev.getHeight() + 1, block.getHash())) {
throw new VerificationException("Block failed checkpoint lockin at " + (storedPrev.getHeight() + 1));
}
if (shouldVerifyTransactions()) {
checkNotNull(block.getTransactions());
for (Transaction tx : block.getTransactions()) {
if (!tx.isFinal(storedPrev.getHeight() + 1, block.getTimeSeconds())) {
throw new VerificationException("Block contains non-final transaction");
}
}
}
StoredBlock head = getChainHead();
if (storedPrev.equals(head)) {
if (filtered && filteredTxn.size() > 0) {
logger.debug("Block {} connects to top of best chain with {} transaction(s) of which we were sent {}", block.getHashreplacedtring(), filteredTxHashList.size(), filteredTxn.size());
for (Sha256Hash hash : filteredTxHashList) {
logger.debug(" matched tx {}", hash);
}
}
if (expensiveChecks && block.getTimeSeconds() <= getMedianTimestampOfRecentBlocks(head, blockStore)) {
throw new VerificationException("Block's timestamp is too early");
}
// BIP 66 & 65: Enforce block version 3/4 once they are a supermajority of blocks
// NOTE: This requires 1,000 blocks since the last checkpoint (on main
// net, less on test) in order to be applied. It is also limited to
// stopping addition of new v2/3 blocks to the tip of the chain.
if (block.getVersion() == Block.BLOCK_VERSION_BIP34 || block.getVersion() == Block.BLOCK_VERSION_BIP66) {
final Integer count = versionTally.getCountAtOrAbove(block.getVersion() + 1);
if (count != null && count >= params.getMajorityRejectBlockOutdated()) {
throw new VerificationException.BlockVersionOutOfDate(block.getVersion());
}
}
// This block connects to the best known block, it is a normal continuation of the system.
TransactionOutputChanges txOutChanges = null;
if (shouldVerifyTransactions()) {
txOutChanges = connectTransactions(storedPrev.getHeight() + 1, block);
}
handleGenesis(storedPrev);
if (!lastHandledBlockHash.equals(block.getHashreplacedtring())) {
try {
bitcoinJListener.onBlocksDownloaded(block, storedPrev.getHeight() + 1);
lastHandledBlockHash = block.getHashreplacedtring();
} catch (Exception e) {
logger.error("Download block handler failed exiting before ruining the SPV blockhash={}", block.getHashreplacedtring(), e);
while (true) {
try {
Thread.sleep(1000);
logger.error("Retrying block handler for hash={}", block.getHashreplacedtring());
bitcoinJListener.onBlocksDownloaded(block, storedPrev.getHeight() + 1);
lastHandledBlockHash = block.getHashreplacedtring();
break;
} catch (Exception ex) {
}
}
}
}
StoredBlock newStoredBlock = addToBlockStore(storedPrev, block.getTransactions() == null ? block : block.cloneAsHeader(), txOutChanges);
versionTally.add(block.getVersion());
setChainHead(newStoredBlock);
logger.debug("Chain is now {} blocks high, running listeners", newStoredBlock.getHeight());
informListenersForNewBlock(block, NewBlockType.BEST_CHAIN, filteredTxHashList, filteredTxn, newStoredBlock);
} else {
// This block connects to somewhere other than the top of the best known chain. We treat these differently.
//
// Note that we send the transactions to the wallet FIRST, even if we're about to re-organize this block
// to become the new best chain head. This simplifies handling of the re-org in the Wallet clreplaced.
StoredBlock newBlock = storedPrev.build(block);
boolean haveNewBestChain = newBlock.moreWorkThan(head);
if (haveNewBestChain) {
logger.info("Block is causing a re-organize");
} else {
StoredBlock splitPoint = findSplit(newBlock, head, blockStore);
if (splitPoint != null && splitPoint.equals(newBlock)) {
// newStoredBlock is a part of the same chain, there's no fork. This happens when we receive a block
// that we already saw and linked into the chain previously, which isn't the chain head.
// Re-processing it is confusing for the wallet so just skip.
logger.warn("Saw duplicated block in main chain at height {}: {}", newBlock.getHeight(), newBlock.getHeader().getHash());
return;
}
if (splitPoint == null) {
// This should absolutely never happen
// (lets not write the full block to disk to keep any bugs which allow this to happen
// from writing unreasonable amounts of data to disk)
throw new VerificationException("Block forks the chain but splitPoint is null");
} else {
// We aren't actually spending any transactions (yet) because we are on a fork
addToBlockStore(storedPrev, block);
int splitPointHeight = splitPoint.getHeight();
String splitPointHash = splitPoint.getHeader().getHashreplacedtring();
logger.info("Block forks the chain at height {}/block {}, but it did not cause a reorganize:\n{}", splitPointHeight, splitPointHash, newBlock.getHeader().getHashreplacedtring());
}
}
// We may not have any transactions if we received only a header, which can happen during fast catchup.
// If we do, send them to the wallet but state that they are on a side chain so it knows not to try and
// spend them until they become activated.
if (block.getTransactions() != null || filtered) {
informListenersForNewBlock(block, NewBlockType.SIDE_CHAIN, filteredTxHashList, filteredTxn, newBlock);
}
if (haveNewBestChain) {
handleNewBestChain(storedPrev, newBlock, block, expensiveChecks);
}
}
}
private void handleGenesis(StoredBlock storedPrev) {
try {
if (storedPrev.getHeight() + 1 == 1) {
logger.info("Adding genesis to index");
bitcoinJListener.onBlocksDownloaded(Genesis.GENESIS, 0);
}
} catch (Exception e) {
try {
logger.error("Error handling genesis block handler for hash={}", storedPrev.getHeader().getHashreplacedtring(), e);
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
handleGenesis(storedPrev);
}
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected void rollbackBlockStore(int height) throws BlockStoreException {
lock.lock();
try {
int currentHeight = getBestChainHeight();
checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
if (height == currentHeight) {
// nothing to do
return;
}
// Look for the block we want to be the new chain head
StoredBlock newChainHead = blockStore.getChainHead();
while (newChainHead.getHeight() > height) {
newChainHead = newChainHead.getPrev(blockStore);
if (newChainHead == null) {
throw new BlockStoreException("Unreachable height");
}
}
// Modify store directly
blockStore.put(newChainHead);
this.setChainHead(newChainHead);
} finally {
lock.unlock();
}
}
@Override
protected boolean shouldVerifyTransactions() {
return false;
}
@Override
protected TransactionOutputChanges connectTransactions(int height, Block block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void disconnectTransactions(StoredBlock block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
blockStore.setChainHead(chainHead);
}
@Override
protected void notSettingChainHead() throws BlockStoreException {
// We don't use DB transactions here, so we don't need to do anything
}
@Override
protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
return blockStore.get(hash);
}
@Override
public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
boolean success = super.add(block);
if (success) {
trackFilteredTransactions(block.getTransactionCount());
}
return success;
}
}
19
Source : BitcoinJConfig.java
with MIT License
from Bitcoin-com
with MIT License
from Bitcoin-com
@Override
public void destroy() throws Exception {
try {
peerGroup.stopAsync().get(10, TimeUnit.SECONDS);
} catch (RuntimeException e) {
logger.error("Failed to stop PeerGroup", e);
} finally {
BlockStore blockStore = blockChain.getBlockStore();
blockStore.close();
}
}
19
Source : VersionTallyTest.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
@Test
public void testInitialize() throws BlockStoreException {
final BlockStore blockStore = new MemoryBlockStore(UNITTEST);
final BlockChain chain = new BlockChain(UNITTEST, blockStore);
// Build a historical chain of version 2 blocks
long timeSeconds = 1231006505;
StoredBlock chainHead = null;
for (int height = 0; height < UNITTEST.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(blockStore, 2, timeSeconds, height).storedBlock;
replacedertEquals(2, chainHead.getHeader().getVersion());
timeSeconds += 60;
}
VersionTally instance = new VersionTally(UNITTEST);
instance.initialize(blockStore, chainHead);
replacedertEquals(UNITTEST.getMajorityWindow(), instance.getCountAtOrAbove(2).intValue());
}
19
Source : TestWithWallet.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
// TODO: This needs to be somewhat rewritten - the "sendMoneyToWallet" methods aren't sending via the block chain object
/**
* A utility clreplaced that you can derive from in your unit tests. TestWithWallet sets up an empty wallet,
* an in-memory block store and a block chain object. It also provides helper methods for filling the wallet
* with money in whatever ways you wish. Note that for simplicity with amounts, this clreplaced sets the default
* fee per kilobyte to zero in setUp.
*/
public clreplaced TestWithWallet {
protected static final NetworkParameters UNITTEST = UnitTestParams.get();
protected static final NetworkParameters MAINNET = MainNetParams.get();
protected ECKey myKey;
protected Address myAddress;
protected Wallet wallet;
protected BlockChain chain;
protected BlockStore blockStore;
public void setUp() throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
wallet = new Wallet(UNITTEST);
myKey = wallet.currentReceiveKey();
myAddress = LegacyAddress.fromKey(UNITTEST, myKey);
blockStore = new MemoryBlockStore(UNITTEST);
chain = new BlockChain(UNITTEST, wallet, blockStore);
}
public void tearDown() throws Exception {
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
if (type == null) {
// Pending transaction
for (Transaction tx : transactions) if (wallet.isPendingTransactionRelevant(tx))
wallet.receivePending(tx, null);
} else {
FakeTxBuilder.BlockPair bp = createFakeBlock(blockStore, Block.BLOCK_HEIGHT_GENESIS, transactions);
for (Transaction tx : transactions) wallet.receiveFromBlock(tx, bp.storedBlock, type, 0);
if (type == AbstractBlockChain.NewBlockType.BEST_CHAIN)
wallet.notifyNewBestBlock(bp.storedBlock);
}
if (transactions.length == 1)
// Can be null if tx is a double spend that's otherwise irrelevant.
return wallet.getTransaction(transactions[0].getHash());
else
return null;
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
return sendMoneyToWallet(wallet, type, createFakeTx(UNITTEST, value, toAddress));
}
@Nullable
protected Transaction sendMoneyToWallet(Wallet wallet, AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
return sendMoneyToWallet(wallet, type, createFakeTx(UNITTEST, value, toPubKey));
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Transaction... transactions) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, transactions);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, myAddress);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, Address toAddress) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, toAddress);
}
@Nullable
protected Transaction sendMoneyToWallet(AbstractBlockChain.NewBlockType type, Coin value, ECKey toPubKey) throws VerificationException {
return sendMoneyToWallet(this.wallet, type, value, toPubKey);
}
}
19
Source : TestWithNetworkConnections.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
/**
* Utility clreplaced that makes it easy to work with mock NetworkConnections.
*/
public clreplaced TestWithNetworkConnections {
public static final int PEER_SERVERS = 5;
protected static final NetworkParameters UNITTEST = UnitTestParams.get();
protected Context context;
protected BlockStore blockStore;
protected BlockChain blockChain;
protected Wallet wallet;
protected ECKey key;
protected Address address;
protected SocketAddress socketAddress;
private NioServer[] peerServers = new NioServer[PEER_SERVERS];
private final ClientConnectionManager channels;
protected final BlockingQueue<InboundMessageQueuer> newPeerWriteTargetQueue = new LinkedBlockingQueue<>();
public enum ClientType {
NIO_CLIENT_MANAGER, BLOCKING_CLIENT_MANAGER, NIO_CLIENT, BLOCKING_CLIENT
}
private final ClientType clientType;
public TestWithNetworkConnections(ClientType clientType) {
this.clientType = clientType;
if (clientType == ClientType.NIO_CLIENT_MANAGER)
channels = new NioClientManager();
else if (clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels = new BlockingClientManager();
else
channels = null;
}
public void setUp() throws Exception {
setUp(new MemoryBlockStore(UNITTEST));
}
public void setUp(BlockStore blockStore) throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
this.blockStore = blockStore;
// Allow subclreplacedes to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(UNITTEST);
key = wallet.freshReceiveKey();
address = LegacyAddress.fromKey(UNITTEST, key);
}
blockChain = new BlockChain(UNITTEST, wallet, blockStore);
startPeerServers();
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
channels.startAsync();
channels.awaitRunning();
}
socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 1111);
}
protected void startPeerServers() throws IOException {
for (int i = 0; i < PEER_SERVERS; i++) {
startPeerServer(i);
}
}
protected void startPeerServer(int i) throws IOException {
peerServers[i] = new NioServer(new StreamConnectionFactory() {
@Nullable
@Override
public StreamConnection getNewConnection(InetAddress inetAddress, int port) {
return new InboundMessageQueuer(UNITTEST) {
@Override
public void connectionClosed() {
}
@Override
public void connectionOpened() {
newPeerWriteTargetQueue.offer(this);
}
};
}
}, new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000 + i));
peerServers[i].startAsync();
peerServers[i].awaitRunning();
}
public void tearDown() throws Exception {
stopPeerServers();
}
protected void stopPeerServers() {
for (int i = 0; i < PEER_SERVERS; i++) stopPeerServer(i);
}
protected void stopPeerServer(int i) {
peerServers[i].stopAsync();
peerServers[i].awaitTerminated();
}
protected InboundMessageQueuer connect(Peer peer, VersionMessage versionMessage) throws Exception {
checkArgument(versionMessage.hasBlockChain());
final AtomicBoolean doneConnecting = new AtomicBoolean(false);
final Thread thisThread = Thread.currentThread();
peer.addDisconnectedEventListener(new PeerDisconnectedEventListener() {
@Override
public void onPeerDisconnected(Peer p, int peerCount) {
synchronized (doneConnecting) {
if (!doneConnecting.get())
thisThread.interrupt();
}
}
});
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER)
channels.openConnection(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer);
else if (clientType == ClientType.NIO_CLIENT)
new NioClient(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer, 100);
else if (clientType == ClientType.BLOCKING_CLIENT)
new BlockingClient(new InetSocketAddress(InetAddress.getLoopbackAddress(), 2000), peer, 100, SocketFactory.getDefault(), null);
else
throw new RuntimeException();
// Claim we are connected to a different IP that what we really are, so tx confidence broadcastBy sets work
InboundMessageQueuer writeTarget = newPeerWriteTargetQueue.take();
writeTarget.peer = peer;
// Complete handshake with the peer - send/receive version(ack)s, receive bloom filter
checkState(!peer.getVersionHandshakeFuture().isDone());
writeTarget.sendMessage(versionMessage);
writeTarget.sendMessage(new VersionAck());
try {
checkState(writeTarget.nextMessageBlocking() instanceof VersionMessage);
checkState(writeTarget.nextMessageBlocking() instanceof VersionAck);
peer.getVersionHandshakeFuture().get();
synchronized (doneConnecting) {
doneConnecting.set(true);
}
// Clear interrupted bit in case it was set before we got into the CS
Thread.interrupted();
} catch (InterruptedException e) {
// We were disconnected before we got back version/verack
}
return writeTarget;
}
protected void closePeer(Peer peer) throws Exception {
peer.close();
}
protected void inbound(InboundMessageQueuer peerChannel, Message message) {
peerChannel.sendMessage(message);
}
private void outboundPingAndWait(final InboundMessageQueuer p, long nonce) throws Exception {
// Send a ping and wait for it to get to the other side
SettableFuture<Void> pingReceivedFuture = SettableFuture.create();
p.mapPingFutures.put(nonce, pingReceivedFuture);
p.peer.sendMessage(new Ping(nonce));
pingReceivedFuture.get();
p.mapPingFutures.remove(nonce);
}
private void inboundPongAndWait(final InboundMessageQueuer p, final long nonce) throws Exception {
// Receive a ping (that the Peer doesn't see) and wait for it to get through the socket
final SettableFuture<Void> pongReceivedFuture = SettableFuture.create();
PreMessageReceivedEventListener listener = new PreMessageReceivedEventListener() {
@Override
public Message onPreMessageReceived(Peer p, Message m) {
if (m instanceof Pong && ((Pong) m).getNonce() == nonce) {
pongReceivedFuture.set(null);
return null;
}
return m;
}
};
p.peer.addPreMessageReceivedEventListener(Threading.SAME_THREAD, listener);
inbound(p, new Pong(nonce));
pongReceivedFuture.get();
p.peer.removePreMessageReceivedEventListener(listener);
}
protected void pingAndWait(final InboundMessageQueuer p) throws Exception {
final long nonce = (long) (Math.random() * Long.MAX_VALUE);
// Start with an inbound Pong as pingAndWait often happens immediately after an inbound() call, and then wants
// to wait on an outbound message, so we do it in the same order or we see race conditions
inboundPongAndWait(p, nonce);
outboundPingAndWait(p, nonce);
}
protected Message outbound(InboundMessageQueuer p1) throws Exception {
pingAndWait(p1);
return p1.nextMessage();
}
protected Message waitForOutbound(InboundMessageQueuer ch) throws InterruptedException {
return ch.nextMessageBlocking();
}
protected Peer peerOf(InboundMessageQueuer ch) {
return ch.peer;
}
}
19
Source : ParseByteCacheTest.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
public clreplaced ParseByteCacheTest {
private static final int BLOCK_HEIGHT_GENESIS = 0;
private final byte[] txMessage = HEX.withSeparator(" ", 2).decode("f9 be b4 d9 74 78 00 00 00 00 00 00 00 00 00 00" + "02 01 00 00 e2 93 cd be 01 00 00 00 01 6d bd db" + "08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a" + "59 23 3f 45 bc 57 83 80 02 20 59 af 01 ca 17 d0" + "0e 41 83 7a 1d 58 e9 7a a3 1b ae 58 4e de c2 8d" + "35 bd 96 92 36 90 91 3b ae 9a 01 41 04 9c 02 bf" + "c9 7e f2 36 ce 6d 8f e5 d9 40 13 c7 21 e9 15 98" + "2a cd 2b 12 b6 5d 9b 7d 59 e2 0a 84 20 05 f8 fc" + "4e 02 53 2e 87 3d 37 b9 6f 09 d6 d4 51 1a da 8f" + "14 04 2f 46 61 4a 4c 70 c0 f1 4b ef f5 ff ff ff" + "ff 02 40 4b 4c 00 00 00 00 00 19 76 a9 14 1a a0" + "cd 1c be a6 e7 45 8a 7a ba d5 12 a9 d9 ea 1a fb" + "22 5e 88 ac 80 fa e9 c7 00 00 00 00 19 76 a9 14" + "0e ab 5b ea 43 6a 04 84 cf ab 12 48 5e fd a0 b7" + "8b 4e cc 52 88 ac 00 00 00 00");
private final byte[] txMessagePart = HEX.withSeparator(" ", 2).decode("08 5b 1d 8a f7 51 84 f0 bc 01 fa d5 8d 12 66 e9" + "b6 3b 50 88 19 90 e4 b4 0d 6a ee 36 29 00 00 00" + "00 8b 48 30 45 02 21 00 f3 58 1e 19 72 ae 8a c7" + "c7 36 7a 7a 25 3b c1 13 52 23 ad b9 a4 68 bb 3a");
private BlockStore blockStore;
private byte[] b1Bytes;
private byte[] b1BytesWithHeader;
private byte[] tx1Bytes;
private byte[] tx1BytesWithHeader;
private byte[] tx2Bytes;
private byte[] tx2BytesWithHeader;
private static final NetworkParameters UNITTEST = UnitTestParams.get();
private static final NetworkParameters MAINNET = MainNetParams.get();
private void resetBlockStore() {
blockStore = new MemoryBlockStore(UNITTEST);
}
@Before
public void setUp() throws Exception {
Context context = new Context(UNITTEST);
Wallet wallet = new Wallet(context);
wallet.freshReceiveKey();
resetBlockStore();
Transaction tx1 = createFakeTx(UNITTEST, valueOf(2, 0), LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
// add a second input so can test granularity of byte cache.
Transaction prevTx = new Transaction(UNITTEST);
TransactionOutput prevOut = new TransactionOutput(UNITTEST, prevTx, COIN, LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
prevTx.addOutput(prevOut);
// Connect it.
tx1.addInput(prevOut);
Transaction tx2 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
Block b1 = createFakeBlock(blockStore, BLOCK_HEIGHT_GENESIS, tx1, tx2).block;
MessageSerializer bs = UNITTEST.getDefaultSerializer();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(tx1, bos);
tx1BytesWithHeader = bos.toByteArray();
tx1Bytes = tx1.bitcoinSerialize();
bos.reset();
bs.serialize(tx2, bos);
tx2BytesWithHeader = bos.toByteArray();
tx2Bytes = tx2.bitcoinSerialize();
bos.reset();
bs.serialize(b1, bos);
b1BytesWithHeader = bos.toByteArray();
b1Bytes = b1.bitcoinSerialize();
}
@Test
public void validateSetup() {
byte[] b1 = { 1, 1, 1, 2, 3, 4, 5, 6, 7 };
byte[] b2 = { 1, 2, 3 };
replacedertTrue(arrayContains(b1, b2));
replacedertTrue(arrayContains(txMessage, txMessagePart));
replacedertTrue(arrayContains(tx1BytesWithHeader, tx1Bytes));
replacedertTrue(arrayContains(tx2BytesWithHeader, tx2Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, b1Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, tx1Bytes));
replacedertTrue(arrayContains(b1BytesWithHeader, tx2Bytes));
replacedertFalse(arrayContains(tx1BytesWithHeader, b1Bytes));
}
@Test
public void testTransactionsRetain() throws Exception {
testTransaction(MAINNET, txMessage, false, true);
testTransaction(UNITTEST, tx1BytesWithHeader, false, true);
testTransaction(UNITTEST, tx2BytesWithHeader, false, true);
}
@Test
public void testTransactionsNoRetain() throws Exception {
testTransaction(MAINNET, txMessage, false, false);
testTransaction(UNITTEST, tx1BytesWithHeader, false, false);
testTransaction(UNITTEST, tx2BytesWithHeader, false, false);
}
@Test
public void testBlockAll() throws Exception {
testBlock(b1BytesWithHeader, false, false);
testBlock(b1BytesWithHeader, false, true);
}
public void testBlock(byte[] blockBytes, boolean isChild, boolean retain) throws Exception {
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = UNITTEST.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = UNITTEST.getSerializer(retain);
Block b1;
Block bRef;
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// verify our reference BitcoinSerializer produces matching byte array.
bos.reset();
bsRef.serialize(bRef, bos);
replacedertTrue(Arrays.equals(bos.toByteArray(), blockBytes));
// check retain status survive both before and after a serialization
replacedertEquals(retain, b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
serDeser(bs, b1, blockBytes, null, null);
replacedertEquals(retain, b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
// compare to ref block
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// retrieve a value from a child
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
// this will always be true for all children of a block once they are retrieved.
// the tx child inputs/outputs may not be parsed however.
replacedertEquals(retain, tx1.isCached());
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from header
b1.getDifficultyTarget();
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from a child and header
b1.getDifficultyTarget();
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
replacedertEquals(retain, tx1.isCached());
}
// does it still match ref block?
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// change a value in header
b1.setNonce(23);
bRef.setNonce(23);
replacedertFalse(b1.isHeaderBytesValid());
replacedertEquals(retain, b1.isTransactionBytesValid());
// does it still match ref block?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// retrieve a value from a child of a child
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
TransactionInput tin = tx1.getInputs().get(0);
replacedertEquals(retain, tin.isCached());
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// add an input
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
if (tx1.getInputs().size() > 0) {
tx1.addInput(tx1.getInputs().get(0));
// replicate on reference tx
bRef.getTransactions().get(0).addInput(bRef.getTransactions().get(0).getInputs().get(0));
replacedertFalse(tx1.isCached());
replacedertFalse(b1.isTransactionBytesValid());
// confirm sibling cache status was unaffected
if (tx1.getInputs().size() > 1) {
replacedertEquals(retain, tx1.getInputs().get(1).isCached());
}
// this has to be false. Altering a tx invalidates the merkle root.
// when we have seperate merkle caching then the entire header won't need to be
// invalidated.
replacedertFalse(b1.isHeaderBytesValid());
bos.reset();
bsRef.serialize(bRef, bos);
byte[] source = bos.toByteArray();
// confirm we still match the reference tx.
serDeser(bs, b1, source, null, null);
}
// does it still match ref tx?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
// refresh block
b1 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
Block b2 = (Block) bs.deserialize(ByteBuffer.wrap(blockBytes));
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
Block bRef2 = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
// reparent an input
b1.getTransactions();
if (b1.getTransactions().size() > 0) {
Transaction tx1 = b1.getTransactions().get(0);
Transaction tx2 = b2.getTransactions().get(0);
if (tx1.getInputs().size() > 0) {
TransactionInput fromTx1 = tx1.getInputs().get(0);
tx2.addInput(fromTx1);
// replicate on reference tx
TransactionInput fromTxRef = bRef.getTransactions().get(0).getInputs().get(0);
bRef2.getTransactions().get(0).addInput(fromTxRef);
// b1 hasn't changed but it's no longer in the parent
// chain of fromTx1 so has to have been uncached since it won't be
// notified of changes throught the parent chain anymore.
replacedertFalse(b1.isTransactionBytesValid());
// b2 should have it's cache invalidated because it has changed.
replacedertFalse(b2.isTransactionBytesValid());
bos.reset();
bsRef.serialize(bRef2, bos);
byte[] source = bos.toByteArray();
// confirm altered block matches altered ref block.
serDeser(bs, b2, source, null, null);
}
// does unaltered block still match ref block?
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
// how about if we refresh it?
bRef = (Block) bsRef.deserialize(ByteBuffer.wrap(blockBytes));
bos.reset();
bsRef.serialize(bRef, bos);
serDeser(bs, b1, bos.toByteArray(), null, null);
}
}
public void testTransaction(NetworkParameters params, byte[] txBytes, boolean isChild, boolean retain) throws Exception {
// reference serializer to produce comparison serialization output after changes to
// message structure.
MessageSerializer bsRef = params.getSerializer(false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BitcoinSerializer bs = params.getSerializer(retain);
Transaction t1;
Transaction tRef;
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
// verify our reference BitcoinSerializer produces matching byte array.
bos.reset();
bsRef.serialize(tRef, bos);
replacedertTrue(Arrays.equals(bos.toByteArray(), txBytes));
// check and retain status survive both before and after a serialization
replacedertEquals(retain, t1.isCached());
serDeser(bs, t1, txBytes, null, null);
replacedertEquals(retain, t1.isCached());
// compare to ref tx
bos.reset();
bsRef.serialize(tRef, bos);
serDeser(bs, t1, bos.toByteArray(), null, null);
// retrieve a value from a child
t1.getInputs();
if (t1.getInputs().size() > 0) {
TransactionInput tin = t1.getInputs().get(0);
replacedertEquals(retain, tin.isCached());
// does it still match ref tx?
serDeser(bs, t1, bos.toByteArray(), null, null);
}
// refresh tx
t1 = (Transaction) bs.deserialize(ByteBuffer.wrap(txBytes));
tRef = (Transaction) bsRef.deserialize(ByteBuffer.wrap(txBytes));
// add an input
if (t1.getInputs().size() > 0) {
t1.addInput(t1.getInputs().get(0));
// replicate on reference tx
tRef.addInput(tRef.getInputs().get(0));
replacedertFalse(t1.isCached());
bos.reset();
bsRef.serialize(tRef, bos);
byte[] source = bos.toByteArray();
// confirm we still match the reference tx.
serDeser(bs, t1, source, null, null);
}
}
private void serDeser(MessageSerializer bs, Message message, byte[] sourceBytes, byte[] containedBytes, byte[] containingBytes) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bs.serialize(message, bos);
byte[] b1 = bos.toByteArray();
Message m2 = bs.deserialize(ByteBuffer.wrap(b1));
replacedertEquals(message, m2);
bos.reset();
bs.serialize(m2, bos);
byte[] b2 = bos.toByteArray();
replacedertTrue(Arrays.equals(b1, b2));
if (sourceBytes != null) {
replacedertTrue(arrayContains(sourceBytes, b1));
replacedertTrue(arrayContains(sourceBytes, b2));
}
if (containedBytes != null) {
replacedertTrue(arrayContains(b1, containedBytes));
}
if (containingBytes != null) {
replacedertTrue(arrayContains(containingBytes, b1));
}
}
public static boolean arrayContains(byte[] sup, byte[] sub) {
if (sup.length < sub.length)
return false;
String superstring = Utils.HEX.encode(sup);
String substring = Utils.HEX.encode(sub);
int ind = superstring.indexOf(substring);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < superstring.indexOf(substring); i++) sb.append(" ");
// System.out.println(superstring);
// System.out.println(sb.append(substring).toString());
// System.out.println();
return ind > -1;
}
}
19
Source : BlockChainTest.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
// Handling of chain splits/reorgs are in ChainSplitTests.
public clreplaced BlockChainTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private BlockChain testNetChain;
private Wallet wallet;
private BlockChain chain;
private BlockStore blockStore;
private Address coinbaseTo;
private final StoredBlock[] block = new StoredBlock[1];
private Transaction coinbaseTransaction;
private static clreplaced TweakableTestNet3Params extends TestNet3Params {
public void setMaxTarget(BigInteger limit) {
maxTarget = limit;
}
}
private static final TweakableTestNet3Params TESTNET = new TweakableTestNet3Params();
private static final NetworkParameters UNITTEST = UnitTestParams.get();
private static final NetworkParameters MAINNET = MainNetParams.get();
private void resetBlockStore() {
blockStore = new MemoryBlockStore(UNITTEST);
}
@Before
public void setUp() throws Exception {
BriefLogFormatter.initVerbose();
Context.propagate(new Context(TESTNET, 100, Coin.ZERO, false));
testNetChain = new BlockChain(TESTNET, new Wallet(TESTNET), new MemoryBlockStore(TESTNET));
Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
wallet = new Wallet(UNITTEST) {
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException {
super.receiveFromBlock(tx, block, blockType, relativityOffset);
BlockChainTest.this.block[0] = block;
if (isTransactionRelevant(tx) && tx.isCoinBase()) {
BlockChainTest.this.coinbaseTransaction = tx;
}
}
};
wallet.freshReceiveKey();
resetBlockStore();
chain = new BlockChain(UNITTEST, wallet, blockStore);
coinbaseTo = LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey());
}
@Test
public void testBasicChaining() throws Exception {
// Check that we can plug a few blocks together and the futures work.
ListenableFuture<StoredBlock> future = testNetChain.getHeightFuture(2);
// Block 1 from the testnet.
Block b1 = getBlock1();
replacedertTrue(testNetChain.add(b1));
replacedertFalse(future.isDone());
// Block 2 from the testnet.
Block b2 = getBlock2();
// Let's try adding an invalid block.
long n = b2.getNonce();
try {
b2.setNonce(12345);
testNetChain.add(b2);
fail();
} catch (VerificationException e) {
b2.setNonce(n);
}
// Now it works because we reset the nonce.
replacedertTrue(testNetChain.add(b2));
replacedertTrue(future.isDone());
replacedertEquals(2, future.get().getHeight());
}
@Test
public void receiveCoins() throws Exception {
int height = 1;
// Quick check that we can actually receive coins.
Transaction tx1 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, wallet.currentReceiveKey()));
Block b1 = createFakeBlock(blockStore, height, tx1).block;
chain.add(b1);
replacedertTrue(wallet.getBalance().signum() > 0);
}
@Test
public void unconnectedBlocks() throws Exception {
Block b1 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
// Connected.
replacedertTrue(chain.add(b1));
// Unconnected but stored. The head of the chain is still b1.
replacedertFalse(chain.add(b3));
replacedertEquals(chain.getChainHead().getHeader(), b1.cloneAsHeader());
// Add in the middle block.
replacedertTrue(chain.add(b2));
replacedertEquals(chain.getChainHead().getHeader(), b3.cloneAsHeader());
}
@Test
public void difficultyTransitions() throws Exception {
// Add a bunch of blocks in a loop until we reach a difficulty transition point. The unit test params have an
// artificially shortened period.
Block prev = UNITTEST.getGenesisBlock();
Utils.setMockClock(System.currentTimeMillis() / 1000);
for (int height = 0; height < UNITTEST.getInterval() - 1; height++) {
Block newBlock = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), height);
replacedertTrue(chain.add(newBlock));
prev = newBlock;
// The fake chain should seem to be "fast" for the purposes of difficulty calculations.
Utils.rollMockClock(2);
}
// Now add another block that has no difficulty adjustment, it should be rejected.
try {
chain.add(prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), UNITTEST.getInterval()));
fail();
} catch (VerificationException e) {
}
// Create a new block with the right difficulty target given our blistering speed relative to the huge amount
// of time it's supposed to take (set in the unit test network parameters).
Block b = prev.createNextBlock(coinbaseTo, 1, Utils.currentTimeSeconds(), UNITTEST.getInterval() + 1);
b.setDifficultyTarget(0x201fFFFFL);
b.solve();
replacedertTrue(chain.add(b));
// Successfully traversed a difficulty transition period.
}
@Test
public void badDifficulty() throws Exception {
replacedertTrue(testNetChain.add(getBlock1()));
Block b2 = getBlock2();
replacedertTrue(testNetChain.add(b2));
Block bad = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
// Merkle root can be anything here, doesn't matter.
bad.setMerkleRoot(Sha256Hash.wrap("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
// Nonce was just some number that made the hash < difficulty limit set below, it can be anything.
bad.setNonce(140548933);
bad.setTime(1279242649);
bad.setPrevBlockHash(b2.getHash());
// We're going to make this block so easy 50% of solutions will preplaced, and check it gets rejected for having a
// bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all
// solutions.
bad.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
try {
testNetChain.add(bad);
// The difficulty target above should be rejected on the grounds of being easier than the networks
// allowable difficulty.
fail();
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Difficulty target is bad"));
}
// Accept any level of difficulty now.
BigInteger oldVal = TESTNET.getMaxTarget();
TESTNET.setMaxTarget(new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16));
try {
testNetChain.add(bad);
// We should not get here as the difficulty target should not be changing at this point.
fail();
} catch (VerificationException e) {
replacedertTrue(e.getMessage(), e.getCause().getMessage().contains("Unexpected change in difficulty"));
}
TESTNET.setMaxTarget(oldVal);
// TODO: Test difficulty change is not out of range when a transition period becomes valid.
}
/**
* Test that version 2 blocks are rejected once version 3 blocks are a super
* majority.
*/
@Test
public void badBip66Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP34, Block.BLOCK_VERSION_BIP66);
}
/**
* Test that version 3 blocks are rejected once version 4 blocks are a super
* majority.
*/
@Test
public void badBip65Version() throws Exception {
testDeprecatedBlockVersion(Block.BLOCK_VERSION_BIP66, Block.BLOCK_VERSION_BIP65);
}
private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
final BlockStore versionBlockStore = new MemoryBlockStore(UNITTEST);
final BlockChain versionChain = new BlockChain(UNITTEST, versionBlockStore);
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (height = 0; height < (UNITTEST.getMajorityWindow() - UNITTEST.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; height < UNITTEST.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
try {
versionChain.add(chainHead.block);
} catch (final VerificationException ex) {
throw (Exception) ex.getCause();
}
}
@Test
public void duplicates() throws Exception {
// Adding a block twice should not have any effect, in particular it should not send the block to the wallet.
Block b1 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
Block b3 = b2.createNextBlock(coinbaseTo);
replacedertTrue(chain.add(b1));
replacedertEquals(b1, block[0].getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b2, block[0].getHeader());
replacedertTrue(chain.add(b3));
replacedertEquals(b3, block[0].getHeader());
replacedertEquals(b3, chain.getChainHead().getHeader());
replacedertTrue(chain.add(b2));
replacedertEquals(b3, chain.getChainHead().getHeader());
// Wallet was NOT called with the new block because the duplicate add was spotted.
replacedertEquals(b3, block[0].getHeader());
}
@Test
public void intraBlockDependencies() throws Exception {
// Covers issue 166 in which transactions that depend on each other inside a block were not always being
// considered relevant.
Address somebodyElse = LegacyAddress.fromKey(UNITTEST, new ECKey());
Block b1 = UNITTEST.getGenesisBlock().createNextBlock(somebodyElse);
ECKey key = wallet.freshReceiveKey();
Address addr = LegacyAddress.fromKey(UNITTEST, key);
// Create a tx that gives us some coins, and another that spends it to someone else in the same block.
Transaction t1 = FakeTxBuilder.createFakeTx(UNITTEST, COIN, addr);
Transaction t2 = new Transaction(UNITTEST);
t2.addInput(t1.getOutputs().get(0));
t2.addOutput(valueOf(2, 0), somebodyElse);
b1.addTransaction(t1);
b1.addTransaction(t2);
b1.solve();
chain.add(b1);
replacedertEquals(Coin.ZERO, wallet.getBalance());
}
@Test
public void coinbaseTransactionAvailability() throws Exception {
// Check that a coinbase transaction is only available to spend after NetworkParameters.getSpendableCoinbaseDepth() blocks.
// Create a second wallet to receive the coinbase spend.
Wallet wallet2 = new Wallet(UNITTEST);
ECKey receiveKey = wallet2.freshReceiveKey();
int height = 1;
chain.addWallet(wallet2);
Address addressToSendTo = LegacyAddress.fromKey(UNITTEST, receiveKey);
// Create a block, sending the coinbase to the coinbaseTo address (which is in the wallet).
Block b1 = UNITTEST.getGenesisBlock().createNextBlockWithCoinbase(Block.BLOCK_VERSION_GENESIS, wallet.currentReceiveKey().getPubKey(), height++);
chain.add(b1);
// Check a transaction has been received.
replacedertNotNull(coinbaseTransaction);
// The coinbase tx is not yet available to spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail as the coinbase is not mature yet.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
// Check that the coinbase is unavailable to spend for the next spendableCoinbaseDepth - 2 blocks.
for (int i = 0; i < UNITTEST.getSpendableCoinbaseDepth() - 2; i++) {
// Non relevant tx - just for fake block creation.
Transaction tx2 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
Block b2 = createFakeBlock(blockStore, height++, tx2).block;
chain.add(b2);
// Wallet still does not have the coinbase transaction available for spend.
replacedertEquals(Coin.ZERO, wallet.getBalance());
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
// The coinbase transaction is still not mature.
replacedertTrue(!coinbaseTransaction.isMature());
// Attempt to spend the coinbase - this should fail.
try {
wallet.createSend(addressToSendTo, valueOf(49, 0));
fail();
} catch (InsufficientMoneyException e) {
}
}
// Give it one more block - should now be able to spend coinbase transaction. Non relevant tx.
Transaction tx3 = createFakeTx(UNITTEST, COIN, LegacyAddress.fromKey(UNITTEST, new ECKey()));
Block b3 = createFakeBlock(blockStore, height++, tx3).block;
chain.add(b3);
// Wallet now has the coinbase transaction available for spend.
replacedertEquals(wallet.getBalance(), FIFTY_COINS);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), FIFTY_COINS);
replacedertTrue(coinbaseTransaction.isMature());
// Create a spend with the coinbase BTC to the address in the second wallet - this should now succeed.
Transaction coinbaseSend2 = wallet.createSend(addressToSendTo, valueOf(49, 0));
replacedertNotNull(coinbaseSend2);
// Commit the coinbaseSpend to the first wallet and check the balances decrement.
wallet.commitTx(coinbaseSend2);
replacedertEquals(wallet.getBalance(BalanceType.ESTIMATED), COIN);
// Available balance is zero as change has not been received from a block yet.
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), ZERO);
// Give it one more block - change from coinbaseSpend should now be available in the first wallet.
Block b4 = createFakeBlock(blockStore, height++, coinbaseSend2).block;
chain.add(b4);
replacedertEquals(wallet.getBalance(BalanceType.AVAILABLE), COIN);
// Check the balances in the second wallet.
replacedertEquals(wallet2.getBalance(BalanceType.ESTIMATED), valueOf(49, 0));
replacedertEquals(wallet2.getBalance(BalanceType.AVAILABLE), valueOf(49, 0));
}
// Some blocks from the test net.
private static Block getBlock2() throws Exception {
Block b2 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
b2.setMerkleRoot(Sha256Hash.wrap("20222eb90f5895556926c112bb5aa0df4ab5abc3107e21a6950aec3b2e3541e2"));
b2.setNonce(875942400L);
b2.setTime(1296688946L);
b2.setDifficultyTarget(0x1d00ffff);
b2.setPrevBlockHash(Sha256Hash.wrap("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206"));
replacedertEquals("000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820", b2.getHashreplacedtring());
b2.verifyHeader();
return b2;
}
private static Block getBlock1() throws Exception {
Block b1 = new Block(TESTNET, Block.BLOCK_VERSION_GENESIS);
b1.setMerkleRoot(Sha256Hash.wrap("f0315ffc38709d70ad5647e22048358dd3745f3ce3874223c80a7c92fab0c8ba"));
b1.setNonce(1924588547);
b1.setTime(1296688928);
b1.setDifficultyTarget(0x1d00ffff);
b1.setPrevBlockHash(Sha256Hash.wrap("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
replacedertEquals("00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206", b1.getHashreplacedtring());
b1.verifyHeader();
return b1;
}
@Test
public void estimatedBlockTime() throws Exception {
BlockChain prod = new BlockChain(new Context(MAINNET), new MemoryBlockStore(MAINNET));
Date d = prod.estimateBlockTime(200000);
// The actual date of block 200,000 was 2012-09-22 10:47:00
replacedertEquals(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US).parse("2012-10-23T08:35:05.000-0700"), d);
}
@Test
public void falsePositives() throws Exception {
double decay = AbstractBlockChain.FP_ESTIMATOR_ALPHA;
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
double rate1 = chain.getFalsePositiveRate();
// Run this scenario a few more time for the filter to converge
for (int i = 1; i < 10; i++) {
chain.trackFalsePositives(55);
chain.trackFilteredTransactions(550);
}
// Ensure we are within 10%
replacedertEquals(0.1, chain.getFalsePositiveRate(), 0.01);
// Check that we get repeatable results after a reset
chain.resetFalsePositiveEstimate();
// Exactly
replacedertTrue(0 == chain.getFalsePositiveRate());
chain.trackFalsePositives(55);
replacedertEquals(decay * 55, chain.getFalsePositiveRate(), 1e-4);
chain.trackFilteredTransactions(550);
replacedertEquals(rate1, chain.getFalsePositiveRate(), 1e-4);
}
@Test
public void rollbackBlockStore() throws Exception {
// This test simulates an issue on Android, that causes the VM to crash while receiving a block, so that the
// block store is persisted but the wallet is not.
Block b1 = UNITTEST.getGenesisBlock().createNextBlock(coinbaseTo);
Block b2 = b1.createNextBlock(coinbaseTo);
// Add block 1, no frills.
replacedertTrue(chain.add(b1));
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add block 2 while wallet is disconnected, to simulate crash.
chain.removeWallet(wallet);
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Add wallet back. This will detect the height mismatch and repair the damage done.
chain.addWallet(wallet);
replacedertEquals(b1.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(1, chain.getBestChainHeight());
replacedertEquals(1, wallet.getLastBlockSeenHeight());
// Now add block 2 correctly.
replacedertTrue(chain.add(b2));
replacedertEquals(b2.cloneAsHeader(), chain.getChainHead().getHeader());
replacedertEquals(2, chain.getBestChainHeight());
replacedertEquals(2, wallet.getLastBlockSeenHeight());
}
}
19
Source : VersionTally.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
/**
* Initialize the version tally from the block store. Note this does not
* search backwards past the start of the block store, so if starting from
* a checkpoint this may not fill the window.
*
* @param blockStore block store to load blocks from.
* @param chainHead current chain tip.
*/
public void initialize(final BlockStore blockStore, final StoredBlock chainHead) throws BlockStoreException {
StoredBlock versionBlock = chainHead;
final Stack<Long> versions = new Stack<>();
// We don't know how many blocks back we can go, so load what we can first
versions.push(versionBlock.getHeader().getVersion());
for (int headOffset = 0; headOffset < versionWindow.length; headOffset++) {
versionBlock = versionBlock.getPrev(blockStore);
if (null == versionBlock) {
break;
}
versions.push(versionBlock.getHeader().getVersion());
}
// Replay the versions into the tally
while (!versions.isEmpty()) {
add(versions.pop());
}
}
19
Source : BlockChain.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
// TODO: Rename this clreplaced to SPVBlockChain at some point.
/**
* A BlockChain implements the <i>simplified payment verification</i> mode of the Bitcoin protocol. It is the right
* choice to use for programs that have limited resources as it won't verify transactions signatures or attempt to store
* all of the block chain. Really, this clreplaced should be called SPVBlockChain but for backwards compatibility it is not.
*/
public clreplaced BlockChain extends AbstractBlockChain {
/**
* Keeps a map of block hashes to StoredBlocks.
*/
protected final BlockStore blockStore;
/**
* <p>Constructs a BlockChain connected to the given wallet and store. To obtain a {@link Wallet} you can construct
* one from scratch, or you can deserialize a saved wallet from disk using
* {@link Wallet#loadFromFile(File, WalletExtension...)}</p>
* <p>
* <p>For the store, you should use {@link SPVBlockStore} or you could also try a
* {@link MemoryBlockStore} if you want to hold all headers in RAM and don't care about
* disk serialization (this is rare).</p>
*/
public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
/**
* See {@link #BlockChain(Context, Wallet, BlockStore)}}
*/
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallet, blockStore);
}
/**
* Constructs a BlockChain that has no wallet at all. This is helpful when you don't actually care about sending
* and receiving coins but rather, just want to explore the network data structures.
*/
public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<Wallet>(), blockStore);
}
/**
* See {@link #BlockChain(Context, BlockStore)}
*/
public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
this(params, new ArrayList<Wallet>(), blockStore);
}
/**
* Constructs a BlockChain connected to the given list of listeners and a store.
*/
public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
super(params, wallets, blockStore);
this.blockStore = blockStore;
}
/**
* See {@link #BlockChain(Context, List, BlockStore)}
*/
public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallets, blockStore);
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader, TransactionOutputChanges txOutChanges) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block blockHeader) throws BlockStoreException, VerificationException {
StoredBlock newBlock = storedPrev.build(blockHeader);
blockStore.put(newBlock);
return newBlock;
}
@Override
protected void rollbackBlockStore(int height) throws BlockStoreException {
lock.lock();
try {
int currentHeight = getBestChainHeight();
checkArgument(height >= 0 && height <= currentHeight, "Bad height: %s", height);
if (height == currentHeight)
// nothing to do
return;
// Look for the block we want to be the new chain head
StoredBlock newChainHead = blockStore.getChainHead();
while (newChainHead.getHeight() > height) {
newChainHead = newChainHead.getPrev(blockStore);
if (newChainHead == null)
throw new BlockStoreException("Unreachable height");
}
// Modify store directly
blockStore.put(newChainHead);
this.setChainHead(newChainHead);
} finally {
lock.unlock();
}
}
@Override
protected boolean shouldVerifyTransactions() {
return false;
}
@Override
protected TransactionOutputChanges connectTransactions(int height, Block block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected TransactionOutputChanges connectTransactions(StoredBlock newBlock) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void disconnectTransactions(StoredBlock block) {
// Don't have to do anything as this is only called if(shouldVerifyTransactions())
throw new UnsupportedOperationException();
}
@Override
protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
blockStore.setChainHead(chainHead);
}
@Override
protected void notSettingChainHead() throws BlockStoreException {
// We don't use DB transactions here, so we don't need to do anything
}
@Override
protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
return blockStore.get(hash);
}
@Override
public boolean add(FilteredBlock block) throws VerificationException, PrunedException {
boolean success = super.add(block);
if (success) {
trackFilteredTransactions(block.getTransactionCount());
}
return success;
}
}
19
Source : CoinType.java
with Apache License 2.0
from AnyBitIO
with Apache License 2.0
from AnyBitIO
@Override
public void checkDifficultyTransitions(StoredBlock storedBlock, Block block, BlockStore blockStore) throws VerificationException, BlockStoreException {
}
18
Source : TestWithNetworkConnections.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
public void setUp(BlockStore blockStore) throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(PARAMS, 100, Coin.ZERO, false));
this.blockStore = blockStore;
// Allow subclreplacedes to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(PARAMS);
key = wallet.freshReceiveKey();
address = key.toAddress(PARAMS);
}
blockChain = new BlockChain(PARAMS, wallet, blockStore);
startPeerServers();
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
channels.startAsync();
channels.awaitRunning();
}
socketAddress = new InetSocketAddress("127.0.0.1", 1111);
}
18
Source : FakeTxBuilder.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* Emulates receiving a valid block
*/
public static BlockPair createFakeBlock(BlockStore blockStore, StoredBlock previousStoredBlock, long version, long timeSeconds, int height, Transaction... transactions) {
try {
Block previousBlock = previousStoredBlock.getHeader();
Address to = new ECKey().toAddress(previousBlock.getParams());
Block b = previousBlock.createNextBlock(to, version, timeSeconds, height);
// Coinbase tx was already added.
for (Transaction tx : transactions) {
tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
b.addTransaction(tx);
}
b.solve();
BlockPair pair = new BlockPair();
pair.block = b;
pair.storedBlock = previousStoredBlock.build(b);
blockStore.put(pair.storedBlock);
blockStore.setChainHead(pair.storedBlock);
return pair;
} catch (VerificationException e) {
// Cannot happen.
throw new RuntimeException(e);
} catch (BlockStoreException e) {
// Cannot happen.
throw new RuntimeException(e);
}
}
18
Source : RefreshWallet.java
with GNU General Public License v3.0
from Blockstream
with GNU General Public License v3.0
from Blockstream
public static void main(String[] args) throws Exception {
File file = new File(args[0]);
Wallet wallet = Wallet.loadFromFile(file);
System.out.println(wallet.toString());
// Set up the components and link them together.
final NetworkParameters params = TestNet3Params.get();
BlockStore blockStore = new MemoryBlockStore(params);
BlockChain chain = new BlockChain(params, wallet, blockStore);
final PeerGroup peerGroup = new PeerGroup(params, chain);
peerGroup.startAsync();
wallet.addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
@Override
public synchronized void onCoinsReceived(Wallet w, Transaction tx, Coin prevBalance, Coin newBalance) {
System.out.println("\nReceived tx " + tx.getHashreplacedtring());
System.out.println(tx.toString());
}
});
// Now download and process the block chain.
peerGroup.downloadBlockChain();
peerGroup.stopAsync();
wallet.saveToFile(file);
System.out.println("\nDone!\n");
System.out.println(wallet.toString());
}
18
Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
/**
* Compute the next required proof of work using a weighted average of the
* estimated hashrate per block.
*
* Using a weighted average ensure that the timestamp parameter cancels out in
* most of the calculation - except for the timestamp of the first and last
* block. Because timestamps are the least trustworthy information we have as
* input, this ensures the algorithm is more resistant to malicious inputs.
*/
protected void checkNextCashWorkRequired(StoredBlock pindexPrev, Block pblock, BlockStore blockStore) {
// This cannot handle the genesis block and early blocks in general.
// replacedert(pindexPrev);
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 2* 10 minutes then allow
// mining of a min-difficulty block.
/*if (params.fPowAllowMinDifficultyBlocks &&
(pblock->GetBlockTime() >
pindexPrev->GetBlockTime() + 2 * params.nPowTargetSpacing)) {
return UintToArith256(params.powLimit).GetCompact();
}*/
// Compute the difficulty based on the full adjustement interval.
int nHeight = pindexPrev.getHeight();
Preconditions.checkState(nHeight >= this.interval);
// Get the last suitable block of the difficulty interval.
try {
StoredBlock pindexLast = GetSuitableBlock(pindexPrev, blockStore);
// replacedert (pindexLast);
// Get the first suitable block of the difficulty interval.
int nHeightFirst = nHeight - 144;
StoredBlock pindexFirst = pindexPrev;
for (int i = 144; i > 0; --i) {
pindexFirst = pindexFirst.getPrev(blockStore);
if (pindexFirst == null)
return;
}
pindexFirst = GetSuitableBlock(pindexFirst, blockStore);
// replacedert (pindexFirst);
// Compute the target based on time and work done during the interval.
BigInteger nextTarget = ComputeTarget(pindexFirst, pindexLast);
verifyDifficulty(nextTarget, pblock);
} catch (BlockStoreException x) {
// this means we don't have enough blocks, yet. let it go until we do.
return;
}
}
18
Source : BitcoinJConfig.java
with MIT License
from Bitcoin-com
with MIT License
from Bitcoin-com
@Bean
public BlockchainExtended blockChain(BlockStore blockStore, Context context, BitcoinJListener bitcoinJListener) throws BlockStoreException {
this.blockChain = new BlockchainExtended(context, blockStore, bitcoinJListener);
return blockChain;
}
18
Source : TestWithNetworkConnections.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
public void setUp(BlockStore blockStore) throws Exception {
BriefLogFormatter.init();
Context.propagate(new Context(UNITTEST, 100, Coin.ZERO, false));
this.blockStore = blockStore;
// Allow subclreplacedes to override the wallet object with their own.
if (wallet == null) {
wallet = new Wallet(UNITTEST);
key = wallet.freshReceiveKey();
address = LegacyAddress.fromKey(UNITTEST, key);
}
blockChain = new BlockChain(UNITTEST, wallet, blockStore);
startPeerServers();
if (clientType == ClientType.NIO_CLIENT_MANAGER || clientType == ClientType.BLOCKING_CLIENT_MANAGER) {
channels.startAsync();
channels.awaitRunning();
}
socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 1111);
}
17
Source : BlockChainTest.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
final BlockStore versionBlockStore = new MemoryBlockStore(PARAMS);
final BlockChain versionChain = new BlockChain(PARAMS, versionBlockStore);
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (height = 0; height < (PARAMS.getMajorityWindow() - PARAMS.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; height < PARAMS.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
try {
versionChain.add(chainHead.block);
} catch (final VerificationException ex) {
throw (Exception) ex.getCause();
}
}
17
Source : AbstractBitcoinNetParams.java
with Apache License 2.0
from bitcoincash-wallet
with Apache License 2.0
from bitcoincash-wallet
@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore, AbstractBlockChain blockChain) throws VerificationException, BlockStoreException {
Block prev = storedPrev.getHeader();
if (storedPrev.getHeight() + 1 >= daaHeight) {
checkNextCashWorkRequired(storedPrev, nextBlock, blockStore);
return;
}
// Is this supposed to be a difficulty transition point
if (!isDifficultyTransitionPoint(storedPrev)) {
if (storedPrev.getHeader().getDifficultyTargetAsInteger().equals(getMaxTarget())) {
// No ... so check the difficulty didn't actually change.
if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Long.toHexString(prev.getDifficultyTarget()));
return;
}
// If producing the last 6 block took less than 12h, we keep the same
// difficulty.
StoredBlock cursor = blockStore.get(prev.getHash());
for (int i = 0; i < 6; i++) {
if (cursor == null) {
return;
// This should never happen. If it does, it means we are following an incorrect or busted chain.
// throw new VerificationException(
// "We did not find a way back to the genesis block.");
}
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
long mpt6blocks = 0;
try {
mpt6blocks = blockChain.getMedianTimestampOfRecentBlocks(storedPrev, blockStore) - blockChain.getMedianTimestampOfRecentBlocks(cursor, blockStore);
} catch (NullPointerException x) {
return;
}
// If producing the last 6 block took more than 12h, increase the difficulty
// target by 1/4 (which reduces the difficulty by 20%). This ensure the
// chain do not get stuck in case we lose hashrate abruptly.
if (mpt6blocks >= 12 * 3600) {
BigInteger nPow = storedPrev.getHeader().getDifficultyTargetAsInteger();
nPow = nPow.add(nPow.shiftRight(2));
if (nPow.compareTo(getMaxTarget()) > 0)
nPow = getMaxTarget();
if (nextBlock.getDifficultyTarget() != Utils.encodeCompactBits(nPow))
throw new VerificationException("Unexpected change in difficulty [6 blocks >12 hours] at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Utils.encodeCompactBits(nPow));
return;
}
// No ... so check the difficulty didn't actually change.
if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Long.toHexString(prev.getDifficultyTarget()));
return;
}
// We need to find a block far back in the chain. It's OK that this is expensive because it only occurs every
// two weeks after the initial block chain download.
final Stopwatch watch = Stopwatch.createStarted();
StoredBlock cursor = blockStore.get(prev.getHash());
for (int i = 0; i < this.getInterval() - 1; i++) {
if (cursor == null) {
// This should never happen. If it does, it means we are following an incorrect or busted chain.
throw new VerificationException("Difficulty transition point but we did not find a way back to the genesis block.");
}
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
watch.stop();
if (watch.elapsed(TimeUnit.MILLISECONDS) > 50)
log.info("Difficulty transition traversal took {}", watch);
Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
// Limit the adjustment step.
final int targetTimespan = this.getTargetTimespan();
if (timespan < targetTimespan / 4)
timespan = targetTimespan / 4;
if (timespan > targetTimespan * 4)
timespan = targetTimespan * 4;
BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget());
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan));
verifyDifficulty(newTarget, nextBlock);
/*if (newTarget.compareTo(this.getMaxTarget()) > 0) {
log.info("Difficulty hit proof of work limit: {}", newTarget.toString(16));
newTarget = this.getMaxTarget();
}
int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3;
long receivedTargetCompact = nextBlock.getDifficultyTarget();
// The calculated difficulty is to a higher precision than received, so reduce here.
BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
newTarget = newTarget.and(mask);
long newTargetCompact = Utils.encodeCompactBits(newTarget);
if (newTargetCompact != receivedTargetCompact)
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
*/
}
17
Source : BlockChainTest.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
private void testDeprecatedBlockVersion(final long deprecatedVersion, final long newVersion) throws Exception {
final BlockStore versionBlockStore = new MemoryBlockStore(UNITTEST);
final BlockChain versionChain = new BlockChain(UNITTEST, versionBlockStore);
// Build a historical chain of version 3 blocks
long timeSeconds = 1231006505;
int height = 0;
FakeTxBuilder.BlockPair chainHead = null;
// Put in just enough v2 blocks to be a minority
for (height = 0; height < (UNITTEST.getMajorityWindow() - UNITTEST.getMajorityRejectBlockOutdated()); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
// Fill the rest of the window with v3 blocks
for (; height < UNITTEST.getMajorityWindow(); height++) {
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, newVersion, timeSeconds, height);
versionChain.add(chainHead.block);
timeSeconds += 60;
}
chainHead = FakeTxBuilder.createFakeBlock(versionBlockStore, deprecatedVersion, timeSeconds, height);
// Trying to add a new v2 block should result in rejection
thrown.expect(VerificationException.BlockVersionOutOfDate.clreplaced);
try {
versionChain.add(chainHead.block);
} catch (final VerificationException ex) {
throw (Exception) ex.getCause();
}
}
15
Source : CheckpointManager.java
with Apache License 2.0
from Samourai-Wallet
with Apache License 2.0
from Samourai-Wallet
/**
* <p>Convenience method that creates a CheckpointManager, loads the given data, gets the checkpoint for the given
* time, then inserts it into the store and sets that to be the chain head. Useful when you have just created
* a new store from scratch and want to use configure it all in one go.</p>
*
* <p>Note that time is adjusted backwards by a week to account for possible clock drift in the block headers.</p>
*/
public static void checkpoint(NetworkParameters params, InputStream checkpoints, BlockStore store, long time) throws IOException, BlockStoreException {
checkNotNull(params);
checkNotNull(store);
checkArgument(!(store instanceof FullPrunedBlockStore), "You cannot use checkpointing with a full store.");
time -= 86400 * 7;
checkArgument(time > 0);
log.info("Attempting to initialize a new block store with a checkpoint for time {} ({})", time, Utils.dateTimeFormat(time * 1000));
BufferedInputStream stream = new BufferedInputStream(checkpoints);
CheckpointManager manager = new CheckpointManager(params, stream);
StoredBlock checkpoint = manager.getCheckpointBefore(time);
store.put(checkpoint);
store.setChainHead(checkpoint);
}
15
Source : CheckpointManager.java
with GNU General Public License v3.0
from bcmapp
with GNU General Public License v3.0
from bcmapp
/**
* <p>Convenience method that creates a CheckpointManager, loads the given data, gets the checkpoint for the given
* time, then inserts it into the store and sets that to be the chain head. Useful when you have just created
* a new store from scratch and want to use configure it all in one go.</p>
* <p>
* <p>Note that time is adjusted backwards by a week to account for possible clock drift in the block headers.</p>
*/
public static void checkpoint(NetworkParameters params, InputStream checkpoints, BlockStore store, long time) throws IOException, BlockStoreException {
checkNotNull(params);
checkNotNull(store);
checkArgument(!(store instanceof FullPrunedBlockStore), "You cannot use checkpointing with a full store.");
time -= 86400 * 7;
checkArgument(time > 0);
log.info("Attempting to initialize a new block store with a checkpoint for time {} ({})", time, Utils.dateTimeFormat(time * 1000));
BufferedInputStream stream = new BufferedInputStream(checkpoints);
CheckpointManager manager = new CheckpointManager(params, stream);
StoredBlock checkpoint = manager.getCheckpointBefore(time);
store.put(checkpoint);
store.setChainHead(checkpoint);
}
See More Examples