mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-11 17:55:53 +00:00
Added dependency on bitcoinj and removed code elements which should taken directly from bitcoinj.
Removed classes with no differences to bitcoinj. Fixed packages.
This commit is contained in:
parent
09c80d44ff
commit
c129d3fc89
109
core/pom.xml
109
core/pom.xml
@ -20,19 +20,19 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.dogecoin</groupId>
|
||||
<artifactId>dogecoinj-parent</artifactId>
|
||||
<groupId>org.altcoinj</groupId>
|
||||
<artifactId>altcoinj-parent</artifactId>
|
||||
<version>0.13-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>dogecoinj-core</artifactId>
|
||||
<artifactId>altcoinj-core</artifactId>
|
||||
|
||||
<name>dogecoinj</name>
|
||||
<description>A Java Dogecoin library</description>
|
||||
<name>altcoinj</name>
|
||||
<description>Extension library to bitcoinj to add altcoin support</description>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<url>https://github.com/langerhans/dogecoinj-new</url>
|
||||
<url>https://altcoinj.github.io</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
@ -45,8 +45,8 @@
|
||||
<!-- Dummy block to make Maven Central happy: authors list is in AUTHORS -->
|
||||
<developers>
|
||||
<developer>
|
||||
<name>The bitcoinj team. Port to Dogecoin by the Dogecoin developers</name>
|
||||
<email>info@dogecoin.com</email>
|
||||
<name>The altcoinj team</name>
|
||||
<email>ross@dogecoin.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
@ -136,6 +136,7 @@
|
||||
<link>http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/</link>
|
||||
</links>
|
||||
<detectJavaApiLink/>
|
||||
<quiet>true</quiet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@ -164,8 +165,10 @@
|
||||
<!-- Format is URN of groupId:artifactId:version:type:classifier:scope:hash -->
|
||||
<!-- classifier is "null" if not present -->
|
||||
<urns>
|
||||
<urn>com.dogecoin:orchid:1.1:jar:null:compile:393d53cad40f32f1c00a717326deeb4bde8ee57b</urn>
|
||||
<urn>cglib:cglib-nodep:2.2:jar:null:test:59afed7ab65e7ec6585d5bc60556c3cbd203532b</urn>
|
||||
<urn>com.fasterxml.jackson.core:jackson-annotations:2.4.0:jar:null:test:d6a66c7a5f01cf500377bd669507a08cfeba882a</urn>
|
||||
<urn>com.fasterxml.jackson.core:jackson-core:2.4.2:jar:null:test:ceb72830d95c512b4b300a38f29febc85bdf6e4b</urn>
|
||||
<urn>com.fasterxml.jackson.core:jackson-databind:2.4.2:jar:null:test:8e31266a272ad25ac4c089734d93e8d811652c1f</urn>
|
||||
<urn>com.google.code.findbugs:jsr305:2.0.1:jar:null:compile:516c03b21d50a644d538de0f0369c620989cd8f0</urn>
|
||||
<urn>com.google.guava:guava:16.0.1:jar:null:compile:5fa98cd1a63c99a44dd8d3b77e4762b066a5d0c5</urn>
|
||||
<urn>com.google.protobuf:protobuf-java:2.5.0:jar:null:compile:a10732c76bfacdbd633a7eb0f7968b1059a65dfa</urn>
|
||||
@ -173,31 +176,30 @@
|
||||
<urn>com.lambdaworks:scrypt:1.4.0:jar:null:compile:906506b74f30c8c20bccd9ed4a11112d8941fe87</urn>
|
||||
<urn>com.madgag.spongycastle:core:1.51.0.0:jar:null:compile:0f642963312ea0e615ad65f28adc5a5b3a2a0862</urn>
|
||||
<urn>junit:junit:4.11:jar:null:test:4e031bb61df09069aeb2bffb4019e7a5034a4ee0</urn>
|
||||
<urn>mysql:mysql-connector-java:5.1.33:jar:null:compile:8af455a9a3267e6664cafc87ace71a4e4ef02837</urn>
|
||||
<urn>net.jcip:jcip-annotations:1.0:jar:null:compile:afba4942caaeaf46aab0b976afd57cc7c181467e</urn>
|
||||
<urn>org.apache.maven.plugins:maven-clean-plugin:2.5:maven-plugin:null:runtime:75653decaefa85ca8114ff3a4f869bb2ee6d605d</urn>
|
||||
<urn>org.apache.maven.plugins:maven-compiler-plugin:3.1:maven-plugin:null:runtime:9977a8d04e75609cf01badc4eb6a9c7198c4c5ea</urn>
|
||||
<urn>org.apache.maven.plugins:maven-dependency-plugin:2.8:maven-plugin:null:runtime:04c8dedf3d9b2a3f45f3daa93e11ca547d2063ca</urn>
|
||||
<urn>org.apache.maven.plugins:maven-deploy-plugin:2.7:maven-plugin:null:runtime:6dadfb75679ca010b41286794f737088ebfe12fd</urn>
|
||||
<urn>org.apache.maven.plugins:maven-enforcer-plugin:1.2:maven-plugin:null:runtime:6b755a9a0d618f8f57c0b5c4a0737a012e710a46</urn>
|
||||
<urn>org.apache.maven.plugins:maven-install-plugin:2.5.1:maven-plugin:null:runtime:b6f5a4b621b9c26699c8deadb20fdc35ce568e35</urn>
|
||||
<urn>org.apache.maven.plugins:maven-jar-plugin:2.5:maven-plugin:null:runtime:344d667f5ec8b90d03d698d096a1147672fc522f</urn>
|
||||
<urn>org.apache.maven.plugins:maven-javadoc-plugin:2.9.1:maven-plugin:null:runtime:95ea7abf00e37e08bd927bf7e448c1e7fe4c6cb9</urn>
|
||||
<urn>org.apache.maven.plugins:maven-resources-plugin:2.6:maven-plugin:null:runtime:dd093ff6a4b680eae7ae83b5ab04310249fc6590</urn>
|
||||
<urn>org.apache.maven.plugins:maven-clean-plugin:2.6.1:maven-plugin:null:runtime:bfdf7d6c2f8fc8759457e9d54f458ba56ac7b30f</urn>
|
||||
<urn>org.apache.maven.plugins:maven-compiler-plugin:3.2:maven-plugin:null:runtime:aec10f274ac07fafab8906cb1aa69669d753b2c2</urn>
|
||||
<urn>org.apache.maven.plugins:maven-dependency-plugin:2.10:maven-plugin:null:runtime:af87ceeb71c6499147c5d27f74c9317bf707538e</urn>
|
||||
<urn>org.apache.maven.plugins:maven-deploy-plugin:2.8.2:maven-plugin:null:runtime:3c2d83ecd387e9843142ae92a0439792c1500319</urn>
|
||||
<urn>org.apache.maven.plugins:maven-enforcer-plugin:1.0:maven-plugin:null:runtime:ad032b7593576e9fe9305c73865633e163895b29</urn>
|
||||
<urn>org.apache.maven.plugins:maven-install-plugin:2.5.2:maven-plugin:null:runtime:8a67631619fc3c1d1f036e59362ddce71e1e496f</urn>
|
||||
<urn>org.apache.maven.plugins:maven-jar-plugin:2.6:maven-plugin:null:runtime:618f08d0fcdd3929af846ef1b65503b5904f93e3</urn>
|
||||
<urn>org.apache.maven.plugins:maven-javadoc-plugin:2.10.2:maven-plugin:null:runtime:5f391697fa85cecc7e5bac7ce5a6f9d056a58ba3</urn>
|
||||
<urn>org.apache.maven.plugins:maven-resources-plugin:2.7:maven-plugin:null:runtime:94af11389943a480ecec7db01b4ded1b9cdf57c5</urn>
|
||||
<urn>org.apache.maven.plugins:maven-shade-plugin:2.3:maven-plugin:null:runtime:d136adc7abccc9c12adcad6ae7a9bc51b2b7184b</urn>
|
||||
<urn>org.apache.maven.plugins:maven-site-plugin:3.3:maven-plugin:null:runtime:77ba1752b1ac4c4339d6f11554800960a56a4ae1</urn>
|
||||
<urn>org.apache.maven.plugins:maven-source-plugin:2.1.2:maven-plugin:null:runtime:35154aa8e6e0e84c2b5c10c3d5220d65670ba984</urn>
|
||||
<urn>org.apache.maven.plugins:maven-surefire-plugin:2.12.4:maven-plugin:null:runtime:2b435f7f77777d2e62354fdc690da3f1dc47a26b</urn>
|
||||
<urn>org.codehaus.mojo:cobertura-maven-plugin:2.6:maven-plugin:null:runtime:5204735a0642b42f5647d8ec876d4301e328c0d5</urn>
|
||||
<urn>org.apache.maven.plugins:maven-site-plugin:3.4:maven-plugin:null:runtime:659cd5f1dd8bff554cf52603339494cbf7f283c5</urn>
|
||||
<urn>org.apache.maven.plugins:maven-source-plugin:2.4:maven-plugin:null:runtime:46f0d7f7823d729ba300d3f8929900c7e9cb5ac0</urn>
|
||||
<urn>org.apache.maven.plugins:maven-surefire-plugin:2.18.1:maven-plugin:null:runtime:402fd3066fd6d85ea4a1a3e7cd82a7e35037e6e8</urn>
|
||||
<urn>org.easymock:easymock:3.0:jar:null:test:f28a4c31c330f95c9acbf1108cea19952b5c496f</urn>
|
||||
<urn>org.eluder.coveralls:coveralls-maven-plugin:3.1.0:maven-plugin:null:runtime:ca9d2915e2b1e99f15c9f54ad653eda893d42a69</urn>
|
||||
<urn>org.hamcrest:hamcrest-core:1.3:jar:null:test:42a25dc3219429f0e5d060061f71acb49bf010a0</urn>
|
||||
<urn>org.jacoco:jacoco-maven-plugin:0.7.4.201502262128:maven-plugin:null:runtime:ee12ed04db135c74d0ae99e9c4e4754ee1582edb</urn>
|
||||
<urn>org.objenesis:objenesis:1.2:jar:null:test:bfcb0539a071a4c5a30690388903ac48c0667f2a</urn>
|
||||
<urn>org.slf4j:slf4j-api:1.7.6:jar:null:compile:562424e36df3d2327e8e9301a76027fca17d54ea</urn>
|
||||
<urn>org.slf4j:slf4j-jdk14:1.7.6:jar:null:test:1a3301a32ea7d90c3d33e9d60edbfdc9589fc748</urn>
|
||||
<url>com.fasterxml.jackson.core:jackson-databind:2.4.2:jar:null:test:8e31266a272ad25ac4c089734d93e8d811652c1f</url>
|
||||
<url>com.fasterxml.jackson.core:jackson-core:2.4.2:jar:null:test:ceb72830d95c512b4b300a38f29febc85bdf6e4b</url>
|
||||
<url>com.fasterxml.jackson.core:jackson-annotations:2.4.2:jar:null:test:6bb52af09372d5064206d47d7887d41671f00f7d</url>
|
||||
<urn>org.jacoco:jacoco-maven-plugin:0.7.2.201409121644:maven-plugin:null:runtime:b2cb310459d082db505fdfa66dbadd4d8bac8e34</urn>
|
||||
<urn>org.eluder.coveralls:coveralls-maven-plugin:3.0.1:maven-plugin:null:runtime:3907ee5cf1e5c85af7bb90e486ce4c7b1408a552</urn>
|
||||
<urn>org.sonatype.plugins:nexus-staging-maven-plugin:1.6.5:maven-plugin:null:runtime:455ca2aa8cd14a06608f1538bd6a1efd09561563</urn>
|
||||
<urn>postgresql:postgresql:9.1-901.jdbc4:jar:null:compile:153f2f92a786f12fc111d0111f709012df87c808</urn>
|
||||
<!-- A check for the rules themselves -->
|
||||
<urn>uk.co.froot.maven.enforcer:digest-enforcer-rules:0.0.1:jar:null:runtime:16a9e04f3fe4bb143c42782d07d5faf65b32106f</urn>
|
||||
</urns>
|
||||
@ -254,7 +256,7 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.7.2.201409121644</version>
|
||||
<version>0.7.4.201502262128</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/Protos*.class</exclude> <!-- Exclude generated protobuf classes -->
|
||||
@ -298,17 +300,17 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<argLine>${surefireArgLine}</argLine> <!-- This is required for code coverage to work. -->
|
||||
<runOrder>alphabetical</runOrder>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Coveralls is a online code coverage tool, you can track code coverage here: https://coveralls.io/r/bitcoinj/bitcoinj -->
|
||||
<!-- Coveralls is a online code coverage tool, you can track code coverage here: https://coveralls.io/r/altcoinj/altcoinj -->
|
||||
<plugin>
|
||||
<groupId>org.eluder.coveralls</groupId>
|
||||
<artifactId>coveralls-maven-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
|
||||
<!-- Create a bundled executable test jar that runs the regtester/pulltester.
|
||||
@ -334,8 +336,8 @@
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<outputDirectory>target/test-classes/</outputDirectory>
|
||||
<groupId>com.dogecoin</groupId>
|
||||
<artifactId>dogecoinj-core</artifactId>
|
||||
<groupId>org.altcoinj</groupId>
|
||||
<artifactId>altcoinj-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
@ -356,32 +358,6 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.dogecoin.dogecoinj.core.BitcoindComparisonTool</mainClass>
|
||||
<addClasspath>false</addClasspath>
|
||||
</manifest>
|
||||
</archive>
|
||||
<finalName>pull</finalName> <!-- becomes pull-tests.jar on disk (hacky) -->
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@ -398,7 +374,7 @@
|
||||
<version>3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- dogecoinj consumers are expected to provide their own SLF4J adapters
|
||||
<!-- altcoinj consumers are expected to provide their own SLF4J adapters
|
||||
such as logback, slf4j-log4j12, slf4j-jcl and so on
|
||||
see http://www.slf4j.org/faq.html -->
|
||||
<dependency>
|
||||
@ -466,10 +442,21 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dogecoin</groupId>
|
||||
<groupId>org.fusesource.leveldbjni</groupId>
|
||||
<artifactId>leveldbjni-all</artifactId>
|
||||
<version>1.8</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitcoinj</groupId>
|
||||
<artifactId>orchid</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitcoinj</groupId>
|
||||
<artifactId>bitcoinj-core</artifactId>
|
||||
<version>0.13-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default no-op implementation of {@link BlockChainListener}.
|
||||
*/
|
||||
public class AbstractBlockChainListener implements BlockChainListener {
|
||||
@Override
|
||||
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransactionRelevant(Transaction tx) throws ScriptException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType,
|
||||
int relativityOffset) throws VerificationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, BlockChain.NewBlockType blockType,
|
||||
int relativityOffset) throws VerificationException {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Convenience implementation of {@link PeerEventListener}.
|
||||
*/
|
||||
public class AbstractPeerEventListener implements PeerEventListener {
|
||||
@Override
|
||||
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerConnected(Peer peer, int peerCount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(Peer peer, int peerCount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message onPreMessageReceived(Peer peer, Message m) {
|
||||
// Just pass the message right through for further processing.
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransaction(Peer peer, Transaction t) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Message> getData(Peer peer, GetDataMessage m) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.wallet.AbstractKeyChainEventListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Convenience implementation of {@link WalletEventListener}.
|
||||
*/
|
||||
public abstract class AbstractWalletEventListener extends AbstractKeyChainEventListener implements WalletEventListener {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(List<ECKey> keys) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsChanged(Wallet wallet, List<Script> scripts, boolean isAddingScripts) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet) {
|
||||
onChange();
|
||||
}
|
||||
|
||||
public void onChange() {
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Giannis Dzegoutanis
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.Networks;
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* <p>A Bitcoin address looks like 1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy and is derived from an elliptic curve public key
|
||||
* plus a set of network parameters. Not to be confused with a {@link PeerAddress} or {@link AddressMessage}
|
||||
* which are about network (TCP) addresses.</p>
|
||||
*
|
||||
* <p>A standard address is built by taking the RIPE-MD160 hash of the public key bytes, with a version prefix and a
|
||||
* checksum suffix, then encoding it textually as base58. The version prefix is used to both denote the network for
|
||||
* which the address is valid (see {@link NetworkParameters}, and also to indicate how the bytes inside the address
|
||||
* should be interpreted. Whilst almost all addresses today are hashes of public keys, another (currently unsupported
|
||||
* type) can contain a hash of a script instead.</p>
|
||||
*/
|
||||
public class Address extends VersionedChecksummedBytes {
|
||||
/**
|
||||
* An address is a RIPEMD160 hash of a public key, therefore is always 160 bits or 20 bytes.
|
||||
*/
|
||||
public static final int LENGTH = 20;
|
||||
|
||||
transient final NetworkParameters params;
|
||||
|
||||
/**
|
||||
* Construct an address from parameters, the address version, and the hash160 form. Example:<p>
|
||||
*
|
||||
* <pre>new Address(MainNetParams.get(), NetworkParameters.getAddressHeader(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
|
||||
*/
|
||||
public Address(NetworkParameters params, int version, byte[] hash160) throws WrongNetworkException {
|
||||
super(version, hash160);
|
||||
checkNotNull(params);
|
||||
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
|
||||
if (!isAcceptableVersion(params, version))
|
||||
throw new WrongNetworkException(version, params.getAcceptableAddressCodes());
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/** Returns an Address that represents the given P2SH script hash. */
|
||||
public static Address fromP2SHHash(NetworkParameters params, byte[] hash160) {
|
||||
try {
|
||||
return new Address(params, params.getP2SHHeader(), hash160);
|
||||
} catch (WrongNetworkException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns an Address that represents the script hash extracted from the given scriptPubKey */
|
||||
public static Address fromP2SHScript(NetworkParameters params, Script scriptPubKey) {
|
||||
checkArgument(scriptPubKey.isPayToScriptHash(), "Not a P2SH script");
|
||||
return fromP2SHHash(params, scriptPubKey.getPubKeyHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an address from parameters and the hash160 form. Example:<p>
|
||||
*
|
||||
* <pre>new Address(MainNetParams.get(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
|
||||
*/
|
||||
public Address(NetworkParameters params, byte[] hash160) {
|
||||
super(params.getAddressHeader(), hash160);
|
||||
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an address from parameters and the standard "human readable" form. Example:<p>
|
||||
*
|
||||
* <pre>new Address(MainNetParams.get(), "17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL");</pre><p>
|
||||
*
|
||||
* @param params The expected NetworkParameters or null if you don't want validation.
|
||||
* @param address The textual form of the address, such as "17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL"
|
||||
* @throws AddressFormatException if the given address doesn't parse or the checksum is invalid
|
||||
* @throws WrongNetworkException if the given address is valid but for a different chain (eg testnet vs mainnet)
|
||||
*/
|
||||
public Address(@Nullable NetworkParameters params, String address) throws AddressFormatException {
|
||||
super(address);
|
||||
if (params != null) {
|
||||
if (!isAcceptableVersion(params, version)) {
|
||||
throw new WrongNetworkException(version, params.getAcceptableAddressCodes());
|
||||
}
|
||||
this.params = params;
|
||||
} else {
|
||||
NetworkParameters paramsFound = null;
|
||||
for (NetworkParameters p : Networks.get()) {
|
||||
if (isAcceptableVersion(p, version)) {
|
||||
paramsFound = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (paramsFound == null)
|
||||
throw new AddressFormatException("No network found for " + address);
|
||||
|
||||
this.params = paramsFound;
|
||||
}
|
||||
}
|
||||
|
||||
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
|
||||
public byte[] getHash160() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this address is a Pay-To-Script-Hash (P2SH) address.
|
||||
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
|
||||
*/
|
||||
public boolean isP2SHAddress() {
|
||||
final NetworkParameters parameters = getParameters();
|
||||
return parameters != null && this.version == parameters.p2shHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the version byte of the address and attempts to find a matching NetworkParameters. If you aren't sure
|
||||
* which network the address is intended for (eg, it was provided by a user), you can use this to decide if it is
|
||||
* compatible with the current wallet. You should be able to handle a null response from this method. Note that the
|
||||
* parameters returned is not necessarily the same as the one the Address was created with.
|
||||
*
|
||||
* @return a NetworkParameters representing the network the address is intended for.
|
||||
*/
|
||||
public NetworkParameters getParameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an address, examines the version byte and attempts to find a matching NetworkParameters. If you aren't sure
|
||||
* which network the address is intended for (eg, it was provided by a user), you can use this to decide if it is
|
||||
* compatible with the current wallet.
|
||||
* @return a NetworkParameters of the address
|
||||
* @throws AddressFormatException if the string wasn't of a known version
|
||||
*/
|
||||
public static NetworkParameters getParametersFromAddress(String address) throws AddressFormatException {
|
||||
try {
|
||||
return new Address(null, address).getParameters();
|
||||
} catch (WrongNetworkException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given address version is valid given the NetworkParameters.
|
||||
*/
|
||||
private static boolean isAcceptableVersion(NetworkParameters params, int version) {
|
||||
for (int v : params.getAcceptableAddressCodes()) {
|
||||
if (version == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation narrows the return type to <code>Address</code>.
|
||||
*/
|
||||
@Override
|
||||
public Address clone() throws CloneNotSupportedException {
|
||||
return (Address) super.clone();
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class AddressFormatException extends Exception {
|
||||
public AddressFormatException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public AddressFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents an "addr" message on the P2P network, which contains broadcast IP addresses of other peers. This is
|
||||
* one of the ways peers can find each other without using the DNS or IRC discovery mechanisms. However storing and
|
||||
* using addr messages is not presently implemented.
|
||||
*/
|
||||
public class AddressMessage extends Message {
|
||||
private static final long serialVersionUID = 8058283864924679460L;
|
||||
private static final long MAX_ADDRESSES = 1024;
|
||||
private List<PeerAddress> addresses;
|
||||
private transient long numAddresses = -1;
|
||||
|
||||
/**
|
||||
* Contruct a new 'addr' message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
AddressMessage(NetworkParameters params, byte[] payload, int offset, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
|
||||
super(params, payload, offset, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct a new 'addr' message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
AddressMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
|
||||
super(params, payload, 0, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
AddressMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
|
||||
super(params, payload, offset, false, false, UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
AddressMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0, false, false, UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
numAddresses = readVarInt();
|
||||
// Guard against ultra large messages that will crash us.
|
||||
if (numAddresses > MAX_ADDRESSES)
|
||||
throw new ProtocolException("Address message too large.");
|
||||
addresses = new ArrayList<PeerAddress>((int) numAddresses);
|
||||
for (int i = 0; i < numAddresses; i++) {
|
||||
PeerAddress addr = new PeerAddress(params, payload, cursor, protocolVersion, this, parseLazy, parseRetain);
|
||||
addresses.add(addr);
|
||||
cursor += addr.getMessageSize();
|
||||
}
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#bitcoinSerializeToStream(java.io.OutputStream)
|
||||
*/
|
||||
@Override
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
if (addresses == null)
|
||||
return;
|
||||
stream.write(new VarInt(addresses.size()).encode());
|
||||
for (PeerAddress addr : addresses) {
|
||||
addr.bitcoinSerialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessageSize() {
|
||||
if (length != UNKNOWN_LENGTH)
|
||||
return length;
|
||||
if (addresses != null) {
|
||||
length = new VarInt(addresses.size()).getSizeInBytes();
|
||||
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
|
||||
length += addresses.size() * (protocolVersion > 31402 ? PeerAddress.MESSAGE_SIZE : PeerAddress.MESSAGE_SIZE - 4);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* AddressMessage cannot cache checksum in non-retain mode due to dynamic time being used.
|
||||
*/
|
||||
@Override
|
||||
void setChecksum(byte[] checksum) {
|
||||
if (parseRetain)
|
||||
super.setChecksum(checksum);
|
||||
else
|
||||
this.checksum = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An unmodifiableList view of the backing List of addresses. Addresses contained within the list may be safely modified.
|
||||
*/
|
||||
public List<PeerAddress> getAddresses() {
|
||||
maybeParse();
|
||||
return Collections.unmodifiableList(addresses);
|
||||
}
|
||||
|
||||
public void addAddress(PeerAddress address) {
|
||||
unCache();
|
||||
maybeParse();
|
||||
address.setParent(this);
|
||||
addresses.add(address);
|
||||
if (length == UNKNOWN_LENGTH)
|
||||
getMessageSize();
|
||||
else
|
||||
length += address.getMessageSize();
|
||||
}
|
||||
|
||||
public void removeAddress(int index) {
|
||||
unCache();
|
||||
PeerAddress address = addresses.remove(index);
|
||||
address.setParent(null);
|
||||
if (length == UNKNOWN_LENGTH)
|
||||
getMessageSize();
|
||||
else
|
||||
length -= address.getMessageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("addr: ");
|
||||
for (PeerAddress a : addresses) {
|
||||
builder.append(a.toString());
|
||||
builder.append(" ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Alerts are signed messages that are broadcast on the peer-to-peer network if they match a hard-coded signing key.
|
||||
* The private keys are held by a small group of core Bitcoin developers, and alerts may be broadcast in the event of
|
||||
* an available upgrade or a serious network problem. Alerts have an expiration time, data that specifies what
|
||||
* set of software versions it matches and the ability to cancel them by broadcasting another type of alert.<p>
|
||||
*
|
||||
* The right course of action on receiving an alert is usually to either ensure a human will see it (display on screen,
|
||||
* log, email), or if you decide to use alerts for notifications that are specific to your app in some way, to parse it.
|
||||
* For example, you could treat it as an upgrade notification specific to your app. Satoshi designed alerts to ensure
|
||||
* that software upgrades could be distributed independently of a hard-coded website, in order to allow everything to
|
||||
* be purely peer-to-peer. You don't have to use this of course, and indeed it often makes more sense not to.<p>
|
||||
*
|
||||
* Before doing anything with an alert, you should check {@link AlertMessage#isSignatureValid()}.
|
||||
*/
|
||||
public class AlertMessage extends Message {
|
||||
private byte[] content;
|
||||
private byte[] signature;
|
||||
|
||||
// See the getters for documentation of what each field means.
|
||||
private long version = 1;
|
||||
private Date relayUntil;
|
||||
private Date expiration;
|
||||
private long id;
|
||||
private long cancel;
|
||||
private Set<Long> cancelSet;
|
||||
private long minVer, maxVer;
|
||||
private Set<String> matchingSubVers;
|
||||
private long priority;
|
||||
private String comment, statusBar, reserved;
|
||||
|
||||
// Chosen arbitrarily to avoid memory blowups.
|
||||
private static final long MAX_SET_SIZE = 100;
|
||||
|
||||
public AlertMessage(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ALERT: " + getStatusBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
// Alerts are formatted in two levels. The top level contains two byte arrays: a signature, and a serialized
|
||||
// data structure containing the actual alert data.
|
||||
int startPos = cursor;
|
||||
content = readByteArray();
|
||||
signature = readByteArray();
|
||||
// Now we need to parse out the contents of the embedded structure. Rewind back to the start of the message.
|
||||
cursor = startPos;
|
||||
readVarInt(); // Skip the length field on the content array.
|
||||
// We're inside the embedded structure.
|
||||
version = readUint32();
|
||||
// Read the timestamps. Bitcoin uses seconds since the epoch.
|
||||
relayUntil = new Date(readUint64().longValue() * 1000);
|
||||
expiration = new Date(readUint64().longValue() * 1000);
|
||||
id = readUint32();
|
||||
cancel = readUint32();
|
||||
// Sets are serialized as <len><item><item><item>....
|
||||
long cancelSetSize = readVarInt();
|
||||
if (cancelSetSize < 0 || cancelSetSize > MAX_SET_SIZE) {
|
||||
throw new ProtocolException("Bad cancel set size: " + cancelSetSize);
|
||||
}
|
||||
// Using a hashset here is very inefficient given that this will normally be only one item. But Java doesn't
|
||||
// make it easy to do better. What we really want is just an array-backed set.
|
||||
cancelSet = new HashSet<Long>((int)cancelSetSize);
|
||||
for (long i = 0; i < cancelSetSize; i++) {
|
||||
cancelSet.add(readUint32());
|
||||
}
|
||||
minVer = readUint32();
|
||||
maxVer = readUint32();
|
||||
// Read the subver matching set.
|
||||
long subverSetSize = readVarInt();
|
||||
if (subverSetSize < 0 || subverSetSize > MAX_SET_SIZE) {
|
||||
throw new ProtocolException("Bad subver set size: " + subverSetSize);
|
||||
}
|
||||
matchingSubVers = new HashSet<String>((int)subverSetSize);
|
||||
for (long i = 0; i < subverSetSize; i++) {
|
||||
matchingSubVers.add(readStr());
|
||||
}
|
||||
priority = readUint32();
|
||||
comment = readStr();
|
||||
statusBar = readStr();
|
||||
reserved = readStr();
|
||||
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the digital signature attached to the message verifies. Don't do anything with the alert if it
|
||||
* doesn't verify, because that would allow arbitrary attackers to spam your users.
|
||||
*/
|
||||
public boolean isSignatureValid() {
|
||||
return ECKey.verify(Utils.doubleDigest(content), signature, params.getAlertSigningKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Do nothing, lazy parsing isn't useful for alerts.
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Field accessors.
|
||||
|
||||
/**
|
||||
* The time at which the alert should stop being broadcast across the network. Note that you can still receive
|
||||
* the alert after this time from other nodes if the alert still applies to them or to you.
|
||||
*/
|
||||
public Date getRelayUntil() {
|
||||
return relayUntil;
|
||||
}
|
||||
|
||||
public void setRelayUntil(Date relayUntil) {
|
||||
this.relayUntil = relayUntil;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time at which the alert ceases to be relevant. It should not be presented to the user or app administrator
|
||||
* after this time.
|
||||
*/
|
||||
public Date getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(Date expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* The numeric identifier of this alert. Each alert should have a unique ID, but the signer can choose any number.
|
||||
* If an alert is broadcast with a cancel field higher than this ID, this alert is considered cancelled.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* A marker that results in any alerts with an ID lower than this value to be considered cancelled.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getCancel() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public void setCancel(long cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inclusive lower bound on software versions that are considered for the purposes of this alert. The Satoshi
|
||||
* client compares this against a protocol version field, but as long as the subVer field is used to restrict it your
|
||||
* alerts could use any version numbers.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getMinVer() {
|
||||
return minVer;
|
||||
}
|
||||
|
||||
public void setMinVer(long minVer) {
|
||||
this.minVer = minVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inclusive upper bound on software versions considered for the purposes of this alert. The Satoshi
|
||||
* client compares this against a protocol version field, but as long as the subVer field is used to restrict it your
|
||||
* alerts could use any version numbers.
|
||||
* @return
|
||||
*/
|
||||
public long getMaxVer() {
|
||||
return maxVer;
|
||||
}
|
||||
|
||||
public void setMaxVer(long maxVer) {
|
||||
this.maxVer = maxVer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an integer ordering amongst simultaneously active alerts.
|
||||
* @return uint32
|
||||
*/
|
||||
public long getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(long priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field is unused. It is presumably intended for the author of the alert to provide a justification for it
|
||||
* visible to protocol developers but not users.
|
||||
*/
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that is intended to display in the status bar of the official GUI client. It contains the user-visible
|
||||
* message. English only.
|
||||
*/
|
||||
public String getStatusBar() {
|
||||
return statusBar;
|
||||
}
|
||||
|
||||
public void setStatusBar(String statusBar) {
|
||||
this.statusBar = statusBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field is never used.
|
||||
*/
|
||||
public String getReserved() {
|
||||
return reserved;
|
||||
}
|
||||
|
||||
public void setReserved(String reserved) {
|
||||
this.reserved = reserved;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This is the code to deserialize the AuxPoW header data
|
||||
*/
|
||||
public class AuxPoWMessage extends Message {
|
||||
private static final Logger log = LoggerFactory.getLogger(AuxPoWMessage.class);
|
||||
|
||||
private AuxHeader header;
|
||||
|
||||
public AuxPoWMessage(byte[] payload, int cursor) throws ProtocolException {
|
||||
this.payload = payload;
|
||||
this.cursor = cursor;
|
||||
this.header = new AuxHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
header.parentCoinbaseVerion = readUint32();
|
||||
header.parentCoinbaseTxInCount = readVarInt();
|
||||
header.parentCointbasePrevOut = readBytes(36); // Always the same on coinbase
|
||||
header.parentCoinbaseInScriptLength = readVarInt();
|
||||
header.parentCoinbaseInScript = readBytes((int) header.parentCoinbaseInScriptLength); // Script length is limited so this cast should be fine.
|
||||
header.parentCoinBaseSequenceNumber = readUint32();
|
||||
header.parentCoinbaseTxOutCount = readVarInt();
|
||||
header.parentCoinbaseOuts = new ArrayList<AuxCoinbaseOut>();
|
||||
for (int i = 0; i < header.parentCoinbaseTxOutCount; i++) {
|
||||
AuxCoinbaseOut out = new AuxCoinbaseOut();
|
||||
out.amount = readInt64();
|
||||
out.scriptLength = readVarInt();
|
||||
out.script = readBytes((int) out.scriptLength); // Script length is limited so this cast should be fine.
|
||||
header.parentCoinbaseOuts.add(out);
|
||||
}
|
||||
header.parentCoinbaseLockTime = readUint32();
|
||||
|
||||
header.parentBlockHeaderHash = readHash();
|
||||
header.numOfCoinbaseLinks = readVarInt();
|
||||
header.coinbaseLinks = new ArrayList<Sha256Hash>();
|
||||
for (int i = 0; i < header.numOfCoinbaseLinks; i++) {
|
||||
header.coinbaseLinks.add(readHash());
|
||||
}
|
||||
header.coinbaseBranchBitmask = readUint32();
|
||||
|
||||
header.numOfAuxChainLinks = readVarInt();
|
||||
header.auxChainLinks = new ArrayList<Sha256Hash>();
|
||||
for (int i = 0; i < header.numOfAuxChainLinks; i++) {
|
||||
header.auxChainLinks.add(readHash());
|
||||
}
|
||||
header.auxChainBranchBitmask = readUint32();
|
||||
|
||||
header.parentBlockVersion = readUint32();
|
||||
header.parentBlockPrev = readHash();
|
||||
header.parentBlockMerkleRoot = readHash();
|
||||
header.parentBlockTime = readUint32();
|
||||
header.parentBlockBits = readUint32();
|
||||
header.parentBlockNonce = readUint32();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// noop
|
||||
}
|
||||
|
||||
public byte[] constructParentHeader() {
|
||||
|
||||
ByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(Block.HEADER_SIZE);
|
||||
try {
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockVersion, stream);
|
||||
stream.write(Utils.reverseBytes(header.parentBlockPrev.getBytes()));
|
||||
stream.write(Utils.reverseBytes(header.parentBlockMerkleRoot.getBytes()));
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockTime, stream);
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockBits, stream);
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockNonce, stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(); // Can't actually happen
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public class AuxHeader {
|
||||
|
||||
// Parent coinbase
|
||||
public long parentCoinbaseVerion;
|
||||
public long parentCoinbaseTxInCount;
|
||||
public byte[] parentCointbasePrevOut;
|
||||
public long parentCoinbaseInScriptLength;
|
||||
public byte[] parentCoinbaseInScript;
|
||||
public long parentCoinBaseSequenceNumber;
|
||||
public long parentCoinbaseTxOutCount;
|
||||
public ArrayList<AuxCoinbaseOut> parentCoinbaseOuts;
|
||||
public long parentCoinbaseLockTime;
|
||||
|
||||
// Coinbase link
|
||||
public Sha256Hash parentBlockHeaderHash;
|
||||
public long numOfCoinbaseLinks;
|
||||
public ArrayList<Sha256Hash> coinbaseLinks;
|
||||
public long coinbaseBranchBitmask;
|
||||
|
||||
// Aux chanin link
|
||||
public long numOfAuxChainLinks;
|
||||
public ArrayList<Sha256Hash> auxChainLinks;
|
||||
public long auxChainBranchBitmask;
|
||||
|
||||
// Parent block header
|
||||
public long parentBlockVersion;
|
||||
public Sha256Hash parentBlockPrev;
|
||||
public Sha256Hash parentBlockMerkleRoot;
|
||||
public long parentBlockTime;
|
||||
public long parentBlockBits;
|
||||
public long parentBlockNonce;
|
||||
}
|
||||
|
||||
public class AuxCoinbaseOut {
|
||||
public long amount;
|
||||
public long scriptLength;
|
||||
public byte[] script;
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>Base58 is a way to encode Bitcoin addresses as numbers and letters. Note that this is not the same base58 as used by
|
||||
* Flickr, which you may see reference to around the internet.</p>
|
||||
*
|
||||
* <p>You may instead wish to work with {@link VersionedChecksummedBytes}, which adds support for testing the prefix
|
||||
* and suffix bytes commonly found in addresses.</p>
|
||||
*
|
||||
* <p>Satoshi says: why base-58 instead of standard base-64 encoding?<p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>Don't want 0OIl characters that look the same in some fonts and
|
||||
* could be used to create visually identical looking account numbers.</li>
|
||||
* <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
|
||||
* <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
|
||||
* <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Base58 {
|
||||
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
|
||||
|
||||
private static final int[] INDEXES = new int[128];
|
||||
static {
|
||||
for (int i = 0; i < INDEXES.length; i++) {
|
||||
INDEXES[i] = -1;
|
||||
}
|
||||
for (int i = 0; i < ALPHABET.length; i++) {
|
||||
INDEXES[ALPHABET[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/** Encodes the given bytes in base58. No checksum is appended. */
|
||||
public static String encode(byte[] input) {
|
||||
if (input.length == 0) {
|
||||
return "";
|
||||
}
|
||||
input = copyOfRange(input, 0, input.length);
|
||||
// Count leading zeroes.
|
||||
int zeroCount = 0;
|
||||
while (zeroCount < input.length && input[zeroCount] == 0) {
|
||||
++zeroCount;
|
||||
}
|
||||
// The actual encoding.
|
||||
byte[] temp = new byte[input.length * 2];
|
||||
int j = temp.length;
|
||||
|
||||
int startAt = zeroCount;
|
||||
while (startAt < input.length) {
|
||||
byte mod = divmod58(input, startAt);
|
||||
if (input[startAt] == 0) {
|
||||
++startAt;
|
||||
}
|
||||
temp[--j] = (byte) ALPHABET[mod];
|
||||
}
|
||||
|
||||
// Strip extra '1' if there are some after decoding.
|
||||
while (j < temp.length && temp[j] == ALPHABET[0]) {
|
||||
++j;
|
||||
}
|
||||
// Add as many leading '1' as there were leading zeros.
|
||||
while (--zeroCount >= 0) {
|
||||
temp[--j] = (byte) ALPHABET[0];
|
||||
}
|
||||
|
||||
byte[] output = copyOfRange(temp, j, temp.length);
|
||||
try {
|
||||
return new String(output, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decode(String input) throws AddressFormatException {
|
||||
if (input.length() == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] input58 = new byte[input.length()];
|
||||
// Transform the String to a base58 byte sequence
|
||||
for (int i = 0; i < input.length(); ++i) {
|
||||
char c = input.charAt(i);
|
||||
|
||||
int digit58 = -1;
|
||||
if (c >= 0 && c < 128) {
|
||||
digit58 = INDEXES[c];
|
||||
}
|
||||
if (digit58 < 0) {
|
||||
throw new AddressFormatException("Illegal character " + c + " at " + i);
|
||||
}
|
||||
|
||||
input58[i] = (byte) digit58;
|
||||
}
|
||||
// Count leading zeroes
|
||||
int zeroCount = 0;
|
||||
while (zeroCount < input58.length && input58[zeroCount] == 0) {
|
||||
++zeroCount;
|
||||
}
|
||||
// The encoding
|
||||
byte[] temp = new byte[input.length()];
|
||||
int j = temp.length;
|
||||
|
||||
int startAt = zeroCount;
|
||||
while (startAt < input58.length) {
|
||||
byte mod = divmod256(input58, startAt);
|
||||
if (input58[startAt] == 0) {
|
||||
++startAt;
|
||||
}
|
||||
|
||||
temp[--j] = mod;
|
||||
}
|
||||
// Do no add extra leading zeroes, move j to first non null byte.
|
||||
while (j < temp.length && temp[j] == 0) {
|
||||
++j;
|
||||
}
|
||||
|
||||
return copyOfRange(temp, j - zeroCount, temp.length);
|
||||
}
|
||||
|
||||
public static BigInteger decodeToBigInteger(String input) throws AddressFormatException {
|
||||
return new BigInteger(1, decode(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the checksum in the last 4 bytes of the decoded data to verify the rest are correct. The checksum is
|
||||
* removed from the returned data.
|
||||
*
|
||||
* @throws AddressFormatException if the input is not base 58 or the checksum does not validate.
|
||||
*/
|
||||
public static byte[] decodeChecked(String input) throws AddressFormatException {
|
||||
byte tmp [] = decode(input);
|
||||
if (tmp.length < 4)
|
||||
throw new AddressFormatException("Input too short");
|
||||
byte[] bytes = copyOfRange(tmp, 0, tmp.length - 4);
|
||||
byte[] checksum = copyOfRange(tmp, tmp.length - 4, tmp.length);
|
||||
|
||||
tmp = Utils.doubleDigest(bytes);
|
||||
byte[] hash = copyOfRange(tmp, 0, 4);
|
||||
if (!Arrays.equals(checksum, hash))
|
||||
throw new AddressFormatException("Checksum does not validate");
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
//
|
||||
// number -> number / 58, returns number % 58
|
||||
//
|
||||
private static byte divmod58(byte[] number, int startAt) {
|
||||
int remainder = 0;
|
||||
for (int i = startAt; i < number.length; i++) {
|
||||
int digit256 = (int) number[i] & 0xFF;
|
||||
int temp = remainder * 256 + digit256;
|
||||
|
||||
number[i] = (byte) (temp / 58);
|
||||
|
||||
remainder = temp % 58;
|
||||
}
|
||||
|
||||
return (byte) remainder;
|
||||
}
|
||||
|
||||
//
|
||||
// number -> number / 256, returns number % 256
|
||||
//
|
||||
private static byte divmod256(byte[] number58, int startAt) {
|
||||
int remainder = 0;
|
||||
for (int i = startAt; i < number58.length; i++) {
|
||||
int digit58 = (int) number58[i] & 0xFF;
|
||||
int temp = remainder * 58 + digit58;
|
||||
|
||||
number58[i] = (byte) (temp / 256);
|
||||
|
||||
remainder = temp % 256;
|
||||
}
|
||||
|
||||
return (byte) remainder;
|
||||
}
|
||||
|
||||
private static byte[] copyOfRange(byte[] source, int from, int to) {
|
||||
byte[] range = new byte[to - from];
|
||||
System.arraycopy(source, from, range, 0, range.length);
|
||||
|
||||
return range;
|
||||
}
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.*;
|
||||
|
||||
/**
|
||||
* <p>Methods to serialize and de-serialize messages to the Bitcoin network format as defined in
|
||||
* <a href="https://en.bitcoin.it/wiki/Protocol_specification">the protocol specification</a>.</p>
|
||||
*
|
||||
* <p>To be able to serialize and deserialize new Message subclasses the following criteria needs to be met.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>The proper Class instance needs to be mapped to its message name in the names variable below</li>
|
||||
* <li>There needs to be a constructor matching: NetworkParameters params, byte[] payload</li>
|
||||
* <li>Message.bitcoinSerializeToStream() needs to be properly subclassed</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class BitcoinSerializer {
|
||||
private static final Logger log = LoggerFactory.getLogger(BitcoinSerializer.class);
|
||||
private static final int COMMAND_LEN = 12;
|
||||
|
||||
private NetworkParameters params;
|
||||
private boolean parseLazy = false;
|
||||
private boolean parseRetain = false;
|
||||
|
||||
private static Map<Class<? extends Message>, String> names = new HashMap<Class<? extends Message>, String>();
|
||||
|
||||
static {
|
||||
names.put(VersionMessage.class, "version");
|
||||
names.put(InventoryMessage.class, "inv");
|
||||
names.put(Block.class, "block");
|
||||
names.put(GetDataMessage.class, "getdata");
|
||||
names.put(Transaction.class, "tx");
|
||||
names.put(AddressMessage.class, "addr");
|
||||
names.put(Ping.class, "ping");
|
||||
names.put(Pong.class, "pong");
|
||||
names.put(VersionAck.class, "verack");
|
||||
names.put(GetBlocksMessage.class, "getblocks");
|
||||
names.put(GetHeadersMessage.class, "getheaders");
|
||||
names.put(GetAddrMessage.class, "getaddr");
|
||||
names.put(HeadersMessage.class, "headers");
|
||||
names.put(BloomFilter.class, "filterload");
|
||||
names.put(FilteredBlock.class, "merkleblock");
|
||||
names.put(NotFoundMessage.class, "notfound");
|
||||
names.put(MemoryPoolMessage.class, "mempool");
|
||||
names.put(RejectMessage.class, "reject");
|
||||
names.put(GetUTXOsMessage.class, "getutxos");
|
||||
names.put(UTXOsMessage.class, "utxos");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a BitcoinSerializer with the given behavior.
|
||||
*
|
||||
* @param params networkParams used to create Messages instances and termining packetMagic
|
||||
*/
|
||||
public BitcoinSerializer(NetworkParameters params) {
|
||||
this(params, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a BitcoinSerializer with the given behavior.
|
||||
*
|
||||
* @param params networkParams used to create Messages instances and termining packetMagic
|
||||
* @param parseLazy deserialize messages in lazy mode.
|
||||
* @param parseRetain retain the backing byte array of a message for fast reserialization.
|
||||
*/
|
||||
public BitcoinSerializer(NetworkParameters params, boolean parseLazy, boolean parseRetain) {
|
||||
this.params = params;
|
||||
this.parseLazy = parseLazy;
|
||||
this.parseRetain = parseRetain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes message to to the output stream.
|
||||
*/
|
||||
public void serialize(String name, byte[] message, OutputStream out) throws IOException {
|
||||
byte[] header = new byte[4 + COMMAND_LEN + 4 + 4 /* checksum */];
|
||||
uint32ToByteArrayBE(params.getPacketMagic(), header, 0);
|
||||
|
||||
// The header array is initialized to zero by Java so we don't have to worry about
|
||||
// NULL terminating the string here.
|
||||
for (int i = 0; i < name.length() && i < COMMAND_LEN; i++) {
|
||||
header[4 + i] = (byte) (name.codePointAt(i) & 0xFF);
|
||||
}
|
||||
|
||||
Utils.uint32ToByteArrayLE(message.length, header, 4 + COMMAND_LEN);
|
||||
|
||||
byte[] hash = doubleDigest(message);
|
||||
System.arraycopy(hash, 0, header, 4 + COMMAND_LEN + 4, 4);
|
||||
out.write(header);
|
||||
out.write(message);
|
||||
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Sending {} message: {}", name, HEX.encode(header) + HEX.encode(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes message to to the output stream.
|
||||
*/
|
||||
public void serialize(Message message, OutputStream out) throws IOException {
|
||||
String name = names.get(message.getClass());
|
||||
if (name == null) {
|
||||
throw new Error("BitcoinSerializer doesn't currently know how to serialize " + message.getClass());
|
||||
}
|
||||
serialize(name, message.bitcoinSerialize(), out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a message from the given ByteBuffer and returns it.
|
||||
*/
|
||||
public Message deserialize(ByteBuffer in) throws ProtocolException, IOException {
|
||||
// A Bitcoin protocol message has the following format.
|
||||
//
|
||||
// - 4 byte magic number: 0xfabfb5da for the testnet or
|
||||
// 0xf9beb4d9 for production
|
||||
// - 12 byte command in ASCII
|
||||
// - 4 byte payload size
|
||||
// - 4 byte checksum
|
||||
// - Payload data
|
||||
//
|
||||
// The checksum is the first 4 bytes of a SHA256 hash of the message payload. It isn't
|
||||
// present for all messages, notably, the first one on a connection.
|
||||
//
|
||||
// Satoshi's implementation ignores garbage before the magic header bytes. We have to do the same because
|
||||
// sometimes it sends us stuff that isn't part of any message.
|
||||
seekPastMagicBytes(in);
|
||||
BitcoinPacketHeader header = new BitcoinPacketHeader(in);
|
||||
// Now try to read the whole message.
|
||||
return deserializePayload(header, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes only the header in case packet meta data is needed before decoding
|
||||
* the payload. This method assumes you have already called seekPastMagicBytes()
|
||||
*/
|
||||
public BitcoinPacketHeader deserializeHeader(ByteBuffer in) throws ProtocolException, IOException {
|
||||
return new BitcoinPacketHeader(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize payload only. You must provide a header, typically obtained by calling
|
||||
* {@link BitcoinSerializer#deserializeHeader}.
|
||||
*/
|
||||
public Message deserializePayload(BitcoinPacketHeader header, ByteBuffer in) throws ProtocolException, BufferUnderflowException {
|
||||
byte[] payloadBytes = new byte[header.size];
|
||||
in.get(payloadBytes, 0, header.size);
|
||||
|
||||
// Verify the checksum.
|
||||
byte[] hash;
|
||||
hash = doubleDigest(payloadBytes);
|
||||
if (header.checksum[0] != hash[0] || header.checksum[1] != hash[1] ||
|
||||
header.checksum[2] != hash[2] || header.checksum[3] != hash[3]) {
|
||||
throw new ProtocolException("Checksum failed to verify, actual " +
|
||||
HEX.encode(hash) +
|
||||
" vs " + HEX.encode(header.checksum));
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received {} byte '{}' message: {}", header.size, header.command,
|
||||
HEX.encode(payloadBytes));
|
||||
}
|
||||
|
||||
try {
|
||||
return makeMessage(header.command, header.size, payloadBytes, hash, header.checksum);
|
||||
} catch (Exception e) {
|
||||
throw new ProtocolException("Error deserializing message " + HEX.encode(payloadBytes) + "\n", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Message makeMessage(String command, int length, byte[] payloadBytes, byte[] hash, byte[] checksum) throws ProtocolException {
|
||||
// We use an if ladder rather than reflection because reflection is very slow on Android.
|
||||
Message message;
|
||||
if (command.equals("version")) {
|
||||
return new VersionMessage(params, payloadBytes);
|
||||
} else if (command.equals("inv")) {
|
||||
message = new InventoryMessage(params, payloadBytes, parseLazy, parseRetain, length);
|
||||
} else if (command.equals("block")) {
|
||||
message = new Block(params, payloadBytes, parseLazy, parseRetain, length);
|
||||
} else if (command.equals("merkleblock")) {
|
||||
message = new FilteredBlock(params, payloadBytes);
|
||||
} else if (command.equals("getdata")) {
|
||||
message = new GetDataMessage(params, payloadBytes, parseLazy, parseRetain, length);
|
||||
} else if (command.equals("getblocks")) {
|
||||
message = new GetBlocksMessage(params, payloadBytes);
|
||||
} else if (command.equals("getheaders")) {
|
||||
message = new GetHeadersMessage(params, payloadBytes);
|
||||
} else if (command.equals("tx")) {
|
||||
Transaction tx = new Transaction(params, payloadBytes, null, parseLazy, parseRetain, length);
|
||||
if (hash != null)
|
||||
tx.setHash(new Sha256Hash(Utils.reverseBytes(hash)));
|
||||
message = tx;
|
||||
} else if (command.equals("addr")) {
|
||||
message = new AddressMessage(params, payloadBytes, parseLazy, parseRetain, length);
|
||||
} else if (command.equals("ping")) {
|
||||
message = new Ping(params, payloadBytes);
|
||||
} else if (command.equals("pong")) {
|
||||
message = new Pong(params, payloadBytes);
|
||||
} else if (command.equals("verack")) {
|
||||
return new VersionAck(params, payloadBytes);
|
||||
} else if (command.equals("headers")) {
|
||||
return new HeadersMessage(params, payloadBytes);
|
||||
} else if (command.equals("alert")) {
|
||||
return new AlertMessage(params, payloadBytes);
|
||||
} else if (command.equals("filterload")) {
|
||||
return new BloomFilter(params, payloadBytes);
|
||||
} else if (command.equals("notfound")) {
|
||||
return new NotFoundMessage(params, payloadBytes);
|
||||
} else if (command.equals("mempool")) {
|
||||
return new MemoryPoolMessage();
|
||||
} else if (command.equals("reject")) {
|
||||
return new RejectMessage(params, payloadBytes);
|
||||
} else if (command.equals("utxos")) {
|
||||
return new UTXOsMessage(params, payloadBytes);
|
||||
} else if (command.equals("getutxos")) {
|
||||
return new GetUTXOsMessage(params, payloadBytes);
|
||||
} else {
|
||||
log.warn("No support for deserializing message with name {}", command);
|
||||
return new UnknownMessage(params, command, payloadBytes);
|
||||
}
|
||||
if (checksum != null)
|
||||
message.setChecksum(checksum);
|
||||
return message;
|
||||
}
|
||||
|
||||
public void seekPastMagicBytes(ByteBuffer in) throws BufferUnderflowException {
|
||||
int magicCursor = 3; // Which byte of the magic we're looking for currently.
|
||||
while (true) {
|
||||
byte b = in.get();
|
||||
// We're looking for a run of bytes that is the same as the packet magic but we want to ignore partial
|
||||
// magics that aren't complete. So we keep track of where we're up to with magicCursor.
|
||||
byte expectedByte = (byte)(0xFF & params.getPacketMagic() >>> (magicCursor * 8));
|
||||
if (b == expectedByte) {
|
||||
magicCursor--;
|
||||
if (magicCursor < 0) {
|
||||
// We found the magic sequence.
|
||||
return;
|
||||
} else {
|
||||
// We still have further to go to find the next message.
|
||||
}
|
||||
} else {
|
||||
magicCursor = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the serializer will produce lazy parse mode Messages
|
||||
*/
|
||||
public boolean isParseLazyMode() {
|
||||
return parseLazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the serializer will produce cached mode Messages
|
||||
*/
|
||||
public boolean isParseRetainMode() {
|
||||
return parseRetain;
|
||||
}
|
||||
|
||||
|
||||
public static class BitcoinPacketHeader {
|
||||
/** The largest number of bytes that a header can represent */
|
||||
public static final int HEADER_LENGTH = COMMAND_LEN + 4 + 4;
|
||||
|
||||
public final byte[] header;
|
||||
public final String command;
|
||||
public final int size;
|
||||
public final byte[] checksum;
|
||||
|
||||
public BitcoinPacketHeader(ByteBuffer in) throws ProtocolException, BufferUnderflowException {
|
||||
header = new byte[HEADER_LENGTH];
|
||||
in.get(header, 0, header.length);
|
||||
|
||||
int cursor = 0;
|
||||
|
||||
// The command is a NULL terminated string, unless the command fills all twelve bytes
|
||||
// in which case the termination is implicit.
|
||||
for (; header[cursor] != 0 && cursor < COMMAND_LEN; cursor++) ;
|
||||
byte[] commandBytes = new byte[cursor];
|
||||
System.arraycopy(header, 0, commandBytes, 0, cursor);
|
||||
try {
|
||||
command = new String(commandBytes, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
cursor = COMMAND_LEN;
|
||||
|
||||
size = (int) readUint32(header, cursor);
|
||||
cursor += 4;
|
||||
|
||||
if (size > Message.MAX_SIZE)
|
||||
throw new ProtocolException("Message size too large: " + size);
|
||||
|
||||
// Old clients don't send the checksum.
|
||||
checksum = new byte[4];
|
||||
// Note that the size read above includes the checksum bytes.
|
||||
System.arraycopy(header, cursor, checksum, 0, 4);
|
||||
cursor += 4;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,157 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.dogecoin.dogecoinj.store.BlockStore;
|
||||
import com.dogecoin.dogecoinj.store.BlockStoreException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>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 class should be called SPVBlockChain but for backwards compatibility it is not.
|
||||
* </p>
|
||||
*/
|
||||
public class 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)}
|
||||
* </p>
|
||||
*
|
||||
* <p>For the store, you should use {@link com.dogecoin.dogecoinj.store.SPVBlockStore} or you could also try a
|
||||
* {@link com.dogecoin.dogecoinj.store.MemoryBlockStore} if you want to hold all headers in RAM and don't care about
|
||||
* disk serialization (this is rare).</p>
|
||||
*/
|
||||
public BlockChain(NetworkParameters params, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
|
||||
this(params, new ArrayList<BlockChainListener>(), blockStore);
|
||||
if (wallet != null)
|
||||
addWallet(wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
|
||||
this(params, new ArrayList<BlockChainListener>(), blockStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a BlockChain connected to the given list of listeners and a store.
|
||||
*/
|
||||
public BlockChain(NetworkParameters params, List<BlockChainListener> wallets,
|
||||
BlockStore blockStore) throws BlockStoreException {
|
||||
super(params, wallets, blockStore);
|
||||
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 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)
|
||||
return; // nothing to do
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementors can be connected to a {@link BlockChain} and have its methods called when various things
|
||||
* happen that modify the state of the chain, for example: new blocks being received, a re-org occurring, or the
|
||||
* best chain head changing.
|
||||
*/
|
||||
public interface BlockChainListener {
|
||||
/**
|
||||
* Called when a new block on the best chain is seen, after relevant transactions are extracted and sent to
|
||||
* us via either {@link #receiveFromBlock(Transaction, StoredBlock, com.dogecoin.dogecoinj.core.BlockChain.NewBlockType, int)}
|
||||
* or {@link #notifyTransactionIsInBlock(Sha256Hash, StoredBlock, com.dogecoin.dogecoinj.core.BlockChain.NewBlockType, int)}.
|
||||
* If this block is causing a re-organise to a new chain, this method is NOT called even though the block may be
|
||||
* the new best block: your reorganize implementation is expected to do whatever would normally be done do for a new
|
||||
* best block in this case.
|
||||
*/
|
||||
void notifyNewBestBlock(StoredBlock block) throws VerificationException;
|
||||
|
||||
/**
|
||||
* Called by the {@link BlockChain} when the best chain (representing total work done) has changed. In this case,
|
||||
* we need to go through our transactions and find out if any have become invalid. It's possible for our balance
|
||||
* to go down in this case: money we thought we had can suddenly vanish if the rest of the network agrees it
|
||||
* should be so.<p>
|
||||
*
|
||||
* The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last (i.e. newest blocks first).
|
||||
*/
|
||||
void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks,
|
||||
List<StoredBlock> newBlocks) throws VerificationException;
|
||||
|
||||
/**
|
||||
* Returns true if the given transaction is interesting to the listener. If yes, then the transaction will
|
||||
* be provided via the receiveFromBlock method. This method is essentially an optimization that lets BlockChain
|
||||
* bypass verification of a blocks merkle tree if no listeners are interested, which can save time when processing
|
||||
* full blocks on mobile phones. It's likely the method will be removed in future and replaced with an alternative
|
||||
* mechanism that involves listeners providing all keys that are interesting.
|
||||
*/
|
||||
boolean isTransactionRelevant(Transaction tx) throws ScriptException;
|
||||
|
||||
/**
|
||||
* <p>Called by the {@link BlockChain} when we receive a new block that contains a relevant transaction.</p>
|
||||
*
|
||||
* <p>A transaction may be received multiple times if is included into blocks in parallel chains. The blockType
|
||||
* parameter describes whether the containing block is on the main/best chain or whether it's on a presently
|
||||
* inactive side chain.</p>
|
||||
*
|
||||
* <p>The relativityOffset parameter is an arbitrary number used to establish an ordering between transactions
|
||||
* within the same block. In the case where full blocks are being downloaded, it is simply the index of the
|
||||
* transaction within that block. When Bloom filtering is in use, we don't find out the exact offset into a block
|
||||
* that a transaction occurred at, so the relativity count is not reflective of anything in an absolute sense but
|
||||
* rather exists only to order the transaction relative to the others.</p>
|
||||
*/
|
||||
void receiveFromBlock(Transaction tx, StoredBlock block,
|
||||
BlockChain.NewBlockType blockType,
|
||||
int relativityOffset) throws VerificationException;
|
||||
|
||||
/**
|
||||
* <p>Called by the {@link BlockChain} when we receive a new {@link FilteredBlock} that contains the given
|
||||
* transaction hash in its merkle tree.</p>
|
||||
*
|
||||
* <p>A transaction may be received multiple times if is included into blocks in parallel chains. The blockType
|
||||
* parameter describes whether the containing block is on the main/best chain or whether it's on a presently
|
||||
* inactive side chain.</p>
|
||||
*
|
||||
* <p>The relativityOffset parameter in this case is an arbitrary (meaningless) number, that is useful only when
|
||||
* compared to the relativity count of another transaction received inside the same block. It is used to establish
|
||||
* an ordering of transactions relative to one another.</p>
|
||||
*
|
||||
* <p>This method should return false if the given tx hash isn't known about, e.g. because the the transaction was
|
||||
* a Bloom false positive. If it was known about and stored, it should return true. The caller may need to know
|
||||
* this to calculate the effective FP rate.</p>
|
||||
*
|
||||
* @return whether the transaction is known about i.e. was considered relevant previously.
|
||||
*/
|
||||
boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block,
|
||||
BlockChain.NewBlockType blockType,
|
||||
int relativityOffset) throws VerificationException;
|
||||
}
|
@ -1,369 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Matt Corallo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.script.ScriptChunk;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.lang.Math.*;
|
||||
|
||||
/**
|
||||
* <p>A Bloom filter is a probabilistic data structure which can be sent to another client so that it can avoid
|
||||
* sending us transactions that aren't relevant to our set of keys. This allows for significantly more efficient
|
||||
* use of available network bandwidth and CPU time.</p>
|
||||
*
|
||||
* <p>Because a Bloom filter is probabilistic, it has a configurable false positive rate. So the filter will sometimes
|
||||
* match transactions that weren't inserted into it, but it will never fail to match transactions that were. This is
|
||||
* a useful privacy feature - if you have spare bandwidth the false positive rate can be increased so the remote peer
|
||||
* gets a noisy picture of what transactions are relevant to your wallet.</p>
|
||||
*/
|
||||
public class BloomFilter extends Message {
|
||||
/** The BLOOM_UPDATE_* constants control when the bloom filter is auto-updated by the peer using
|
||||
it as a filter, either never, for all outputs or only for pay-2-pubkey outputs (default) */
|
||||
public enum BloomUpdate {
|
||||
UPDATE_NONE, // 0
|
||||
UPDATE_ALL, // 1
|
||||
/** Only adds outpoints to the filter if the output is a pay-to-pubkey/pay-to-multisig script */
|
||||
UPDATE_P2PUBKEY_ONLY //2
|
||||
}
|
||||
|
||||
private byte[] data;
|
||||
private long hashFuncs;
|
||||
private long nTweak;
|
||||
private byte nFlags;
|
||||
|
||||
// Same value as the reference client
|
||||
// A filter of 20,000 items and a false positive rate of 0.1% or one of 10,000 items and 0.0001% is just under 36,000 bytes
|
||||
private static final long MAX_FILTER_SIZE = 36000;
|
||||
// There is little reason to ever have more hash functions than 50 given a limit of 36,000 bytes
|
||||
private static final int MAX_HASH_FUNCS = 50;
|
||||
|
||||
/**
|
||||
* Construct a BloomFilter by deserializing payloadBytes
|
||||
*/
|
||||
public BloomFilter(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a filter with the given parameters which is updated on pay2pubkey outputs only.
|
||||
*/
|
||||
public BloomFilter(int elements, double falsePositiveRate, long randomNonce) {
|
||||
this(elements, falsePositiveRate, randomNonce, BloomUpdate.UPDATE_P2PUBKEY_ONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructs a new Bloom Filter which will provide approximately the given false positive rate when the given
|
||||
* number of elements have been inserted. If the filter would otherwise be larger than the maximum allowed size,
|
||||
* it will be automatically downsized to the maximum size.</p>
|
||||
*
|
||||
* <p>To check the theoretical false positive rate of a given filter, use
|
||||
* {@link BloomFilter#getFalsePositiveRate(int)}.</p>
|
||||
*
|
||||
* <p>The anonymity of which coins are yours to any peer which you send a BloomFilter to is controlled by the
|
||||
* false positive rate. For reference, as of block 187,000, the total number of addresses used in the chain was
|
||||
* roughly 4.5 million. Thus, if you use a false positive rate of 0.001 (0.1%), there will be, on average, 4,500
|
||||
* distinct public keys/addresses which will be thought to be yours by nodes which have your bloom filter, but
|
||||
* which are not actually yours. Keep in mind that a remote node can do a pretty good job estimating the order of
|
||||
* magnitude of the false positive rate of a given filter you provide it when considering the anonymity of a given
|
||||
* filter.</p>
|
||||
*
|
||||
* <p>In order for filtered block download to function efficiently, the number of matched transactions in any given
|
||||
* block should be less than (with some headroom) the maximum size of the MemoryPool used by the Peer
|
||||
* doing the downloading (default is {@link TxConfidenceTable#MAX_SIZE}). See the comment in processBlock(FilteredBlock)
|
||||
* for more information on this restriction.</p>
|
||||
*
|
||||
* <p>randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks.
|
||||
* It should be a random value, however secureness of the random value is of no great consequence.</p>
|
||||
*
|
||||
* <p>updateFlag is used to control filter behaviour on the server (remote node) side when it encounters a hit.
|
||||
* See {@link com.dogecoin.dogecoinj.core.BloomFilter.BloomUpdate} for a brief description of each mode. The purpose
|
||||
* of this flag is to reduce network round-tripping and avoid over-dirtying the filter for the most common
|
||||
* wallet configurations.</p>
|
||||
*/
|
||||
public BloomFilter(int elements, double falsePositiveRate, long randomNonce, BloomUpdate updateFlag) {
|
||||
// The following formulas were stolen from Wikipedia's page on Bloom Filters (with the addition of min(..., MAX_...))
|
||||
// Size required for a given number of elements and false-positive rate
|
||||
int size = (int)(-1 / (pow(log(2), 2)) * elements * log(falsePositiveRate));
|
||||
size = max(1, min(size, (int) MAX_FILTER_SIZE * 8) / 8);
|
||||
data = new byte[size];
|
||||
// Optimal number of hash functions for a given filter size and element count.
|
||||
hashFuncs = (int)(data.length * 8 / (double)elements * log(2));
|
||||
hashFuncs = max(1, min(hashFuncs, MAX_HASH_FUNCS));
|
||||
this.nTweak = randomNonce;
|
||||
this.nFlags = (byte)(0xff & updateFlag.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theoretical false positive rate of this filter if were to contain the given number of elements.
|
||||
*/
|
||||
public double getFalsePositiveRate(int elements) {
|
||||
return pow(1 - pow(E, -1.0 * (hashFuncs * elements) / (data.length * 8)), hashFuncs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Bloom Filter of size " + data.length + " with " + hashFuncs + " hash functions.";
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
data = readByteArray();
|
||||
if (data.length > MAX_FILTER_SIZE)
|
||||
throw new ProtocolException ("Bloom filter out of size range.");
|
||||
hashFuncs = readUint32();
|
||||
if (hashFuncs > MAX_HASH_FUNCS)
|
||||
throw new ProtocolException("Bloom filter hash function count out of range");
|
||||
nTweak = readUint32();
|
||||
nFlags = readBytes(1)[0];
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this message to the provided stream. If you just want the raw bytes use bitcoinSerialize().
|
||||
*/
|
||||
@Override
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(new VarInt(data.length).encode());
|
||||
stream.write(data);
|
||||
Utils.uint32ToByteStreamLE(hashFuncs, stream);
|
||||
Utils.uint32ToByteStreamLE(nTweak, stream);
|
||||
stream.write(nFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Do nothing, lazy parsing isn't useful for bloom filters.
|
||||
}
|
||||
|
||||
private static int rotateLeft32(int x, int r) {
|
||||
return (x << r) | (x >>> (32 - r));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the MurmurHash3 (x86_32) algorithm to the given data.
|
||||
* See this <a href="http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp">C++ code for the original.</a>
|
||||
*/
|
||||
public static int murmurHash3(byte[] data, long nTweak, int hashNum, byte[] object) {
|
||||
int h1 = (int)(hashNum * 0xFBA4C795L + nTweak);
|
||||
final int c1 = 0xcc9e2d51;
|
||||
final int c2 = 0x1b873593;
|
||||
|
||||
int numBlocks = (object.length / 4) * 4;
|
||||
// body
|
||||
for(int i = 0; i < numBlocks; i += 4) {
|
||||
int k1 = (object[i] & 0xFF) |
|
||||
((object[i+1] & 0xFF) << 8) |
|
||||
((object[i+2] & 0xFF) << 16) |
|
||||
((object[i+3] & 0xFF) << 24);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotateLeft32(k1, 15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = rotateLeft32(h1, 13);
|
||||
h1 = h1*5+0xe6546b64;
|
||||
}
|
||||
|
||||
int k1 = 0;
|
||||
switch(object.length & 3)
|
||||
{
|
||||
case 3:
|
||||
k1 ^= (object[numBlocks + 2] & 0xff) << 16;
|
||||
// Fall through.
|
||||
case 2:
|
||||
k1 ^= (object[numBlocks + 1] & 0xff) << 8;
|
||||
// Fall through.
|
||||
case 1:
|
||||
k1 ^= (object[numBlocks] & 0xff);
|
||||
k1 *= c1; k1 = rotateLeft32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
// Fall through.
|
||||
default:
|
||||
// Do nothing.
|
||||
break;
|
||||
}
|
||||
|
||||
// finalization
|
||||
h1 ^= object.length;
|
||||
h1 ^= h1 >>> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ^= h1 >>> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ^= h1 >>> 16;
|
||||
|
||||
return (int)((h1&0xFFFFFFFFL) % (data.length * 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given object matches the filter either because it was inserted, or because we have a
|
||||
* false-positive.
|
||||
*/
|
||||
public synchronized boolean contains(byte[] object) {
|
||||
for (int i = 0; i < hashFuncs; i++) {
|
||||
if (!Utils.checkBitLE(data, murmurHash3(data, nTweak, i, object)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Insert the given arbitrary data into the filter */
|
||||
public synchronized void insert(byte[] object) {
|
||||
for (int i = 0; i < hashFuncs; i++)
|
||||
Utils.setBitLE(data, murmurHash3(data, nTweak, i, object));
|
||||
}
|
||||
|
||||
/** Inserts the given key and equivalent hashed form (for the address). */
|
||||
public synchronized void insert(ECKey key) {
|
||||
insert(key.getPubKey());
|
||||
insert(key.getPubKeyHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this filter to match all objects. A Bloom filter which matches everything may seem pointless, however,
|
||||
* it is useful in order to reduce steady state bandwidth usage when you want full blocks. Instead of receiving
|
||||
* all transaction data twice, you will receive the vast majority of all transactions just once, at broadcast time.
|
||||
* Solved blocks will then be send just as Merkle trees of tx hashes, meaning a constant 32 bytes of data for each
|
||||
* transaction instead of 100-300 bytes as per usual.
|
||||
*/
|
||||
public synchronized void setMatchAll() {
|
||||
data = new byte[] {(byte) 0xff};
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies filter into this. Filter must have the same size, hash function count and nTweak or an
|
||||
* IllegalArgumentException will be thrown.
|
||||
*/
|
||||
public synchronized void merge(BloomFilter filter) {
|
||||
if (!this.matchesAll() && !filter.matchesAll()) {
|
||||
checkArgument(filter.data.length == this.data.length &&
|
||||
filter.hashFuncs == this.hashFuncs &&
|
||||
filter.nTweak == this.nTweak);
|
||||
for (int i = 0; i < data.length; i++)
|
||||
this.data[i] |= filter.data[i];
|
||||
} else {
|
||||
this.data = new byte[] {(byte) 0xff};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this filter will match anything. See {@link com.dogecoin.dogecoinj.core.BloomFilter#setMatchAll()}
|
||||
* for when this can be a useful thing to do.
|
||||
*/
|
||||
public synchronized boolean matchesAll() {
|
||||
for (byte b : data)
|
||||
if (b != (byte) 0xff)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The update flag controls how application of the filter to a block modifies the filter. See the enum javadocs
|
||||
* for information on what occurs and when.
|
||||
*/
|
||||
public synchronized BloomUpdate getUpdateFlag() {
|
||||
if (nFlags == 0)
|
||||
return BloomUpdate.UPDATE_NONE;
|
||||
else if (nFlags == 1)
|
||||
return BloomUpdate.UPDATE_ALL;
|
||||
else if (nFlags == 2)
|
||||
return BloomUpdate.UPDATE_P2PUBKEY_ONLY;
|
||||
else
|
||||
throw new IllegalStateException("Unknown flag combination");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FilteredBlock from the given Block, using this filter to select transactions. Matches can cause the
|
||||
* filter to be updated with the matched element, this ensures that when a filter is applied to a block, spends of
|
||||
* matched transactions are also matched. However it means this filter can be mutated by the operation. The returned
|
||||
* filtered block already has the matched transactions associated with it.
|
||||
*/
|
||||
public synchronized FilteredBlock applyAndUpdate(Block block) {
|
||||
List<Transaction> txns = block.getTransactions();
|
||||
List<Sha256Hash> txHashes = new ArrayList<Sha256Hash>(txns.size());
|
||||
List<Transaction> matched = Lists.newArrayList();
|
||||
byte[] bits = new byte[(int) Math.ceil(txns.size() / 8.0)];
|
||||
for (int i = 0; i < txns.size(); i++) {
|
||||
Transaction tx = txns.get(i);
|
||||
txHashes.add(tx.getHash());
|
||||
if (applyAndUpdate(tx)) {
|
||||
Utils.setBitLE(bits, i);
|
||||
matched.add(tx);
|
||||
}
|
||||
}
|
||||
PartialMerkleTree pmt = PartialMerkleTree.buildFromLeaves(block.getParams(), bits, txHashes);
|
||||
FilteredBlock filteredBlock = new FilteredBlock(block.getParams(), block.cloneAsHeader(), pmt);
|
||||
for (Transaction transaction : matched)
|
||||
filteredBlock.provideTransaction(transaction);
|
||||
return filteredBlock;
|
||||
}
|
||||
|
||||
public synchronized boolean applyAndUpdate(Transaction tx) {
|
||||
if (contains(tx.getHash().getBytes()))
|
||||
return true;
|
||||
boolean found = false;
|
||||
BloomUpdate flag = getUpdateFlag();
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
Script script = output.getScriptPubKey();
|
||||
for (ScriptChunk chunk : script.getChunks()) {
|
||||
if (!chunk.isPushData())
|
||||
continue;
|
||||
if (contains(chunk.data)) {
|
||||
boolean isSendingToPubKeys = script.isSentToRawPubKey() || script.isSentToMultiSig();
|
||||
if (flag == BloomUpdate.UPDATE_ALL || (flag == BloomUpdate.UPDATE_P2PUBKEY_ONLY && isSendingToPubKeys))
|
||||
insert(output.getOutPointFor().bitcoinSerialize());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) return true;
|
||||
for (TransactionInput input : tx.getInputs()) {
|
||||
if (contains(input.getOutpoint().bitcoinSerialize())) {
|
||||
return true;
|
||||
}
|
||||
for (ScriptChunk chunk : input.getScriptSig().getChunks()) {
|
||||
if (chunk.isPushData() && contains(chunk.data))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BloomFilter other = (BloomFilter) o;
|
||||
return hashFuncs == other.hashFuncs &&
|
||||
nTweak == other.nTweak &&
|
||||
Arrays.equals(data, other.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return Objects.hashCode(hashFuncs, nTweak, Arrays.hashCode(data));
|
||||
}
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.store.BlockStore;
|
||||
import com.dogecoin.dogecoinj.store.BlockStoreException;
|
||||
import com.dogecoin.dogecoinj.store.FullPrunedBlockStore;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* <p>Vends hard-coded {@link StoredBlock}s for blocks throughout the chain. Checkpoints serve two purposes:</p>
|
||||
* <ol>
|
||||
* <li>They act as a safety mechanism against huge re-orgs that could rewrite large chunks of history, thus
|
||||
* constraining the block chain to be a consensus mechanism only for recent parts of the timeline.</li>
|
||||
* <li>They allow synchronization to the head of the chain for new wallets/users much faster than syncing all
|
||||
* headers from the genesis block.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Checkpoints are used by the SPV {@link BlockChain} to initialize fresh
|
||||
* {@link com.dogecoin.dogecoinj.store.SPVBlockStore}s. They are not used by fully validating mode, which instead has a
|
||||
* different concept of checkpoints that are used to hard-code the validity of blocks that violate BIP30 (duplicate
|
||||
* coinbase transactions). Those "checkpoints" can be found in NetworkParameters.</p>
|
||||
*
|
||||
* <p>The file format consists of the string "CHECKPOINTS 1", followed by a uint32 containing the number of signatures
|
||||
* to read. The value may not be larger than 256 (so it could have been a byte but isn't for historical reasons).
|
||||
* If the number of signatures is larger than zero, each 65 byte ECDSA secp256k1 signature then follows. The signatures
|
||||
* sign the hash of all bytes that follow the last signature.</p>
|
||||
*
|
||||
* <p>After the signatures come an int32 containing the number of checkpoints in the file. Then each checkpoint follows
|
||||
* one after the other. A checkpoint is 12 bytes for the total work done field, 4 bytes for the height, 80 bytes
|
||||
* for the block header and then 1 zero byte at the end (i.e. number of transactions in the block: always zero).</p>
|
||||
*/
|
||||
public class CheckpointManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(CheckpointManager.class);
|
||||
|
||||
private static final String BINARY_MAGIC = "CHECKPOINTS 1";
|
||||
private static final String TEXTUAL_MAGIC = "TXT CHECKPOINTS 1";
|
||||
private static final int MAX_SIGNATURES = 256;
|
||||
|
||||
// Map of block header time to data.
|
||||
protected final TreeMap<Long, StoredBlock> checkpoints = new TreeMap<Long, StoredBlock>();
|
||||
|
||||
protected final NetworkParameters params;
|
||||
protected final Sha256Hash dataHash;
|
||||
|
||||
public static final BaseEncoding BASE64 = BaseEncoding.base64().omitPadding();
|
||||
|
||||
public CheckpointManager(NetworkParameters params, InputStream inputStream) throws IOException {
|
||||
this.params = checkNotNull(params);
|
||||
checkNotNull(inputStream);
|
||||
inputStream = new BufferedInputStream(inputStream);
|
||||
inputStream.mark(1);
|
||||
int first = inputStream.read();
|
||||
inputStream.reset();
|
||||
if (first == BINARY_MAGIC.charAt(0))
|
||||
dataHash = readBinary(inputStream);
|
||||
else if (first == TEXTUAL_MAGIC.charAt(0))
|
||||
dataHash = readTextual(inputStream);
|
||||
else
|
||||
throw new IOException("Unsupported format.");
|
||||
}
|
||||
|
||||
private Sha256Hash readBinary(InputStream inputStream) throws IOException {
|
||||
DataInputStream dis = null;
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
DigestInputStream digestInputStream = new DigestInputStream(inputStream, digest);
|
||||
dis = new DataInputStream(digestInputStream);
|
||||
digestInputStream.on(false);
|
||||
byte[] header = new byte[BINARY_MAGIC.length()];
|
||||
dis.readFully(header);
|
||||
if (!Arrays.equals(header, BINARY_MAGIC.getBytes("US-ASCII")))
|
||||
throw new IOException("Header bytes did not match expected version");
|
||||
int numSignatures = checkPositionIndex(dis.readInt(), MAX_SIGNATURES, "Num signatures out of range");
|
||||
for (int i = 0; i < numSignatures; i++) {
|
||||
byte[] sig = new byte[65];
|
||||
dis.readFully(sig);
|
||||
// TODO: Do something with the signature here.
|
||||
}
|
||||
digestInputStream.on(true);
|
||||
int numCheckpoints = dis.readInt();
|
||||
checkState(numCheckpoints > 0);
|
||||
final int size = StoredBlock.COMPACT_SERIALIZED_SIZE;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
||||
for (int i = 0; i < numCheckpoints; i++) {
|
||||
if (dis.read(buffer.array(), 0, size) < size)
|
||||
throw new IOException("Incomplete read whilst loading checkpoints.");
|
||||
StoredBlock block = StoredBlock.deserializeCompact(params, buffer);
|
||||
buffer.position(0);
|
||||
checkpoints.put(block.getHeader().getTimeSeconds(), block);
|
||||
}
|
||||
Sha256Hash dataHash = new Sha256Hash(digest.digest());
|
||||
log.info("Read {} checkpoints, hash is {}", checkpoints.size(), dataHash);
|
||||
return dataHash;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
} catch (ProtocolException e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
if (dis != null) dis.close();
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Sha256Hash readTextual(InputStream inputStream) throws IOException {
|
||||
Hasher hasher = Hashing.sha256().newHasher();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, Charsets.US_ASCII));
|
||||
String magic = reader.readLine();
|
||||
if (!TEXTUAL_MAGIC.equals(magic))
|
||||
throw new IOException("unexpected magic: " + magic);
|
||||
int numSigs = Integer.parseInt(reader.readLine());
|
||||
for (int i = 0; i < numSigs; i++)
|
||||
reader.readLine(); // Skip sigs for now.
|
||||
int numCheckpoints = Integer.parseInt(reader.readLine());
|
||||
checkState(numCheckpoints > 0);
|
||||
// Hash numCheckpoints in a way compatible to the binary format.
|
||||
hasher.putBytes(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(numCheckpoints).array());
|
||||
final int size = StoredBlock.COMPACT_SERIALIZED_SIZE;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
||||
for (int i = 0; i < numCheckpoints; i++) {
|
||||
byte[] bytes = BASE64.decode(reader.readLine());
|
||||
hasher.putBytes(bytes);
|
||||
buffer.position(0);
|
||||
buffer.put(bytes);
|
||||
buffer.position(0);
|
||||
StoredBlock block = StoredBlock.deserializeCompact(params, buffer);
|
||||
checkpoints.put(block.getHeader().getTimeSeconds(), block);
|
||||
}
|
||||
HashCode hash = hasher.hash();
|
||||
log.info("Read {} checkpoints, hash is {}", checkpoints.size(), hash);
|
||||
return new Sha256Hash(hash.asBytes());
|
||||
} finally {
|
||||
if (reader != null) reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link StoredBlock} representing the last checkpoint before the given time, for example, normally
|
||||
* you would want to know the checkpoint before the earliest wallet birthday.
|
||||
*/
|
||||
public StoredBlock getCheckpointBefore(long time) {
|
||||
try {
|
||||
checkArgument(time > params.getGenesisBlock().getTimeSeconds());
|
||||
// This is thread safe because the map never changes after creation.
|
||||
Map.Entry<Long, StoredBlock> entry = checkpoints.floorEntry(time);
|
||||
if (entry != null) return entry.getValue();
|
||||
Block genesis = params.getGenesisBlock().cloneAsHeader();
|
||||
return new StoredBlock(genesis, genesis.getWork(), 0);
|
||||
} catch (VerificationException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the number of checkpoints that were loaded. */
|
||||
public int numCheckpoints() {
|
||||
return checkpoints.size();
|
||||
}
|
||||
|
||||
/** Returns a hash of the concatenated checkpoint data. */
|
||||
public Sha256Hash getDataHash() {
|
||||
return dataHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* <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;
|
||||
|
||||
BufferedInputStream stream = new BufferedInputStream(checkpoints);
|
||||
CheckpointManager manager = new CheckpointManager(params, stream);
|
||||
StoredBlock checkpoint = manager.getCheckpointBefore(time);
|
||||
store.put(checkpoint);
|
||||
store.setChainHead(checkpoint);
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Steve Coughlan.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a Message type that can be contained within another Message. ChildMessages that have a cached
|
||||
* backing byte array need to invalidate their parent's caches as well as their own if they are modified.
|
||||
*/
|
||||
public abstract class ChildMessage extends Message {
|
||||
private static final long serialVersionUID = -7657113383624517931L;
|
||||
|
||||
@Nullable protected Message parent;
|
||||
|
||||
protected ChildMessage() {
|
||||
}
|
||||
|
||||
public ChildMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
public ChildMessage(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException {
|
||||
super(params, payload, offset, protocolVersion);
|
||||
}
|
||||
|
||||
public ChildMessage(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, boolean parseLazy,
|
||||
boolean parseRetain, int length) throws ProtocolException {
|
||||
super(params, payload, offset, protocolVersion, parseLazy, parseRetain, length);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public ChildMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
}
|
||||
|
||||
public ChildMessage(NetworkParameters params, byte[] payload, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payload, offset, parseLazy, parseRetain, length);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setParent(@Nullable Message parent) {
|
||||
if (this.parent != null && this.parent != parent && parent != null) {
|
||||
// After old parent is unlinked it won't be able to receive notice if this ChildMessage
|
||||
// changes internally. To be safe we invalidate the parent cache to ensure it rebuilds
|
||||
// manually on serialization.
|
||||
this.parent.unCache();
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#unCache()
|
||||
*/
|
||||
@Override
|
||||
protected void unCache() {
|
||||
super.unCache();
|
||||
if (parent != null)
|
||||
parent.unCache();
|
||||
}
|
||||
|
||||
protected void adjustLength(int adjustment) {
|
||||
adjustLength(0, adjustment);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void adjustLength(int newArraySize, int adjustment) {
|
||||
super.adjustLength(newArraySize, adjustment);
|
||||
if (parent != null)
|
||||
parent.adjustLength(newArraySize, adjustment);
|
||||
}
|
||||
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.utils.MonetaryFormat;
|
||||
import com.google.common.math.LongMath;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* Represents a monetary Bitcoin value. This class is immutable.
|
||||
*/
|
||||
public final class Coin implements Monetary, Comparable<Coin>, Serializable {
|
||||
|
||||
/**
|
||||
* Number of decimals for one Bitcoin. This constant is useful for quick adapting to other coins because a lot of
|
||||
* constants derive from it.
|
||||
*/
|
||||
public static final int SMALLEST_UNIT_EXPONENT = 8;
|
||||
|
||||
/**
|
||||
* The number of satoshis equal to one bitcoin.
|
||||
*/
|
||||
private static final long COIN_VALUE = LongMath.pow(10, SMALLEST_UNIT_EXPONENT);
|
||||
|
||||
/**
|
||||
* Zero Bitcoins.
|
||||
*/
|
||||
public static final Coin ZERO = Coin.valueOf(0);
|
||||
|
||||
/**
|
||||
* One Bitcoin.
|
||||
*/
|
||||
public static final Coin COIN = Coin.valueOf(COIN_VALUE);
|
||||
|
||||
/**
|
||||
* 0.01 Bitcoins. This unit is not really used much.
|
||||
*/
|
||||
public static final Coin CENT = COIN.divide(100);
|
||||
|
||||
/**
|
||||
* 0.001 Bitcoins, also known as 1 mBTC.
|
||||
*/
|
||||
public static final Coin MILLICOIN = COIN.divide(1000);
|
||||
|
||||
/**
|
||||
* 0.000001 Bitcoins, also known as 1 µBTC or 1 uBTC.
|
||||
*/
|
||||
public static final Coin MICROCOIN = MILLICOIN.divide(1000);
|
||||
|
||||
/**
|
||||
* A satoshi is the smallest unit that can be transferred. 100 million of them fit into a Bitcoin.
|
||||
*/
|
||||
public static final Coin SATOSHI = Coin.valueOf(1);
|
||||
|
||||
public static final Coin FIFTY_COINS = COIN.multiply(50);
|
||||
|
||||
/**
|
||||
* Represents a monetary value of minus one satoshi.
|
||||
*/
|
||||
public static final Coin NEGATIVE_SATOSHI = Coin.valueOf(-1);
|
||||
|
||||
/**
|
||||
* The number of satoshis of this monetary value.
|
||||
*/
|
||||
public final long value;
|
||||
|
||||
private final long MAX_SATOSHIS = Long.MAX_VALUE;
|
||||
|
||||
private Coin(final long satoshis) {
|
||||
checkArgument(-MAX_SATOSHIS <= satoshis && satoshis <= MAX_SATOSHIS,
|
||||
"%s satoshis exceeds maximum possible quantity of Bitcoin.", satoshis);
|
||||
this.value = satoshis;
|
||||
}
|
||||
|
||||
public static Coin valueOf(final long satoshis) {
|
||||
return new Coin(satoshis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int smallestUnitExponent() {
|
||||
return SMALLEST_UNIT_EXPONENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of satoshis of this monetary value.
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an amount expressed in the way humans are used to into satoshis.
|
||||
*/
|
||||
public static Coin valueOf(final int coins, final int cents) {
|
||||
checkArgument(cents < 100);
|
||||
checkArgument(cents >= 0);
|
||||
checkArgument(coins >= 0);
|
||||
final Coin coin = COIN.multiply(coins).add(CENT.multiply(cents));
|
||||
checkArgument(coin.compareTo(NetworkParameters.MAX_MONEY) <= 0);
|
||||
return coin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an amount expressed in the way humans are used to.<p>
|
||||
* <p/>
|
||||
* This takes string in a format understood by {@link BigDecimal#BigDecimal(String)},
|
||||
* for example "0", "1", "0.10", "1.23E3", "1234.5E-5".
|
||||
*
|
||||
* @throws IllegalArgumentException if you try to specify fractional satoshis, or a value out of range.
|
||||
*/
|
||||
public static Coin parseCoin(final String str) {
|
||||
try {
|
||||
long satoshis = new BigDecimal(str).movePointRight(SMALLEST_UNIT_EXPONENT).toBigIntegerExact().longValue();
|
||||
return Coin.valueOf(satoshis);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new IllegalArgumentException(e); // Repackage exception to honor method contract
|
||||
}
|
||||
}
|
||||
|
||||
public Coin add(final Coin value) {
|
||||
return new Coin(LongMath.checkedAdd(this.value, value.value));
|
||||
}
|
||||
|
||||
public Coin subtract(final Coin value) {
|
||||
return new Coin(LongMath.checkedSubtract(this.value, value.value));
|
||||
}
|
||||
|
||||
public Coin multiply(final long factor) {
|
||||
return new Coin(LongMath.checkedMultiply(this.value, factor));
|
||||
}
|
||||
|
||||
public Coin divide(final long divisor) {
|
||||
return new Coin(this.value / divisor);
|
||||
}
|
||||
|
||||
public Coin[] divideAndRemainder(final long divisor) {
|
||||
return new Coin[] { new Coin(this.value / divisor), new Coin(this.value % divisor) };
|
||||
}
|
||||
|
||||
public long divide(final Coin divisor) {
|
||||
return this.value / divisor.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value greater than zero,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isPositive() {
|
||||
return signum() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value less than zero,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isNegative() {
|
||||
return signum() == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents zero monetary value,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isZero() {
|
||||
return signum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is greater than that
|
||||
* of the given other Coin, otherwise false.
|
||||
*/
|
||||
public boolean isGreaterThan(Coin other) {
|
||||
return compareTo(other) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is less than that
|
||||
* of the given other Coin, otherwise false.
|
||||
*/
|
||||
public boolean isLessThan(Coin other) {
|
||||
return compareTo(other) < 0;
|
||||
}
|
||||
|
||||
public Coin shiftLeft(final int n) {
|
||||
return new Coin(this.value << n);
|
||||
}
|
||||
|
||||
public Coin shiftRight(final int n) {
|
||||
return new Coin(this.value >> n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int signum() {
|
||||
if (this.value == 0)
|
||||
return 0;
|
||||
return this.value < 0 ? -1 : 1;
|
||||
}
|
||||
|
||||
public Coin negate() {
|
||||
return new Coin(-this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of satoshis of this monetary value. It's deprecated in favour of accessing {@link #value}
|
||||
* directly.
|
||||
*/
|
||||
public long longValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
private static final MonetaryFormat FRIENDLY_FORMAT = MonetaryFormat.BTC.minDecimals(2).repeatOptionalDecimals(1, 6).postfixCode();
|
||||
|
||||
/**
|
||||
* Returns the value as a 0.12 type string. More digits after the decimal place will be used
|
||||
* if necessary, but two will always be present.
|
||||
*/
|
||||
public String toFriendlyString() {
|
||||
return FRIENDLY_FORMAT.format(this).toString();
|
||||
}
|
||||
|
||||
private static final MonetaryFormat PLAIN_FORMAT = MonetaryFormat.BTC.minDecimals(0).repeatOptionalDecimals(1, 8).noCode();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the value as a plain string denominated in BTC.
|
||||
* The result is unformatted with no trailing zeroes.
|
||||
* For instance, a value of 150000 satoshis gives an output string of "0.0015" BTC
|
||||
* </p>
|
||||
*/
|
||||
public String toPlainString() {
|
||||
return PLAIN_FORMAT.format(this).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Long.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (o == null || o.getClass() != getClass())
|
||||
return false;
|
||||
final Coin other = (Coin) o;
|
||||
if (this.value != other.value)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Coin other) {
|
||||
if (this.value == other.value)
|
||||
return 0;
|
||||
return this.value > other.value ? 1 : -1;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* The Context object holds various objects that are scoped to a specific instantiation of bitcoinj for a specific
|
||||
* network. You can get an instance of this class through {@link AbstractBlockChain#getContext()}. At the momemnt it
|
||||
* only contains a {@link com.dogecoin.dogecoinj.core.TxConfidenceTable} but in future it will likely contain file paths and
|
||||
* other global configuration of use.
|
||||
*/
|
||||
public class Context {
|
||||
protected TxConfidenceTable confidenceTable;
|
||||
|
||||
protected Context() {
|
||||
confidenceTable = new TxConfidenceTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TxConfidenceTable} created by this context. The pool tracks advertised
|
||||
* and downloaded transactions so their confidence can be measured as a proportion of how many peers announced it.
|
||||
* With an un-tampered with internet connection, the more peers announce a transaction the more confidence you can
|
||||
* have that it's really valid.
|
||||
*/
|
||||
public TxConfidenceTable getConfidenceTable() {
|
||||
return confidenceTable;
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
// TODO: Rename this to DownloadProgressTracker or something more appropriate.
|
||||
|
||||
/**
|
||||
* <p>An implementation of {@link AbstractPeerEventListener} that listens to chain download events and tracks progress
|
||||
* as a percentage. The default implementation prints progress to stdout, but you can subclass it and override the
|
||||
* progress method to update a GUI instead.</p>
|
||||
*/
|
||||
public class DownloadListener extends AbstractPeerEventListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(DownloadListener.class);
|
||||
private int originalBlocksLeft = -1;
|
||||
private int lastPercent = 0;
|
||||
private Semaphore done = new Semaphore(0);
|
||||
private boolean caughtUp = false;
|
||||
|
||||
@Override
|
||||
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
|
||||
startDownload(blocksLeft);
|
||||
// Only mark this the first time, because this method can be called more than once during a chain download
|
||||
// if we switch peers during it.
|
||||
if (originalBlocksLeft == -1)
|
||||
originalBlocksLeft = blocksLeft;
|
||||
else
|
||||
log.info("Chain download switched to {}", peer);
|
||||
if (blocksLeft == 0) {
|
||||
doneDownload();
|
||||
done.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) {
|
||||
if (caughtUp)
|
||||
return;
|
||||
|
||||
if (blocksLeft == 0) {
|
||||
caughtUp = true;
|
||||
doneDownload();
|
||||
done.release();
|
||||
}
|
||||
|
||||
if (blocksLeft < 0 || originalBlocksLeft <= 0)
|
||||
return;
|
||||
|
||||
double pct = 100.0 - (100.0 * (blocksLeft / (double) originalBlocksLeft));
|
||||
if ((int) pct != lastPercent) {
|
||||
progress(pct, blocksLeft, new Date(block.getTimeSeconds() * 1000));
|
||||
lastPercent = (int) pct;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when download progress is made.
|
||||
*
|
||||
* @param pct the percentage of chain downloaded, estimated
|
||||
* @param date the date of the last block downloaded
|
||||
*/
|
||||
protected void progress(double pct, int blocksSoFar, Date date) {
|
||||
log.info(String.format("Chain download %d%% done with %d blocks to go, block date %s", (int) pct,
|
||||
blocksSoFar, DateFormat.getDateTimeInstance().format(date)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when download is initiated.
|
||||
*
|
||||
* @param blocks the number of blocks to download, estimated
|
||||
*/
|
||||
protected void startDownload(int blocks) {
|
||||
if (blocks > 0 && originalBlocksLeft == -1)
|
||||
log.info("Downloading block chain of size " + blocks + ". " +
|
||||
(blocks > 1000 ? "This may take a while." : ""));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we are done downloading the block chain.
|
||||
*/
|
||||
protected void doneDownload() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the chain to be downloaded.
|
||||
*/
|
||||
public void await() throws InterruptedException {
|
||||
done.acquire();
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* <p>An implementation of {@link AbstractPeerEventListener} that listens to chain download events and tracks progress
|
||||
* as a percentage. The default implementation prints progress to stdout, but you can subclass it and override the
|
||||
* progress method to update a GUI instead.</p>
|
||||
*/
|
||||
public class DownloadProgressTracker extends AbstractPeerEventListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(DownloadProgressTracker.class);
|
||||
private int originalBlocksLeft = -1;
|
||||
private int lastPercent = 0;
|
||||
private SettableFuture<Long> future = SettableFuture.create();
|
||||
private boolean caughtUp = false;
|
||||
|
||||
@Override
|
||||
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
|
||||
if (blocksLeft > 0 && originalBlocksLeft == -1)
|
||||
startDownload(blocksLeft);
|
||||
// Only mark this the first time, because this method can be called more than once during a chain download
|
||||
// if we switch peers during it.
|
||||
if (originalBlocksLeft == -1)
|
||||
originalBlocksLeft = blocksLeft;
|
||||
else
|
||||
log.info("Chain download switched to {}", peer);
|
||||
if (blocksLeft == 0) {
|
||||
doneDownload();
|
||||
future.set(peer.getBestHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) {
|
||||
if (caughtUp)
|
||||
return;
|
||||
|
||||
if (blocksLeft == 0) {
|
||||
caughtUp = true;
|
||||
doneDownload();
|
||||
future.set(peer.getBestHeight());
|
||||
}
|
||||
|
||||
if (blocksLeft < 0 || originalBlocksLeft <= 0)
|
||||
return;
|
||||
|
||||
double pct = 100.0 - (100.0 * (blocksLeft / (double) originalBlocksLeft));
|
||||
if ((int) pct != lastPercent) {
|
||||
progress(pct, blocksLeft, new Date(block.getTimeSeconds() * 1000));
|
||||
lastPercent = (int) pct;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when download progress is made.
|
||||
*
|
||||
* @param pct the percentage of chain downloaded, estimated
|
||||
* @param date the date of the last block downloaded
|
||||
*/
|
||||
protected void progress(double pct, int blocksSoFar, Date date) {
|
||||
log.info(String.format("Chain download %d%% done with %d blocks to go, block date %s", (int) pct, blocksSoFar,
|
||||
Utils.dateTimeFormat(date)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when download is initiated.
|
||||
*
|
||||
* @param blocks the number of blocks to download, estimated
|
||||
*/
|
||||
protected void startDownload(int blocks) {
|
||||
log.info("Downloading block chain of size " + blocks + ". " +
|
||||
(blocks > 1000 ? "This may take a while." : ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we are done downloading the block chain.
|
||||
*/
|
||||
protected void doneDownload() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the chain to be downloaded.
|
||||
*/
|
||||
public void await() throws InterruptedException {
|
||||
try {
|
||||
future.get();
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a listenable future that completes with the height of the best chain (as reported by the peer) once chain
|
||||
* download seems to be finished.
|
||||
*/
|
||||
public ListenableFuture<Long> getFuture() {
|
||||
return future;
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Parses and generates private keys in the form used by the Bitcoin "dumpprivkey" command. This is the private key
|
||||
* bytes with a header byte and 4 checksum bytes at the end. If there are 33 private key bytes instead of 32, then
|
||||
* the last byte is a discriminator value for the compressed pubkey.
|
||||
*/
|
||||
public class DumpedPrivateKey extends VersionedChecksummedBytes {
|
||||
private boolean compressed;
|
||||
|
||||
// Used by ECKey.getPrivateKeyEncoded()
|
||||
DumpedPrivateKey(NetworkParameters params, byte[] keyBytes, boolean compressed) {
|
||||
super(params.getDumpedPrivateKeyHeader(), encode(keyBytes, compressed));
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
private static byte[] encode(byte[] keyBytes, boolean compressed) {
|
||||
Preconditions.checkArgument(keyBytes.length == 32, "Private keys must be 32 bytes");
|
||||
if (!compressed) {
|
||||
return keyBytes;
|
||||
} else {
|
||||
// Keys that have compressed public components have an extra 1 byte on the end in dumped form.
|
||||
byte[] bytes = new byte[33];
|
||||
System.arraycopy(keyBytes, 0, bytes, 0, 32);
|
||||
bytes[32] = 1;
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given private key as created by the "dumpprivkey" Bitcoin C++ RPC.
|
||||
*
|
||||
* @param params The expected network parameters of the key. If you don't care, provide null.
|
||||
* @param encoded The base58 encoded string.
|
||||
* @throws AddressFormatException If the string is invalid or the header byte doesn't match the network params.
|
||||
*/
|
||||
public DumpedPrivateKey(NetworkParameters params, String encoded) throws AddressFormatException {
|
||||
super(encoded);
|
||||
if (params != null && version != params.getDumpedPrivateKeyHeader())
|
||||
throw new AddressFormatException("Mismatched version number, trying to cross networks? " + version +
|
||||
" vs " + params.getDumpedPrivateKeyHeader());
|
||||
if (bytes.length == 33 && bytes[32] == 1) {
|
||||
compressed = true;
|
||||
bytes = Arrays.copyOf(bytes, 32); // Chop off the additional marker byte.
|
||||
} else if (bytes.length == 32) {
|
||||
compressed = false;
|
||||
} else {
|
||||
throw new AddressFormatException("Wrong number of bytes for a private key, not 32 or 33");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ECKey created from this encoded private key.
|
||||
*/
|
||||
public ECKey getKey() {
|
||||
final ECKey key = ECKey.fromPrivate(bytes);
|
||||
return compressed ? key : key.decompress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DumpedPrivateKey other = (DumpedPrivateKey) o;
|
||||
return Arrays.equals(bytes, other.bytes) &&
|
||||
version == other.version &&
|
||||
compressed == other.compressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(bytes, version, compressed);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,83 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Steve Coughlan.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Parent class for header only messages that don't have a payload.
|
||||
* Currently this includes getaddr, verack and special bitcoinj class UnknownMessage.
|
||||
*/
|
||||
public abstract class EmptyMessage extends Message {
|
||||
private static final long serialVersionUID = 8240801253854151802L;
|
||||
|
||||
public EmptyMessage() {
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public EmptyMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public EmptyMessage(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessageSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#parse()
|
||||
*/
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#parseLite()
|
||||
*/
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
length = 0;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#ensureParsed()
|
||||
*/
|
||||
@Override
|
||||
public void ensureParsed() throws ProtocolException {
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#bitcoinSerialize()
|
||||
*/
|
||||
@Override
|
||||
public byte[] bitcoinSerialize() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 Matt Corallo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <p>A FilteredBlock is used to relay a block with its transactions filtered using a {@link BloomFilter}. It consists
|
||||
* of the block header and a {@link PartialMerkleTree} which contains the transactions which matched the filter.</p>
|
||||
*/
|
||||
public class FilteredBlock extends Message {
|
||||
/** The protocol version at which Bloom filtering started to be supported. */
|
||||
public static final int MIN_PROTOCOL_VERSION = 70001;
|
||||
private Block header;
|
||||
|
||||
private PartialMerkleTree merkleTree;
|
||||
private List<Sha256Hash> cachedTransactionHashes = null;
|
||||
|
||||
// A set of transactions whose hashes are a subset of getTransactionHashes()
|
||||
// These were relayed as a part of the filteredblock getdata, ie likely weren't previously received as loose transactions
|
||||
private Map<Sha256Hash, Transaction> associatedTransactions = new HashMap<Sha256Hash, Transaction>();
|
||||
|
||||
public FilteredBlock(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
public FilteredBlock(NetworkParameters params, Block header, PartialMerkleTree pmt) {
|
||||
super(params);
|
||||
this.header = header;
|
||||
this.merkleTree = pmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
if (header.transactions == null)
|
||||
header.bitcoinSerializeToStream(stream);
|
||||
else
|
||||
header.cloneAsHeader().bitcoinSerializeToStream(stream);
|
||||
merkleTree.bitcoinSerializeToStream(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
long blockVersion = Utils.readUint32(payload, cursor);
|
||||
|
||||
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
||||
System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE);
|
||||
|
||||
|
||||
if (blockVersion == Block.BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
AuxPoWMessage auxPoWMessage = new AuxPoWMessage(payload, cursor + Block.HEADER_SIZE);
|
||||
auxPoWMessage.parse();
|
||||
this.cursor = auxPoWMessage.cursor;
|
||||
|
||||
header = new Block(params, headerBytes, new Block(params, auxPoWMessage.constructParentHeader()));
|
||||
|
||||
byte[] filteredBlock = new byte[Block.HEADER_SIZE + payload.length - cursor];
|
||||
System.arraycopy(headerBytes, 0, filteredBlock, 0, headerBytes.length-1);
|
||||
System.arraycopy(payload, cursor, filteredBlock, Block.HEADER_SIZE, payload.length-cursor);
|
||||
merkleTree = new PartialMerkleTree(params, filteredBlock, Block.HEADER_SIZE);
|
||||
} else {
|
||||
header = new Block(params, headerBytes);
|
||||
merkleTree = new PartialMerkleTree(params, payload, Block.HEADER_SIZE);
|
||||
}
|
||||
|
||||
length = Block.HEADER_SIZE + merkleTree.getMessageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of leaf hashes which are contained in the partial merkle tree in this filtered block
|
||||
*
|
||||
* @throws ProtocolException If the partial merkle block is invalid or the merkle root of the partial merkle block doesnt match the block header
|
||||
*/
|
||||
public List<Sha256Hash> getTransactionHashes() throws VerificationException {
|
||||
if (cachedTransactionHashes != null)
|
||||
return Collections.unmodifiableList(cachedTransactionHashes);
|
||||
List<Sha256Hash> hashesMatched = new LinkedList<Sha256Hash>();
|
||||
if (header.getMerkleRoot().equals(merkleTree.getTxnHashAndMerkleRoot(hashesMatched))) {
|
||||
cachedTransactionHashes = hashesMatched;
|
||||
return Collections.unmodifiableList(cachedTransactionHashes);
|
||||
} else
|
||||
throw new VerificationException("Merkle root of block header does not match merkle root of partial merkle tree.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the block header
|
||||
*/
|
||||
public Block getBlockHeader() {
|
||||
return header.cloneAsHeader();
|
||||
}
|
||||
|
||||
/** Gets the hash of the block represented in this Filtered Block */
|
||||
@Override
|
||||
public Sha256Hash getHash() {
|
||||
return header.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide this FilteredBlock with a transaction which is in its merkle tree
|
||||
* @returns false if the tx is not relevant to this FilteredBlock
|
||||
*/
|
||||
public boolean provideTransaction(Transaction tx) throws VerificationException {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
if (getTransactionHashes().contains(hash)) {
|
||||
associatedTransactions.put(hash, tx);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Gets the set of transactions which were provided using provideTransaction() which match in getTransactionHashes() */
|
||||
public Map<Sha256Hash, Transaction> getAssociatedTransactions() {
|
||||
return Collections.unmodifiableMap(associatedTransactions);
|
||||
}
|
||||
|
||||
/** Number of transactions in this block, before it was filtered */
|
||||
public int getTransactionCount() {
|
||||
return merkleTree.getTransactionCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FilteredBlock block = (FilteredBlock) o;
|
||||
|
||||
if (!associatedTransactions.equals(block.associatedTransactions)) return false;
|
||||
if (!header.equals(block.header)) return false;
|
||||
if (!merkleTree.equals(block.merkleTree)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = header.hashCode();
|
||||
result = 31 * result + merkleTree.hashCode();
|
||||
result = 31 * result + associatedTransactions.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FilteredBlock{" +
|
||||
"merkleTree=" + merkleTree +
|
||||
", header=" + header +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,517 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Matt Corallo.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.script.Script.VerifyFlag;
|
||||
import com.dogecoin.dogecoinj.store.BlockStoreException;
|
||||
import com.dogecoin.dogecoinj.store.FullPrunedBlockStore;
|
||||
import com.dogecoin.dogecoinj.utils.DaemonThreadFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* <p>A FullPrunedBlockChain works in conjunction with a {@link FullPrunedBlockStore} to verify all the rules of the
|
||||
* Bitcoin system, with the downside being a larg cost in system resources. Fully verifying means all unspent transaction
|
||||
* outputs are stored. Once a transaction output is spent and that spend is buried deep enough, the data related to it
|
||||
* is deleted to ensure disk space usage doesn't grow forever. For this reason a pruning node cannot serve the full
|
||||
* block chain to other clients, but it nevertheless provides the same security guarantees as a regular Satoshi
|
||||
* client does.</p>
|
||||
*/
|
||||
public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
private static final Logger log = LoggerFactory.getLogger(FullPrunedBlockChain.class);
|
||||
|
||||
/** Keeps a map of block hashes to StoredBlocks. */
|
||||
protected final FullPrunedBlockStore blockStore;
|
||||
|
||||
// Whether or not to execute scriptPubKeys before accepting a transaction (i.e. check signatures).
|
||||
private boolean runScripts = true;
|
||||
|
||||
/**
|
||||
* 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)}
|
||||
*/
|
||||
public FullPrunedBlockChain(NetworkParameters params, Wallet wallet, FullPrunedBlockStore blockStore) throws BlockStoreException {
|
||||
this(params, new ArrayList<BlockChainListener>(), blockStore);
|
||||
if (wallet != null)
|
||||
addWallet(wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 FullPrunedBlockChain(NetworkParameters params, FullPrunedBlockStore blockStore) throws BlockStoreException {
|
||||
this(params, new ArrayList<BlockChainListener>(), blockStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a BlockChain connected to the given list of wallets and a store.
|
||||
*/
|
||||
public FullPrunedBlockChain(NetworkParameters params, List<BlockChainListener> listeners,
|
||||
FullPrunedBlockStore blockStore) throws BlockStoreException {
|
||||
super(params, listeners, blockStore);
|
||||
this.blockStore = blockStore;
|
||||
// Ignore upgrading for now
|
||||
this.chainHead = blockStore.getVerifiedChainHead();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block header, TransactionOutputChanges txOutChanges)
|
||||
throws BlockStoreException, VerificationException {
|
||||
StoredBlock newBlock = storedPrev.build(header);
|
||||
blockStore.put(newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), txOutChanges));
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StoredBlock addToBlockStore(StoredBlock storedPrev, Block block)
|
||||
throws BlockStoreException, VerificationException {
|
||||
StoredBlock newBlock = storedPrev.build(block);
|
||||
blockStore.put(newBlock, new StoredUndoableBlock(newBlock.getHeader().getHash(), block.transactions));
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void rollbackBlockStore(int height) throws BlockStoreException {
|
||||
throw new BlockStoreException("Unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldVerifyTransactions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to run scripts whilst accepting blocks (i.e. checking signatures, for most transactions).
|
||||
* If you're accepting data from an untrusted node, such as one found via the P2P network, this should be set
|
||||
* to true (which is the default). If you're downloading a chain from a node you control, script execution
|
||||
* is redundant because you know the connected node won't relay bad data to you. In that case it's safe to set
|
||||
* this to false and obtain a significant speedup.
|
||||
*/
|
||||
public void setRunScripts(boolean value) {
|
||||
this.runScripts = value;
|
||||
}
|
||||
|
||||
//TODO: Remove lots of duplicated code in the two connectTransactions
|
||||
|
||||
// TODO: execute in order of largest transaction (by input count) first
|
||||
ExecutorService scriptVerificationExecutor = Executors.newFixedThreadPool(
|
||||
Runtime.getRuntime().availableProcessors(), new DaemonThreadFactory());
|
||||
|
||||
/** A job submitted to the executor which verifies signatures. */
|
||||
private static class Verifier implements Callable<VerificationException> {
|
||||
final Transaction tx;
|
||||
final List<Script> prevOutScripts;
|
||||
final Set<VerifyFlag> verifyFlags;
|
||||
|
||||
public Verifier(final Transaction tx, final List<Script> prevOutScripts, final Set<VerifyFlag> verifyFlags) {
|
||||
this.tx = tx; this.prevOutScripts = prevOutScripts; this.verifyFlags = verifyFlags;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VerificationException call() throws Exception {
|
||||
try{
|
||||
ListIterator<Script> prevOutIt = prevOutScripts.listIterator();
|
||||
for (int index = 0; index < tx.getInputs().size(); index++) {
|
||||
tx.getInputs().get(index).getScriptSig().correctlySpends(tx, index, prevOutIt.next(), verifyFlags);
|
||||
}
|
||||
} catch (VerificationException e) {
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the {@link Script} from the script bytes or null if it doesn't parse. */
|
||||
@Nullable
|
||||
private Script getScript(byte[] scriptBytes) {
|
||||
try {
|
||||
return new Script(scriptBytes);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address from the {@link Script} if it exists otherwise return empty string "".
|
||||
* @param script The script.
|
||||
* @return The address.
|
||||
*/
|
||||
private String getScriptAddress(@Nullable Script script) {
|
||||
String address = "";
|
||||
try {
|
||||
if (script != null) {
|
||||
address = script.getToAddress(params, true).toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Script.ScriptType} of this script.
|
||||
* @param script The script.
|
||||
* @return The script type.
|
||||
*/
|
||||
private Script.ScriptType getScriptType(@Nullable Script script) {
|
||||
if (script != null) {
|
||||
return script.getScriptType();
|
||||
}
|
||||
return Script.ScriptType.NO_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransactionOutputChanges connectTransactions(int height, Block block)
|
||||
throws VerificationException, BlockStoreException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
if (block.transactions == null)
|
||||
throw new RuntimeException("connectTransactions called with Block that didn't have transactions!");
|
||||
if (!params.passesCheckpoint(height, block.getHash()))
|
||||
throw new VerificationException("Block failed checkpoint lockin at " + height);
|
||||
|
||||
blockStore.beginDatabaseBatchWrite();
|
||||
|
||||
LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
|
||||
LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
|
||||
long sigOps = 0;
|
||||
final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
|
||||
if (block.getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
|
||||
verifyFlags.add(VerifyFlag.P2SH);
|
||||
|
||||
if (scriptVerificationExecutor.isShutdown())
|
||||
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(block.transactions.size());
|
||||
try {
|
||||
if (!params.isCheckpoint(height)) {
|
||||
// BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the
|
||||
// checkpoints list and we therefore only check non-checkpoints for duplicated transactions here. See the
|
||||
// BIP30 document for more details on this: https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
|
||||
for (Transaction tx : block.transactions) {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
// If we already have unspent outputs for this hash, we saw the tx already. Either the block is
|
||||
// being added twice (bug) or the block is a BIP30 violator.
|
||||
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
||||
throw new VerificationException("Block failed BIP30 test!");
|
||||
if (verifyFlags.contains(VerifyFlag.P2SH)) // We already check non-BIP16 sigops in Block.verifyTransactions(true)
|
||||
sigOps += tx.getSigOpCount();
|
||||
}
|
||||
}
|
||||
Coin totalFees = Coin.ZERO;
|
||||
Coin coinbaseValue = null;
|
||||
for (final Transaction tx : block.transactions) {
|
||||
boolean isCoinBase = tx.isCoinBase();
|
||||
Coin valueIn = Coin.ZERO;
|
||||
Coin valueOut = Coin.ZERO;
|
||||
final List<Script> prevOutScripts = new LinkedList<Script>();
|
||||
if (!isCoinBase) {
|
||||
// For each input of the transaction remove the corresponding output from the set of unspent
|
||||
// outputs.
|
||||
for (int index = 0; index < tx.getInputs().size(); index++) {
|
||||
TransactionInput in = tx.getInputs().get(index);
|
||||
UTXO prevOut = blockStore.getTransactionOutput(in.getOutpoint().getHash(),
|
||||
in.getOutpoint().getIndex());
|
||||
if (prevOut == null)
|
||||
throw new VerificationException("Attempted to spend a non-existent or already spent output!");
|
||||
// Coinbases can't be spent until they mature, to avoid re-orgs destroying entire transaction
|
||||
// chains. The assumption is there will ~never be re-orgs deeper than the spendable coinbase
|
||||
// chain depth.
|
||||
if (prevOut.isCoinbase()) {
|
||||
if (height - prevOut.getHeight() < params.getSpendableCoinbaseDepth()) {
|
||||
throw new VerificationException("Tried to spend coinbase at depth " + (height - prevOut.getHeight()));
|
||||
}
|
||||
}
|
||||
// TODO: Check we're not spending the genesis transaction here. Satoshis code won't allow it.
|
||||
valueIn = valueIn.add(prevOut.getValue());
|
||||
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
||||
if (new Script(prevOut.getScriptBytes()).isPayToScriptHash())
|
||||
sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
|
||||
if (sigOps > Block.MAX_BLOCK_SIGOPS)
|
||||
throw new VerificationException("Too many P2SH SigOps in block");
|
||||
}
|
||||
|
||||
prevOutScripts.add(new Script(prevOut.getScriptBytes()));
|
||||
|
||||
//in.getScriptSig().correctlySpends(tx, index, new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length));
|
||||
|
||||
blockStore.removeUnspentTransactionOutput(prevOut);
|
||||
txOutsSpent.add(prevOut);
|
||||
}
|
||||
}
|
||||
Sha256Hash hash = tx.getHash();
|
||||
for (TransactionOutput out : tx.getOutputs()) {
|
||||
valueOut = valueOut.add(out.getValue());
|
||||
// For each output, add it to the set of unspent outputs so it can be consumed in future.
|
||||
Script script = getScript(out.getScriptBytes());
|
||||
UTXO newOut = new UTXO(hash,
|
||||
out.getIndex(),
|
||||
out.getValue(),
|
||||
height, isCoinBase,
|
||||
out.getScriptBytes(),
|
||||
getScriptAddress(script),
|
||||
getScriptType(script).ordinal());
|
||||
blockStore.addUnspentTransactionOutput(newOut);
|
||||
txOutsCreated.add(newOut);
|
||||
}
|
||||
// All values were already checked for being non-negative (as it is verified in Transaction.verify())
|
||||
// but we check again here just for defence in depth. Transactions with zero output value are OK.
|
||||
if (valueOut.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new VerificationException("Transaction output value out of range");
|
||||
if (isCoinBase) {
|
||||
coinbaseValue = valueOut;
|
||||
} else {
|
||||
if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new VerificationException("Transaction input value out of range");
|
||||
totalFees = totalFees.add(valueIn.subtract(valueOut));
|
||||
}
|
||||
|
||||
if (!isCoinBase && runScripts) {
|
||||
// Because correctlySpends modifies transactions, this must come after we are done with tx
|
||||
FutureTask<VerificationException> future = new FutureTask<VerificationException>(new Verifier(tx, prevOutScripts, verifyFlags));
|
||||
scriptVerificationExecutor.execute(future);
|
||||
listScriptVerificationResults.add(future);
|
||||
}
|
||||
}
|
||||
if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0 || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0)
|
||||
throw new VerificationException("Transaction fees out of range");
|
||||
for (Future<VerificationException> future : listScriptVerificationResults) {
|
||||
VerificationException e;
|
||||
try {
|
||||
e = future.get();
|
||||
} catch (InterruptedException thrownE) {
|
||||
throw new RuntimeException(thrownE); // Shouldn't happen
|
||||
} catch (ExecutionException thrownE) {
|
||||
log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
|
||||
throw new VerificationException("Bug in Script.correctlySpends, likely script malformed in some new and interesting way.", thrownE);
|
||||
}
|
||||
if (e != null)
|
||||
throw e;
|
||||
}
|
||||
} catch (VerificationException e) {
|
||||
scriptVerificationExecutor.shutdownNow();
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
} catch (BlockStoreException e) {
|
||||
scriptVerificationExecutor.shutdownNow();
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
}
|
||||
return new TransactionOutputChanges(txOutsCreated, txOutsSpent);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Used during reorgs to connect a block previously on a fork
|
||||
*/
|
||||
protected synchronized TransactionOutputChanges connectTransactions(StoredBlock newBlock)
|
||||
throws VerificationException, BlockStoreException, PrunedException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
if (!params.passesCheckpoint(newBlock.getHeight(), newBlock.getHeader().getHash()))
|
||||
throw new VerificationException("Block failed checkpoint lockin at " + newBlock.getHeight());
|
||||
|
||||
blockStore.beginDatabaseBatchWrite();
|
||||
StoredUndoableBlock block = blockStore.getUndoBlock(newBlock.getHeader().getHash());
|
||||
if (block == null) {
|
||||
// We're trying to re-org too deep and the data needed has been deleted.
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw new PrunedException(newBlock.getHeader().getHash());
|
||||
}
|
||||
TransactionOutputChanges txOutChanges;
|
||||
try {
|
||||
List<Transaction> transactions = block.getTransactions();
|
||||
if (transactions != null) {
|
||||
LinkedList<UTXO> txOutsSpent = new LinkedList<UTXO>();
|
||||
LinkedList<UTXO> txOutsCreated = new LinkedList<UTXO>();
|
||||
long sigOps = 0;
|
||||
final Set<VerifyFlag> verifyFlags = EnumSet.noneOf(VerifyFlag.class);
|
||||
if (newBlock.getHeader().getTimeSeconds() >= NetworkParameters.BIP16_ENFORCE_TIME)
|
||||
verifyFlags.add(VerifyFlag.P2SH);
|
||||
if (!params.isCheckpoint(newBlock.getHeight())) {
|
||||
for(Transaction tx : transactions) {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
||||
throw new VerificationException("Block failed BIP30 test!");
|
||||
}
|
||||
}
|
||||
Coin totalFees = Coin.ZERO;
|
||||
Coin coinbaseValue = null;
|
||||
|
||||
if (scriptVerificationExecutor.isShutdown())
|
||||
scriptVerificationExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
List<Future<VerificationException>> listScriptVerificationResults = new ArrayList<Future<VerificationException>>(transactions.size());
|
||||
for(final Transaction tx : transactions) {
|
||||
boolean isCoinBase = tx.isCoinBase();
|
||||
Coin valueIn = Coin.ZERO;
|
||||
Coin valueOut = Coin.ZERO;
|
||||
final List<Script> prevOutScripts = new LinkedList<Script>();
|
||||
if (!isCoinBase) {
|
||||
for (int index = 0; index < tx.getInputs().size(); index++) {
|
||||
final TransactionInput in = tx.getInputs().get(index);
|
||||
final UTXO prevOut = blockStore.getTransactionOutput(in.getOutpoint().getHash(),
|
||||
in.getOutpoint().getIndex());
|
||||
if (prevOut == null)
|
||||
throw new VerificationException("Attempted spend of a non-existent or already spent output!");
|
||||
if (prevOut.isCoinbase() && newBlock.getHeight() - prevOut.getHeight() < params.getSpendableCoinbaseDepth())
|
||||
throw new VerificationException("Tried to spend coinbase at depth " + (newBlock.getHeight() - prevOut.getHeight()));
|
||||
valueIn = valueIn.add(prevOut.getValue());
|
||||
if (verifyFlags.contains(VerifyFlag.P2SH)) {
|
||||
Script script = new Script(prevOut.getScriptBytes());
|
||||
if (script.isPayToScriptHash())
|
||||
sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
|
||||
if (sigOps > Block.MAX_BLOCK_SIGOPS)
|
||||
throw new VerificationException("Too many P2SH SigOps in block");
|
||||
}
|
||||
|
||||
prevOutScripts.add(new Script(prevOut.getScriptBytes()));
|
||||
|
||||
blockStore.removeUnspentTransactionOutput(prevOut);
|
||||
txOutsSpent.add(prevOut);
|
||||
}
|
||||
}
|
||||
Sha256Hash hash = tx.getHash();
|
||||
for (TransactionOutput out : tx.getOutputs()) {
|
||||
valueOut = valueOut.add(out.getValue());
|
||||
Script script = getScript(out.getScriptBytes());
|
||||
UTXO newOut = new UTXO(hash,
|
||||
out.getIndex(),
|
||||
out.getValue(),
|
||||
newBlock.getHeight(),
|
||||
isCoinBase,
|
||||
out.getScriptBytes(),
|
||||
getScriptAddress(script),
|
||||
getScriptType(script).ordinal());
|
||||
blockStore.addUnspentTransactionOutput(newOut);
|
||||
txOutsCreated.add(newOut);
|
||||
}
|
||||
// All values were already checked for being non-negative (as it is verified in Transaction.verify())
|
||||
// but we check again here just for defence in depth. Transactions with zero output value are OK.
|
||||
if (valueOut.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new VerificationException("Transaction output value out of range");
|
||||
if (isCoinBase) {
|
||||
coinbaseValue = valueOut;
|
||||
} else {
|
||||
if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new VerificationException("Transaction input value out of range");
|
||||
totalFees = totalFees.add(valueIn.subtract(valueOut));
|
||||
}
|
||||
|
||||
if (!isCoinBase) {
|
||||
// Because correctlySpends modifies transactions, this must come after we are done with tx
|
||||
FutureTask<VerificationException> future = new FutureTask<VerificationException>(new Verifier(tx, prevOutScripts, verifyFlags));
|
||||
scriptVerificationExecutor.execute(future);
|
||||
listScriptVerificationResults.add(future);
|
||||
}
|
||||
}
|
||||
if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0 ||
|
||||
newBlock.getHeader().getBlockInflation(newBlock.getHeight()).add(totalFees).compareTo(coinbaseValue) < 0)
|
||||
throw new VerificationException("Transaction fees out of range");
|
||||
txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent);
|
||||
for (Future<VerificationException> future : listScriptVerificationResults) {
|
||||
VerificationException e;
|
||||
try {
|
||||
e = future.get();
|
||||
} catch (InterruptedException thrownE) {
|
||||
throw new RuntimeException(thrownE); // Shouldn't happen
|
||||
} catch (ExecutionException thrownE) {
|
||||
log.error("Script.correctlySpends threw a non-normal exception: " + thrownE.getCause());
|
||||
throw new VerificationException("Bug in Script.correctlySpends, likely script malformed in some new and interesting way.", thrownE);
|
||||
}
|
||||
if (e != null)
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
txOutChanges = block.getTxOutChanges();
|
||||
if (!params.isCheckpoint(newBlock.getHeight()))
|
||||
for(UTXO out : txOutChanges.txOutsCreated) {
|
||||
Sha256Hash hash = out.getHash();
|
||||
if (blockStore.getTransactionOutput(hash, out.getIndex()) != null)
|
||||
throw new VerificationException("Block failed BIP30 test!");
|
||||
}
|
||||
for (UTXO out : txOutChanges.txOutsCreated)
|
||||
blockStore.addUnspentTransactionOutput(out);
|
||||
for (UTXO out : txOutChanges.txOutsSpent)
|
||||
blockStore.removeUnspentTransactionOutput(out);
|
||||
}
|
||||
} catch (VerificationException e) {
|
||||
scriptVerificationExecutor.shutdownNow();
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
} catch (BlockStoreException e) {
|
||||
scriptVerificationExecutor.shutdownNow();
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
}
|
||||
return txOutChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is broken for blocks that do not pass BIP30, so all BIP30-failing blocks which are allowed to fail BIP30
|
||||
* must be checkpointed.
|
||||
*/
|
||||
@Override
|
||||
protected void disconnectTransactions(StoredBlock oldBlock) throws PrunedException, BlockStoreException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
blockStore.beginDatabaseBatchWrite();
|
||||
try {
|
||||
StoredUndoableBlock undoBlock = blockStore.getUndoBlock(oldBlock.getHeader().getHash());
|
||||
if (undoBlock == null) throw new PrunedException(oldBlock.getHeader().getHash());
|
||||
TransactionOutputChanges txOutChanges = undoBlock.getTxOutChanges();
|
||||
for(UTXO out : txOutChanges.txOutsSpent)
|
||||
blockStore.addUnspentTransactionOutput(out);
|
||||
for(UTXO out : txOutChanges.txOutsCreated)
|
||||
blockStore.removeUnspentTransactionOutput(out);
|
||||
} catch (PrunedException e) {
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
} catch (BlockStoreException e) {
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetChainHead(StoredBlock chainHead) throws BlockStoreException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
blockStore.setVerifiedChainHead(chainHead);
|
||||
blockStore.commitDatabaseBatchWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notSettingChainHead() throws BlockStoreException {
|
||||
blockStore.abortDatabaseBatchWrite();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StoredBlock getStoredBlockInCurrentScope(Sha256Hash hash) throws BlockStoreException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
return blockStore.getOnceUndoableStoredBlock(hash);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* Represents the "getaddr" P2P protocol message, which requests network {@link AddressMessage}s from a peer. Not to
|
||||
* be confused with {@link Address} which is sort of like an account number.
|
||||
*/
|
||||
public class GetAddrMessage extends EmptyMessage {
|
||||
private static final long serialVersionUID = 6204437624599661503L;
|
||||
|
||||
public GetAddrMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents the "getblocks" P2P network message, which requests the hashes of the parts of the block chain we're
|
||||
* missing. Those blocks can then be downloaded with a {@link GetDataMessage}.
|
||||
*/
|
||||
public class GetBlocksMessage extends Message {
|
||||
private static final long serialVersionUID = 3479412877853645644L;
|
||||
protected long version;
|
||||
protected List<Sha256Hash> locator;
|
||||
protected Sha256Hash stopHash;
|
||||
|
||||
public GetBlocksMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
||||
super(params);
|
||||
this.version = protocolVersion;
|
||||
this.locator = locator;
|
||||
this.stopHash = stopHash;
|
||||
}
|
||||
|
||||
public GetBlocksMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
cursor = offset;
|
||||
version = readUint32();
|
||||
int startCount = (int) readVarInt();
|
||||
if (startCount > 500)
|
||||
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
||||
length = (int) (cursor - offset + ((startCount + 1) * 32));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws ProtocolException {
|
||||
cursor = offset;
|
||||
version = readUint32();
|
||||
int startCount = (int) readVarInt();
|
||||
if (startCount > 500)
|
||||
throw new ProtocolException("Number of locators cannot be > 500, received: " + startCount);
|
||||
locator = new ArrayList<Sha256Hash>(startCount);
|
||||
for (int i = 0; i < startCount; i++) {
|
||||
locator.add(readHash());
|
||||
}
|
||||
stopHash = readHash();
|
||||
}
|
||||
|
||||
public List<Sha256Hash> getLocator() {
|
||||
return locator;
|
||||
}
|
||||
|
||||
public Sha256Hash getStopHash() {
|
||||
return stopHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer b = new StringBuffer();
|
||||
b.append("getblocks: ");
|
||||
for (Sha256Hash hash : locator) {
|
||||
b.append(hash.toString());
|
||||
b.append(" ");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
// Version, for some reason.
|
||||
Utils.uint32ToByteStreamLE(NetworkParameters.PROTOCOL_VERSION, stream);
|
||||
// Then a vector of block hashes. This is actually a "block locator", a set of block
|
||||
// identifiers that spans the entire chain with exponentially increasing gaps between
|
||||
// them, until we end up at the genesis block. See CBlockLocator::Set()
|
||||
stream.write(new VarInt(locator.size()).encode());
|
||||
for (Sha256Hash hash : locator) {
|
||||
// Have to reverse as wire format is little endian.
|
||||
stream.write(Utils.reverseBytes(hash.getBytes()));
|
||||
}
|
||||
// Next, a block ID to stop at.
|
||||
stream.write(Utils.reverseBytes(stopHash.getBytes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GetBlocksMessage other = (GetBlocksMessage) o;
|
||||
return version == other.version &&
|
||||
locator.size() == other.locator.size() &&
|
||||
locator.containsAll(other.locator) &&
|
||||
stopHash.equals(other.stopHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (int) version ^ "getblocks".hashCode();
|
||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode();
|
||||
hashCode ^= stopHash.hashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* Represents the "getdata" P2P network message, which requests the contents of blocks or transactions given their
|
||||
* hashes.
|
||||
*/
|
||||
public class GetDataMessage extends ListMessage {
|
||||
private static final long serialVersionUID = 2754681589501709887L;
|
||||
|
||||
public GetDataMessage(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a 'getdata' message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public GetDataMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payload, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
public GetDataMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
public void addTransaction(Sha256Hash hash) {
|
||||
addItem(new InventoryItem(InventoryItem.Type.Transaction, hash));
|
||||
}
|
||||
|
||||
public void addBlock(Sha256Hash hash) {
|
||||
addItem(new InventoryItem(InventoryItem.Type.Block, hash));
|
||||
}
|
||||
|
||||
public void addFilteredBlock(Sha256Hash hash) {
|
||||
addItem(new InventoryItem(InventoryItem.Type.FilteredBlock, hash));
|
||||
}
|
||||
|
||||
public Sha256Hash getHashOf(int i) {
|
||||
return getItems().get(i).hash;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The "getheaders" command is structurally identical to "getblocks", but has different meaning. On receiving this
|
||||
* message a Bitcoin node returns matching blocks up to the limit, but without the bodies. It is useful as an
|
||||
* optimization: when your wallet does not contain any keys created before a particular time, you don't have to download
|
||||
* the bodies for those blocks because you know there are no relevant transactions.
|
||||
*/
|
||||
public class GetHeadersMessage extends GetBlocksMessage {
|
||||
public GetHeadersMessage(NetworkParameters params, List<Sha256Hash> locator, Sha256Hash stopHash) {
|
||||
super(params, locator, stopHash);
|
||||
}
|
||||
|
||||
public GetHeadersMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer b = new StringBuffer();
|
||||
b.append("getheaders: ");
|
||||
for (Sha256Hash hash : locator) {
|
||||
b.append(hash.toString());
|
||||
b.append(" ");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two getheaders messages. Note that even though they are structurally identical a GetHeadersMessage
|
||||
* will not compare equal to a GetBlocksMessage containing the same data.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
GetHeadersMessage other = (GetHeadersMessage) o;
|
||||
return version == other.version &&
|
||||
locator.size() == other.locator.size() &&
|
||||
locator.containsAll(other.locator) &&
|
||||
stopHash.equals(other.stopHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (int) version ^ "getheaders".hashCode();
|
||||
for (Sha256Hash aLocator : locator) hashCode ^= aLocator.hashCode();
|
||||
hashCode ^= stopHash.hashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 the bitcoinj authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This command is supported only by <a href="http://github.com/bitcoinxt/bitcoinxt">Bitcoin XT</a> nodes, which
|
||||
* advertise themselves using the second service bit flag. It requests a query of the UTXO set keyed by a set of
|
||||
* outpoints (i.e. tx hash and output index). The result contains a bitmap of spentness flags, and the contents of
|
||||
* the associated outputs if they were found. The results aren't authenticated by anything, so the peer could lie,
|
||||
* or a man in the middle could swap out its answer for something else.
|
||||
*/
|
||||
public class GetUTXOsMessage extends Message {
|
||||
public static final int MIN_PROTOCOL_VERSION = 70002;
|
||||
/** Bitmask of service flags required for a node to support this command (0x3) */
|
||||
public static final int SERVICE_FLAGS_REQUIRED = 3;
|
||||
|
||||
private boolean includeMempool;
|
||||
private ImmutableList<TransactionOutPoint> outPoints;
|
||||
|
||||
public GetUTXOsMessage(NetworkParameters params, List<TransactionOutPoint> outPoints, boolean includeMempool) {
|
||||
super(params);
|
||||
this.outPoints = ImmutableList.copyOf(outPoints);
|
||||
this.includeMempool = includeMempool;
|
||||
}
|
||||
|
||||
public GetUTXOsMessage(NetworkParameters params, byte[] payloadBytes) {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parse() throws ProtocolException {
|
||||
includeMempool = readBytes(1)[0] == 1;
|
||||
long numOutpoints = readVarInt();
|
||||
ImmutableList.Builder<TransactionOutPoint> list = ImmutableList.builder();
|
||||
for (int i = 0; i < numOutpoints; i++) {
|
||||
TransactionOutPoint outPoint = new TransactionOutPoint(params, payload, cursor);
|
||||
list.add(outPoint);
|
||||
cursor += outPoint.getMessageSize();
|
||||
}
|
||||
outPoints = list.build();
|
||||
length = cursor;
|
||||
}
|
||||
|
||||
public boolean getIncludeMempool() {
|
||||
return includeMempool;
|
||||
}
|
||||
|
||||
public ImmutableList<TransactionOutPoint> getOutPoints() {
|
||||
return outPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Not needed.
|
||||
}
|
||||
|
||||
@Override
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(new byte[]{1}); // include mempool.
|
||||
stream.write(new VarInt(outPoints.size()).encode());
|
||||
for (TransactionOutPoint outPoint : outPoints) {
|
||||
outPoint.bitcoinSerializeToStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
GetUTXOsMessage that = (GetUTXOsMessage) o;
|
||||
|
||||
if (includeMempool != that.includeMempool) return false;
|
||||
if (!outPoints.equals(that.outPoints)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (includeMempool ? 1 : 0);
|
||||
result = 31 * result + outPoints.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A protocol message that contains a repeated series of block headers, sent in response to the "getheaders" command.
|
||||
* This is useful when you want to traverse the chain but know you don't care about the block contents, for example,
|
||||
* because you have a freshly created wallet with no keys.
|
||||
*/
|
||||
public class HeadersMessage extends Message {
|
||||
private static final Logger log = LoggerFactory.getLogger(HeadersMessage.class);
|
||||
|
||||
// The main client will never send us more than this number of headers.
|
||||
public static final int MAX_HEADERS = 2000;
|
||||
|
||||
private List<Block> blockHeaders;
|
||||
|
||||
public HeadersMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0);
|
||||
}
|
||||
|
||||
public HeadersMessage(NetworkParameters params, Block... headers) throws ProtocolException {
|
||||
super(params);
|
||||
blockHeaders = Arrays.asList(headers);
|
||||
}
|
||||
|
||||
public HeadersMessage(NetworkParameters params, List<Block> headers) throws ProtocolException {
|
||||
super(params);
|
||||
blockHeaders = headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(new VarInt(blockHeaders.size()).encode());
|
||||
for (Block header : blockHeaders) {
|
||||
if (header.transactions == null)
|
||||
header.bitcoinSerializeToStream(stream);
|
||||
else
|
||||
header.cloneAsHeader().bitcoinSerializeToStream(stream);
|
||||
stream.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
if (length == UNKNOWN_LENGTH) {
|
||||
int saveCursor = cursor;
|
||||
long numHeaders = readVarInt();
|
||||
cursor = saveCursor;
|
||||
|
||||
// Each header has 80 bytes and one more byte for transactions number which is 00.
|
||||
length = 81 * (int)numHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
long numHeaders = readVarInt();
|
||||
if (numHeaders > MAX_HEADERS)
|
||||
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
||||
MAX_HEADERS);
|
||||
|
||||
blockHeaders = new ArrayList<Block>();
|
||||
|
||||
for (int i = 0; i < numHeaders; ++i) {
|
||||
// Read the block version. If it's not an aux block all is fine, else throw out the aux stuff
|
||||
long blockVersion = Utils.readUint32(payload, cursor);
|
||||
byte[] blockHeader;
|
||||
Block newBlockHeader;
|
||||
if (blockVersion == Block.BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
blockHeader = readBytes(80);
|
||||
byte[] tmp = new byte[81];
|
||||
System.arraycopy(blockHeader, 0, tmp, 0, blockHeader.length);
|
||||
tmp[80] = 0;
|
||||
blockHeader = tmp;
|
||||
|
||||
AuxPoWMessage auxPoWMessage = new AuxPoWMessage(payload, cursor);
|
||||
auxPoWMessage.parse();
|
||||
this.cursor = auxPoWMessage.cursor;
|
||||
|
||||
if (readBytes(1)[0] != 0)
|
||||
throw new ProtocolException("Block header does not end with a null byte");
|
||||
|
||||
newBlockHeader = new Block(this.params, blockHeader, true, true, 81, new Block(params, auxPoWMessage.constructParentHeader()));
|
||||
} else {
|
||||
// Read 80 bytes of the header and one more byte for the transaction list, which is always a 00 because the
|
||||
// transaction list is empty.
|
||||
blockHeader = readBytes(81);
|
||||
newBlockHeader = new Block(this.params, blockHeader, true, true, 81);
|
||||
}
|
||||
if (blockHeader[80] != 0)
|
||||
throw new ProtocolException("Block header does not end with a null byte");
|
||||
blockHeaders.add(newBlockHeader);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
for (int i = 0; i < numHeaders; ++i) {
|
||||
log.debug(this.blockHeaders.get(i).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<Block> getBlockHeaders() {
|
||||
return blockHeaders;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that you don't have enough money available to perform the requested operation.
|
||||
*/
|
||||
public class InsufficientMoneyException extends Exception {
|
||||
/** Contains the number of satoshis that would have been required to complete the operation. */
|
||||
@Nullable
|
||||
public final Coin missing;
|
||||
|
||||
protected InsufficientMoneyException() {
|
||||
this.missing = null;
|
||||
}
|
||||
|
||||
public InsufficientMoneyException(Coin missing) {
|
||||
this(missing, "Insufficient money, missing " + missing + " satoshis");
|
||||
}
|
||||
|
||||
public InsufficientMoneyException(Coin missing, String message) {
|
||||
super(message);
|
||||
this.missing = checkNotNull(missing);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
public class InventoryItem {
|
||||
|
||||
/**
|
||||
* 4 byte uint32 type field + 32 byte hash
|
||||
*/
|
||||
static final int MESSAGE_LENGTH = 36;
|
||||
|
||||
public enum Type {
|
||||
Error,
|
||||
Transaction,
|
||||
Block,
|
||||
FilteredBlock
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
public final Sha256Hash hash;
|
||||
|
||||
public InventoryItem(Type type, Sha256Hash hash) {
|
||||
this.type = type;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type.toString() + ": " + hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
InventoryItem other = (InventoryItem) o;
|
||||
return type == other.type &&
|
||||
hash.equals(other.hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash.hashCode() + type.ordinal();
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* <p>Represents the "inv" P2P network message. An inv contains a list of hashes of either blocks or transactions. It's
|
||||
* a bandwidth optimization - on receiving some data, a (fully validating) peer sends every connected peer an inv
|
||||
* containing the hash of what it saw. It'll only transmit the full thing if a peer asks for it with a
|
||||
* {@link GetDataMessage}.</p>
|
||||
*/
|
||||
public class InventoryMessage extends ListMessage {
|
||||
private static final long serialVersionUID = -7050246551646107066L;
|
||||
|
||||
/** A hard coded constant in the protocol. */
|
||||
public static final int MAX_INV_SIZE = 50000;
|
||||
|
||||
public InventoryMessage(NetworkParameters params, byte[] bytes) throws ProtocolException {
|
||||
super(params, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes an 'inv' message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public InventoryMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payload, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
public InventoryMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
public void addBlock(Block block) {
|
||||
addItem(new InventoryItem(InventoryItem.Type.Block, block.getHash()));
|
||||
}
|
||||
|
||||
public void addTransaction(Transaction tx) {
|
||||
addItem(new InventoryItem(InventoryItem.Type.Transaction, tx.getHash()));
|
||||
}
|
||||
|
||||
/** Creates a new inv message for the given transactions. */
|
||||
public static InventoryMessage with(Transaction... txns) {
|
||||
checkArgument(txns.length > 0);
|
||||
InventoryMessage result = new InventoryMessage(txns[0].getParams());
|
||||
for (Transaction tx : txns)
|
||||
result.addTransaction(tx);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstract superclass of classes with list based payload, ie InventoryMessage and GetDataMessage.
|
||||
*/
|
||||
public abstract class ListMessage extends Message {
|
||||
private static final long serialVersionUID = -4275896329391143643L;
|
||||
|
||||
private long arrayLen;
|
||||
// For some reason the compiler complains if this is inside InventoryItem
|
||||
protected List<InventoryItem> items;
|
||||
|
||||
public static final long MAX_INVENTORY_ITEMS = 50000;
|
||||
|
||||
public ListMessage(NetworkParameters params, byte[] bytes) throws ProtocolException {
|
||||
super(params, bytes, 0);
|
||||
}
|
||||
|
||||
public ListMessage(NetworkParameters params, byte[] payload, boolean parseLazy, boolean parseRetain, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payload, 0, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
public ListMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
items = new ArrayList<InventoryItem>();
|
||||
length = 1; //length of 0 varint;
|
||||
}
|
||||
|
||||
public List<InventoryItem> getItems() {
|
||||
maybeParse();
|
||||
return Collections.unmodifiableList(items);
|
||||
}
|
||||
|
||||
public void addItem(InventoryItem item) {
|
||||
unCache();
|
||||
length -= VarInt.sizeOf(items.size());
|
||||
items.add(item);
|
||||
length += VarInt.sizeOf(items.size()) + InventoryItem.MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
public void removeItem(int index) {
|
||||
unCache();
|
||||
length -= VarInt.sizeOf(items.size());
|
||||
items.remove(index);
|
||||
length += VarInt.sizeOf(items.size()) - InventoryItem.MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
arrayLen = readVarInt();
|
||||
if (arrayLen > MAX_INVENTORY_ITEMS)
|
||||
throw new ProtocolException("Too many items in INV message: " + arrayLen);
|
||||
length = (int) (cursor - offset + (arrayLen * InventoryItem.MESSAGE_LENGTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws ProtocolException {
|
||||
// An inv is vector<CInv> where CInv is int+hash. The int is either 1 or 2 for tx or block.
|
||||
items = new ArrayList<InventoryItem>((int) arrayLen);
|
||||
for (int i = 0; i < arrayLen; i++) {
|
||||
if (cursor + InventoryItem.MESSAGE_LENGTH > payload.length) {
|
||||
throw new ProtocolException("Ran off the end of the INV");
|
||||
}
|
||||
int typeCode = (int) readUint32();
|
||||
InventoryItem.Type type;
|
||||
// See ppszTypeName in net.h
|
||||
switch (typeCode) {
|
||||
case 0:
|
||||
type = InventoryItem.Type.Error;
|
||||
break;
|
||||
case 1:
|
||||
type = InventoryItem.Type.Transaction;
|
||||
break;
|
||||
case 2:
|
||||
type = InventoryItem.Type.Block;
|
||||
break;
|
||||
case 3:
|
||||
type = InventoryItem.Type.FilteredBlock;
|
||||
break;
|
||||
default:
|
||||
throw new ProtocolException("Unknown CInv type: " + typeCode);
|
||||
}
|
||||
InventoryItem item = new InventoryItem(type, readHash());
|
||||
items.add(item);
|
||||
}
|
||||
payload = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(new VarInt(items.size()).encode());
|
||||
for (InventoryItem i : items) {
|
||||
// Write out the type code.
|
||||
Utils.uint32ToByteStreamLE(i.type.ordinal(), stream);
|
||||
// And now the hash.
|
||||
stream.write(Utils.reverseBytes(i.hash.getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ListMessage other = (ListMessage) o;
|
||||
return items.equals(other.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return items.hashCode();
|
||||
}
|
||||
}
|
@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* <p>Tracks transactions that are being announced across the network. Typically one is created for you by a
|
||||
* {@link PeerGroup} and then given to each Peer to update. The current purpose is to let Peers update the confidence
|
||||
* (number of peers broadcasting). It helps address an attack scenario in which a malicious remote peer (or several)
|
||||
* feeds you invalid transactions, eg, ones that spend coins which don't exist. If you don't see most of the peers
|
||||
* announce the transaction within a reasonable time, it may be that the TX is not valid. Alternatively, an attacker
|
||||
* may control your entire internet connection: in this scenario counting broadcasting peers does not help you.</p>
|
||||
*
|
||||
* <p>It is <b>not</b> at this time directly equivalent to the Satoshi clients memory pool, which tracks
|
||||
* all transactions not currently included in the best chain - it's simply a cache.</p>
|
||||
*/
|
||||
public class MemoryPool {
|
||||
private static final Logger log = LoggerFactory.getLogger(MemoryPool.class);
|
||||
protected ReentrantLock lock = Threading.lock("mempool");
|
||||
|
||||
// For each transaction we may have seen:
|
||||
// - only its hash in an inv packet
|
||||
// - the full transaction itself, if we asked for it to be sent to us (or a peer sent it regardless), or if we
|
||||
// sent it.
|
||||
//
|
||||
// Before we see the full transaction, we need to track how many peers advertised it, so we can estimate its
|
||||
// confidence pre-chain inclusion assuming an un-tampered with network connection. After we see the full transaction
|
||||
// we need to switch from tracking that data in the Entry to tracking it in the TransactionConfidence object itself.
|
||||
private static class WeakTransactionReference extends WeakReference<Transaction> {
|
||||
public Sha256Hash hash;
|
||||
public WeakTransactionReference(Transaction tx, ReferenceQueue<Transaction> queue) {
|
||||
super(tx, queue);
|
||||
hash = tx.getHash();
|
||||
}
|
||||
}
|
||||
private static class Entry {
|
||||
// Invariants: one of the two fields must be null, to indicate which is used.
|
||||
Set<PeerAddress> addresses;
|
||||
// We keep a weak reference to the transaction. This means that if no other bit of code finds the transaction
|
||||
// worth keeping around it will drop out of memory and we will, at some point, forget about it, which means
|
||||
// both addresses and tx.get() will be null. When this happens the WeakTransactionReference appears in the queue
|
||||
// allowing us to delete the associated entry (the tx itself has already gone away).
|
||||
WeakTransactionReference tx;
|
||||
}
|
||||
private LinkedHashMap<Sha256Hash, Entry> memoryPool;
|
||||
|
||||
// This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the MemoryPool is the
|
||||
// only thing that is tracking the transaction anymore. We check it from time to time and delete memoryPool entries
|
||||
// corresponding to expired transactions. In this way memory usage of the system is in line with however many
|
||||
// transactions you actually care to track the confidence of. We can still end up with lots of hashes being stored
|
||||
// if our peers flood us with invs but the MAX_SIZE param caps this.
|
||||
private ReferenceQueue<Transaction> referenceQueue;
|
||||
|
||||
/** The max size of a memory pool created with the no-args constructor. */
|
||||
public static final int MAX_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* Creates a memory pool that will track at most the given number of transactions (allowing you to bound memory
|
||||
* usage).
|
||||
* @param size Max number of transactions to track. The pool will fill up to this size then stop growing.
|
||||
*/
|
||||
public MemoryPool(final int size) {
|
||||
memoryPool = new LinkedHashMap<Sha256Hash, Entry>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, Entry> entry) {
|
||||
// An arbitrary choice to stop the memory used by tracked transactions getting too huge in the event
|
||||
// of some kind of DoS attack.
|
||||
return size() > size;
|
||||
}
|
||||
};
|
||||
referenceQueue = new ReferenceQueue<Transaction>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memory pool that will track at most {@link MemoryPool#MAX_SIZE} entries. You should normally use
|
||||
* this constructor.
|
||||
*/
|
||||
public MemoryPool() {
|
||||
this(MAX_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If any transactions have expired due to being only weakly reachable through us, go ahead and delete their
|
||||
* memoryPool entries - it means we downloaded the transaction and sent it to various event listeners, none of
|
||||
* which bothered to keep a reference. Typically, this is because the transaction does not involve any keys that
|
||||
* are relevant to any of our wallets.
|
||||
*/
|
||||
private void cleanPool() {
|
||||
lock.lock();
|
||||
try {
|
||||
Reference<? extends Transaction> ref;
|
||||
while ((ref = referenceQueue.poll()) != null) {
|
||||
// Find which transaction got deleted by the GC.
|
||||
WeakTransactionReference txRef = (WeakTransactionReference) ref;
|
||||
// And remove the associated map entry so the other bits of memory can also be reclaimed.
|
||||
memoryPool.remove(txRef.hash);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of peers that have seen the given hash recently.
|
||||
*/
|
||||
public int numBroadcastPeers(Sha256Hash txHash) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanPool();
|
||||
Entry entry = memoryPool.get(txHash);
|
||||
if (entry == null) {
|
||||
// No such TX known.
|
||||
return 0;
|
||||
} else if (entry.tx == null) {
|
||||
// We've seen at least one peer announce with an inv.
|
||||
checkNotNull(entry.addresses);
|
||||
return entry.addresses.size();
|
||||
} else {
|
||||
final Transaction tx = entry.tx.get();
|
||||
if (tx == null) {
|
||||
// We previously downloaded this transaction, but nothing cared about it so the garbage collector threw
|
||||
// it away. We also deleted the set that tracked which peers had seen it. Treat this case as a zero and
|
||||
// just delete it from the map.
|
||||
memoryPool.remove(txHash);
|
||||
return 0;
|
||||
} else {
|
||||
checkState(entry.addresses == null);
|
||||
return tx.getConfidence().numBroadcastPeers();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the tx into the table and returns either it, or a different Transaction object that has the same hash.
|
||||
* Unlike seen and the other methods, this one does not imply that a tx has been announced by a peer and does
|
||||
* not mark it as such.
|
||||
*/
|
||||
public Transaction intern(Transaction tx) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanPool();
|
||||
Entry entry = memoryPool.get(tx.getHash());
|
||||
if (entry != null) {
|
||||
// This TX or its hash have been previously interned.
|
||||
if (entry.tx != null) {
|
||||
// We already interned it (but may have thrown it away).
|
||||
checkState(entry.addresses == null);
|
||||
// We only want one canonical object instance for a transaction no matter how many times it is
|
||||
// deserialized.
|
||||
Transaction transaction = entry.tx.get();
|
||||
if (transaction != null) {
|
||||
// We saw it before and kept it around. Hand back the canonical copy.
|
||||
tx = transaction;
|
||||
}
|
||||
return tx;
|
||||
} else {
|
||||
// We received a transaction that we have previously seen announced but not downloaded until now.
|
||||
checkNotNull(entry.addresses);
|
||||
entry.tx = new WeakTransactionReference(tx, referenceQueue);
|
||||
Set<PeerAddress> addrs = entry.addresses;
|
||||
entry.addresses = null;
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
log.debug("Adding tx [{}] {} to the memory pool",
|
||||
confidence.numBroadcastPeers(), tx.getHashAsString());
|
||||
for (PeerAddress a : addrs) {
|
||||
markBroadcast(a, tx);
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
} else {
|
||||
// This often happens when we are downloading a Bloom filtered chain, or recursively downloading
|
||||
// dependencies of a relevant transaction (see Peer.downloadDependencies).
|
||||
log.debug("Provided with a downloaded transaction we didn't see announced yet: {}", tx.getHashAsString());
|
||||
entry = new Entry();
|
||||
entry.tx = new WeakTransactionReference(tx, referenceQueue);
|
||||
memoryPool.put(tx.getHash(), entry);
|
||||
return tx;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by peers when they receive a "tx" message containing a valid serialized transaction.
|
||||
* @param tx The TX deserialized from the wire.
|
||||
* @param byPeer The Peer that received it.
|
||||
* @return An object that is semantically the same TX but may be a different object instance.
|
||||
*/
|
||||
public Transaction seen(Transaction tx, PeerAddress byPeer) {
|
||||
lock.lock();
|
||||
try {
|
||||
final Transaction interned = intern(tx);
|
||||
markBroadcast(byPeer, interned);
|
||||
return interned;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by peers when they see a transaction advertised in an "inv" message. It either will increase the
|
||||
* confidence of the pre-existing transaction or will just keep a record of the address for future usage.
|
||||
*/
|
||||
public void seen(Sha256Hash hash, PeerAddress byPeer) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanPool();
|
||||
Entry entry = memoryPool.get(hash);
|
||||
if (entry != null) {
|
||||
// This TX or its hash have been previously announced.
|
||||
if (entry.tx != null) {
|
||||
checkState(entry.addresses == null);
|
||||
Transaction tx = entry.tx.get();
|
||||
if (tx != null) {
|
||||
markBroadcast(byPeer, tx);
|
||||
log.debug("{}: Peer announced transaction we have seen before [{}] {}",
|
||||
byPeer, tx.getConfidence().numBroadcastPeers(), tx.getHashAsString());
|
||||
} else {
|
||||
// The inv is telling us about a transaction that we previously downloaded, and threw away
|
||||
// because nothing found it interesting enough to keep around. So do nothing.
|
||||
}
|
||||
} else {
|
||||
checkNotNull(entry.addresses);
|
||||
entry.addresses.add(byPeer);
|
||||
log.debug("{}: Peer announced transaction we have seen announced before [{}] {}",
|
||||
byPeer, entry.addresses.size(), hash);
|
||||
}
|
||||
} else {
|
||||
// This TX has never been seen before.
|
||||
entry = new Entry();
|
||||
// TODO: Using hashsets here is inefficient compared to just having an array.
|
||||
entry.addresses = new HashSet<PeerAddress>();
|
||||
entry.addresses.add(byPeer);
|
||||
memoryPool.put(hash, entry);
|
||||
log.info("{}: Peer announced new transaction [1] {}", byPeer, hash);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void markBroadcast(PeerAddress byPeer, Transaction tx) {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
final TransactionConfidence confidence = tx.getConfidence();
|
||||
if (confidence.markBroadcastBy(byPeer))
|
||||
confidence.queueListeners(TransactionConfidence.Listener.ChangeReason.SEEN_PEERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Transaction} for the given hash if we have downloaded it, or null if that hash is unknown or
|
||||
* we only saw advertisements for it yet or it has been downloaded but garbage collected due to nowhere else
|
||||
* holding a reference to it.
|
||||
*/
|
||||
@Nullable
|
||||
public Transaction get(Sha256Hash hash) {
|
||||
lock.lock();
|
||||
try {
|
||||
Entry entry = memoryPool.get(hash);
|
||||
if (entry == null) return null; // Unknown.
|
||||
if (entry.tx == null) return null; // Seen but only in advertisements.
|
||||
if (entry.tx.get() == null) return null; // Was downloaded but garbage collected.
|
||||
Transaction tx = entry.tx.get();
|
||||
checkNotNull(tx);
|
||||
return tx;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the TX identified by hash has been seen before (ie, in an inv). Note that a transaction that
|
||||
* was broadcast, downloaded and nothing kept a reference to it will eventually be cleared out by the garbage
|
||||
* collector and wasSeen() will return false - it does not keep a permanent record of every hash ever broadcast.
|
||||
*/
|
||||
public boolean maybeWasSeen(Sha256Hash hash) {
|
||||
lock.lock();
|
||||
try {
|
||||
Entry entry = memoryPool.get(hash);
|
||||
return entry != null;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The "mempool" message asks a remote peer to announce all transactions in its memory pool, possibly restricted by
|
||||
* any Bloom filter set on the connection. The list of transaction hashes comes back in an inv message. Note that
|
||||
* this is different to the {@link TxConfidenceTable} object which doesn't try to keep track of all pending transactions,
|
||||
* it's just a holding area for transactions that a part of the app may find interesting. The mempool message has
|
||||
* no fields.
|
||||
*/
|
||||
public class MemoryPoolMessage extends Message {
|
||||
@Override
|
||||
void parse() throws ProtocolException {}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {}
|
||||
|
||||
@Override
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {}
|
||||
}
|
@ -1,527 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* <p>A Message is a data structure that can be serialized/deserialized using both the Bitcoin proprietary serialization
|
||||
* format and built-in Java object serialization. Specific types of messages that are used both in the block chain,
|
||||
* and on the wire, are derived from this class.</p>
|
||||
*/
|
||||
public abstract class Message implements Serializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(Message.class);
|
||||
private static final long serialVersionUID = -3561053461717079135L;
|
||||
|
||||
public static final int MAX_SIZE = 0x02000000; // 32MB
|
||||
|
||||
public static final int UNKNOWN_LENGTH = Integer.MIN_VALUE;
|
||||
|
||||
// Useful to ensure serialize/deserialize are consistent with each other.
|
||||
private static final boolean SELF_CHECK = false;
|
||||
|
||||
// The offset is how many bytes into the provided byte array this message payload starts at.
|
||||
protected transient int offset;
|
||||
// The cursor keeps track of where we are in the byte array as we parse it.
|
||||
// Note that it's relative to the start of the array NOT the start of the message payload.
|
||||
protected transient int cursor;
|
||||
|
||||
protected transient int length = UNKNOWN_LENGTH;
|
||||
|
||||
// The raw message payload bytes themselves.
|
||||
protected transient byte[] payload;
|
||||
|
||||
protected transient boolean parsed = false;
|
||||
protected transient boolean recached = false;
|
||||
protected transient final boolean parseLazy;
|
||||
protected transient final boolean parseRetain;
|
||||
|
||||
protected transient int protocolVersion;
|
||||
|
||||
protected transient byte[] checksum;
|
||||
|
||||
// This will be saved by subclasses that implement Serializable.
|
||||
protected NetworkParameters params;
|
||||
|
||||
/**
|
||||
* This exists for the Java serialization framework to use only.
|
||||
*/
|
||||
protected Message() {
|
||||
parsed = true;
|
||||
parseLazy = false;
|
||||
parseRetain = false;
|
||||
}
|
||||
|
||||
Message(NetworkParameters params) {
|
||||
this.params = params;
|
||||
parsed = true;
|
||||
parseLazy = false;
|
||||
parseRetain = false;
|
||||
}
|
||||
|
||||
Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException {
|
||||
this(params, payload, offset, protocolVersion, false, false, UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param protocolVersion Bitcoin protocol version.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message payload if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
Message(NetworkParameters params, byte[] payload, int offset, int protocolVersion, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
|
||||
this.parseLazy = parseLazy;
|
||||
this.parseRetain = parseRetain;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.params = params;
|
||||
this.payload = payload;
|
||||
this.cursor = this.offset = offset;
|
||||
this.length = length;
|
||||
if (parseLazy) {
|
||||
parseLite();
|
||||
} else {
|
||||
parseLite();
|
||||
parse();
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
if (this.length == UNKNOWN_LENGTH)
|
||||
checkState(false, "Length field has not been set in constructor for %s after %s parse. " +
|
||||
"Refer to Message.parseLite() for detail of required Length field contract.",
|
||||
getClass().getSimpleName(), parseLazy ? "lite" : "full");
|
||||
|
||||
if (SELF_CHECK) {
|
||||
selfCheck(payload, offset);
|
||||
}
|
||||
|
||||
if (parseRetain || !parsed)
|
||||
return;
|
||||
this.payload = null;
|
||||
}
|
||||
|
||||
private void selfCheck(byte[] payload, int offset) {
|
||||
if (!(this instanceof VersionMessage)) {
|
||||
maybeParse();
|
||||
byte[] payloadBytes = new byte[cursor - offset];
|
||||
System.arraycopy(payload, offset, payloadBytes, 0, cursor - offset);
|
||||
byte[] reserialized = bitcoinSerialize();
|
||||
if (!Arrays.equals(reserialized, payloadBytes))
|
||||
throw new RuntimeException("Serialization is wrong: \n" +
|
||||
Utils.HEX.encode(reserialized) + " vs \n" +
|
||||
Utils.HEX.encode(payloadBytes));
|
||||
}
|
||||
}
|
||||
|
||||
Message(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
|
||||
this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, false, false, UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
Message(NetworkParameters params, byte[] payload, int offset, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException {
|
||||
this(params, payload, offset, NetworkParameters.PROTOCOL_VERSION, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
// These methods handle the serialization/deserialization using the custom Bitcoin protocol.
|
||||
// It's somewhat painful to work with in Java, so some of these objects support a second
|
||||
// serialization mechanism - the standard Java serialization system. This is used when things
|
||||
// are serialized to the wallet.
|
||||
abstract void parse() throws ProtocolException;
|
||||
|
||||
/**
|
||||
* Perform the most minimal parse possible to calculate the length of the message payload.
|
||||
* This is only required for subclasses of ChildMessage as root level messages will have their length passed
|
||||
* into the constructor.
|
||||
* <p/>
|
||||
* Implementations should adhere to the following contract: If parseLazy = true the 'length'
|
||||
* field must be set before returning. If parseLazy = false the length field must be set either
|
||||
* within the parseLite() method OR the parse() method. The overriding requirement is that length
|
||||
* must be set to non UNKNOWN_MESSAGE value by the time the constructor exits.
|
||||
*
|
||||
* @return
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
protected abstract void parseLite() throws ProtocolException;
|
||||
|
||||
/**
|
||||
* Ensure the object is parsed if needed. This should be called in every getter before returning a value.
|
||||
* If the lazy parse flag is not set this is a method returns immediately.
|
||||
*/
|
||||
protected synchronized void maybeParse() {
|
||||
if (parsed || payload == null)
|
||||
return;
|
||||
try {
|
||||
parse();
|
||||
parsed = true;
|
||||
if (!parseRetain)
|
||||
payload = null;
|
||||
} catch (ProtocolException e) {
|
||||
throw new LazyParseException("ProtocolException caught during lazy parse. For safe access to fields call ensureParsed before attempting read or write access", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In lazy parsing mode access to getters and setters may throw an unchecked LazyParseException. If guaranteed safe access is required
|
||||
* this method will force parsing to occur immediately thus ensuring LazyParseExeption will never be thrown from this Message.
|
||||
* If the Message contains child messages (e.g. a Block containing Transaction messages) this will not force child messages to parse.
|
||||
* <p/>
|
||||
* This could be overidden for Transaction and it's child classes to ensure the entire tree of Message objects is parsed.
|
||||
*
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public void ensureParsed() throws ProtocolException {
|
||||
try {
|
||||
maybeParse();
|
||||
} catch (LazyParseException e) {
|
||||
if (e.getCause() instanceof ProtocolException)
|
||||
throw (ProtocolException) e.getCause();
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called before any change of internal values including any setters. This ensures any cached byte array is
|
||||
* removed after performing a lazy parse if necessary to ensure the object is fully populated.
|
||||
* <p/>
|
||||
* Child messages of this object(e.g. Transactions belonging to a Block) will not have their internal byte caches
|
||||
* invalidated unless they are also modified internally.
|
||||
*/
|
||||
protected void unCache() {
|
||||
maybeParse();
|
||||
checksum = null;
|
||||
payload = null;
|
||||
recached = false;
|
||||
}
|
||||
|
||||
protected void adjustLength(int newArraySize, int adjustment) {
|
||||
if (length == UNKNOWN_LENGTH)
|
||||
return;
|
||||
// Our own length is now unknown if we have an unknown length adjustment.
|
||||
if (adjustment == UNKNOWN_LENGTH) {
|
||||
length = UNKNOWN_LENGTH;
|
||||
return;
|
||||
}
|
||||
length += adjustment;
|
||||
// Check if we will need more bytes to encode the length prefix.
|
||||
if (newArraySize == 1)
|
||||
length++; // The assumption here is we never call adjustLength with the same arraySize as before.
|
||||
else if (newArraySize != 0)
|
||||
length += VarInt.sizeOf(newArraySize) - VarInt.sizeOf(newArraySize - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* used for unit testing
|
||||
*/
|
||||
public boolean isParsed() {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* used for unit testing
|
||||
*/
|
||||
public boolean isCached() {
|
||||
return payload != null;
|
||||
}
|
||||
|
||||
public boolean isRecached() {
|
||||
return recached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only used by BitcoinSerializer for cached checksum
|
||||
*
|
||||
* @return the checksum
|
||||
*/
|
||||
byte[] getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only used by BitcoinSerializer for caching checksum
|
||||
*
|
||||
* @param checksum the checksum to set
|
||||
*/
|
||||
void setChecksum(byte[] checksum) {
|
||||
if (checksum.length != 4)
|
||||
throw new IllegalArgumentException("Checksum length must be 4 bytes, actual length: " + checksum.length);
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the array returned by {@link Message#unsafeBitcoinSerialize()}, which is safe to mutate.
|
||||
* If you need extra performance and can guarantee you won't write to the array, you can use the unsafe version.
|
||||
*
|
||||
* @return a freshly allocated serialized byte array
|
||||
*/
|
||||
public byte[] bitcoinSerialize() {
|
||||
byte[] bytes = unsafeBitcoinSerialize();
|
||||
byte[] copy = new byte[bytes.length];
|
||||
System.arraycopy(bytes, 0, copy, 0, bytes.length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize this message to a byte array that conforms to the bitcoin wire protocol.
|
||||
* <br/>
|
||||
* This method may return the original byte array used to construct this message if the
|
||||
* following conditions are met:
|
||||
* <ol>
|
||||
* <li>1) The message was parsed from a byte array with parseRetain = true</li>
|
||||
* <li>2) The message has not been modified</li>
|
||||
* <li>3) The array had an offset of 0 and no surplus bytes</li>
|
||||
* </ol>
|
||||
*
|
||||
* If condition 3 is not met then an copy of the relevant portion of the array will be returned.
|
||||
* Otherwise a full serialize will occur. For this reason you should only use this API if you can guarantee you
|
||||
* will treat the resulting array as read only.
|
||||
*
|
||||
* @return a byte array owned by this object, do NOT mutate it.
|
||||
*/
|
||||
public byte[] unsafeBitcoinSerialize() {
|
||||
// 1st attempt to use a cached array.
|
||||
if (payload != null) {
|
||||
if (offset == 0 && length == payload.length) {
|
||||
// Cached byte array is the entire message with no extras so we can return as is and avoid an array
|
||||
// copy.
|
||||
return payload;
|
||||
}
|
||||
|
||||
byte[] buf = new byte[length];
|
||||
System.arraycopy(payload, offset, buf, 0, length);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// No cached array available so serialize parts by stream.
|
||||
ByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(length < 32 ? 32 : length + 32);
|
||||
try {
|
||||
bitcoinSerializeToStream(stream);
|
||||
} catch (IOException e) {
|
||||
// Cannot happen, we are serializing to a memory stream.
|
||||
}
|
||||
|
||||
if (parseRetain) {
|
||||
// A free set of steak knives!
|
||||
// If there happens to be a call to this method we gain an opportunity to recache
|
||||
// the byte array and in this case it contains no bytes from parent messages.
|
||||
// This give a dual benefit. Releasing references to the larger byte array so that it
|
||||
// it is more likely to be GC'd. And preventing double serializations. E.g. calculating
|
||||
// merkle root calls this method. It is will frequently happen prior to serializing the block
|
||||
// which means another call to bitcoinSerialize is coming. If we didn't recache then internal
|
||||
// serialization would occur a 2nd time and every subsequent time the message is serialized.
|
||||
payload = stream.toByteArray();
|
||||
cursor = cursor - offset;
|
||||
offset = 0;
|
||||
recached = true;
|
||||
length = payload.length;
|
||||
return payload;
|
||||
}
|
||||
// Record length. If this Message wasn't parsed from a byte stream it won't have length field
|
||||
// set (except for static length message types). Setting it makes future streaming more efficient
|
||||
// because we can preallocate the ByteArrayOutputStream buffer and avoid resizing.
|
||||
byte[] buf = stream.toByteArray();
|
||||
length = buf.length;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize this message to the provided OutputStream using the bitcoin wire format.
|
||||
*
|
||||
* @param stream
|
||||
* @throws IOException
|
||||
*/
|
||||
final public void bitcoinSerialize(OutputStream stream) throws IOException {
|
||||
// 1st check for cached bytes.
|
||||
if (payload != null && length != UNKNOWN_LENGTH) {
|
||||
stream.write(payload, offset, length);
|
||||
return;
|
||||
}
|
||||
|
||||
bitcoinSerializeToStream(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this message to the provided stream. If you just want the raw bytes use bitcoinSerialize().
|
||||
*/
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
log.error("Error: {} class has not implemented bitcoinSerializeToStream method. Generating message with no payload", getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is a NOP for all classes except Block and Transaction. It is only declared in Message
|
||||
* so BitcoinSerializer can avoid 2 instanceof checks + a casting.
|
||||
*/
|
||||
public Sha256Hash getHash() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be overridden to extract correct message size in the case of lazy parsing. Until this method is
|
||||
* implemented in a subclass of ChildMessage lazy parsing may have no effect.
|
||||
*
|
||||
* This default implementation is a safe fall back that will ensure it returns a correct value by parsing the message.
|
||||
*/
|
||||
public int getMessageSize() {
|
||||
if (length != UNKNOWN_LENGTH)
|
||||
return length;
|
||||
maybeParse();
|
||||
if (length == UNKNOWN_LENGTH)
|
||||
checkState(false, "Length field has not been set in %s after full parse.", getClass().getSimpleName());
|
||||
return length;
|
||||
}
|
||||
|
||||
long readUint32() throws ProtocolException {
|
||||
try {
|
||||
long u = Utils.readUint32(payload, cursor);
|
||||
cursor += 4;
|
||||
return u;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Sha256Hash readHash() throws ProtocolException {
|
||||
try {
|
||||
byte[] hash = new byte[32];
|
||||
System.arraycopy(payload, cursor, hash, 0, 32);
|
||||
// We have to flip it around, as it's been read off the wire in little endian.
|
||||
// Not the most efficient way to do this but the clearest.
|
||||
hash = Utils.reverseBytes(hash);
|
||||
cursor += 32;
|
||||
return new Sha256Hash(hash);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
long readInt64() throws ProtocolException {
|
||||
try {
|
||||
long u = Utils.readInt64(payload, cursor);
|
||||
cursor += 8;
|
||||
return u;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
BigInteger readUint64() throws ProtocolException {
|
||||
try {
|
||||
// Java does not have an unsigned 64 bit type. So scrape it off the wire then flip.
|
||||
byte[] valbytes = new byte[8];
|
||||
System.arraycopy(payload, cursor, valbytes, 0, 8);
|
||||
valbytes = Utils.reverseBytes(valbytes);
|
||||
cursor += valbytes.length;
|
||||
return new BigInteger(valbytes);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
long readVarInt() throws ProtocolException {
|
||||
return readVarInt(0);
|
||||
}
|
||||
|
||||
long readVarInt(int offset) throws ProtocolException {
|
||||
try {
|
||||
VarInt varint = new VarInt(payload, cursor + offset);
|
||||
cursor += offset + varint.getOriginalSizeInBytes();
|
||||
return varint.value;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] readBytes(int length) throws ProtocolException {
|
||||
if (length > MAX_SIZE) {
|
||||
throw new ProtocolException("Claimed byte array length too large: " + length);
|
||||
}
|
||||
try {
|
||||
byte[] b = new byte[length];
|
||||
System.arraycopy(payload, cursor, b, 0, length);
|
||||
cursor += length;
|
||||
return b;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] readByteArray() throws ProtocolException {
|
||||
long len = readVarInt();
|
||||
return readBytes((int)len);
|
||||
}
|
||||
|
||||
String readStr() throws ProtocolException {
|
||||
try {
|
||||
VarInt varInt = new VarInt(payload, cursor);
|
||||
if (varInt.value == 0) {
|
||||
cursor += 1;
|
||||
return "";
|
||||
}
|
||||
cursor += varInt.getOriginalSizeInBytes();
|
||||
if (varInt.value > MAX_SIZE) {
|
||||
throw new ProtocolException("Claimed var_str length too large: " + varInt.value);
|
||||
}
|
||||
byte[] characters = new byte[(int) varInt.value];
|
||||
System.arraycopy(payload, cursor, characters, 0, characters.length);
|
||||
cursor += characters.length;
|
||||
try {
|
||||
return new String(characters, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // Cannot happen, UTF-8 is always supported.
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new ProtocolException(e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasMoreBytes() {
|
||||
return cursor < payload.length;
|
||||
}
|
||||
|
||||
/** Network parameters this message was created with. */
|
||||
public NetworkParameters getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public static class LazyParseException extends RuntimeException {
|
||||
private static final long serialVersionUID = 6971943053112975594L;
|
||||
|
||||
public LazyParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LazyParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Classes implementing this interface represent a monetary value, such as a Bitcoin or fiat amount.
|
||||
*/
|
||||
public interface Monetary extends Serializable {
|
||||
|
||||
/**
|
||||
* Returns the absolute value of exponent of the value of a "smallest unit" in scientific notation. For Bitcoin, a
|
||||
* satoshi is worth 1E-8 so this would be 8.
|
||||
*/
|
||||
int smallestUnitExponent();
|
||||
|
||||
/**
|
||||
* Returns the number of "smallest units" of this monetary value. For Bitcoin, this would be the number of satoshis.
|
||||
*/
|
||||
long getValue();
|
||||
|
||||
int signum();
|
||||
}
|
@ -1,394 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.*;
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.script.ScriptOpCodes;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.math.LongMath;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Coin.*;
|
||||
|
||||
/**
|
||||
* <p>NetworkParameters contains the data needed for working with an instantiation of a Bitcoin chain.</p>
|
||||
*
|
||||
* <p>This is an abstract class, concrete instantiations can be found in the params package. There are four:
|
||||
* one for the main network ({@link MainNetParams}), one for the public test network, and two others that are
|
||||
* intended for unit testing and local app development purposes. Although this class contains some aliases for
|
||||
* them, you are encouraged to call the static get() methods on each specific params class directly.</p>
|
||||
*/
|
||||
public abstract class NetworkParameters implements Serializable {
|
||||
/**
|
||||
* The protocol version this library implements.
|
||||
*/
|
||||
public static final int PROTOCOL_VERSION = 70003;
|
||||
|
||||
/**
|
||||
* The alert signing key originally owned by Satoshi, and now passed on to Gavin along with a few others.
|
||||
*/
|
||||
public static final byte[] SATOSHI_KEY = Hex.decode("04d4da7a5dae4db797d9b0644d57a5cd50e05a70f36091cd62e2fc41c98ded06340be5a43a35e185690cd9cde5d72da8f6d065b499b06f51dcfba14aad859f443a");
|
||||
|
||||
/** The string returned by getId() for the main, production network where people trade things. */
|
||||
public static final String ID_MAINNET = "org.dogecoin.production";
|
||||
/** The string returned by getId() for the testnet. */
|
||||
public static final String ID_TESTNET = "org.dogecoin.test";
|
||||
/** The string returned by getId() for regtest mode. */
|
||||
public static final String ID_REGTEST = "org.dogecoin.regtest";
|
||||
/** Unit test network. */
|
||||
public static final String ID_UNITTESTNET = "org.dogecoinj.unittest";
|
||||
|
||||
/** The string used by the payment protocol to represent the main net. */
|
||||
public static final String PAYMENT_PROTOCOL_ID_MAINNET = "main";
|
||||
/** The string used by the payment protocol to represent the test net. */
|
||||
public static final String PAYMENT_PROTOCOL_ID_TESTNET = "test";
|
||||
/** The string used by the payment protocol to represent unit testing (note that this is non-standard). */
|
||||
public static final String PAYMENT_PROTOCOL_ID_UNIT_TESTS = "unittest";
|
||||
public static final String PAYMENT_PROTOCOL_ID_REGTEST = "regtest";
|
||||
|
||||
// TODO: Seed nodes should be here as well.
|
||||
|
||||
protected Block genesisBlock;
|
||||
protected BigInteger maxTarget;
|
||||
protected int port;
|
||||
protected long packetMagic; // Indicates message origin network and is used to seek to the next message when stream state is unknown.
|
||||
protected int addressHeader;
|
||||
protected int p2shHeader;
|
||||
protected int dumpedPrivateKeyHeader;
|
||||
protected int interval;
|
||||
protected int newInterval;
|
||||
protected int targetTimespan;
|
||||
protected int newTargetTimespan;
|
||||
protected int diffChangeTarget;
|
||||
protected byte[] alertSigningKey;
|
||||
protected int bip32HeaderPub;
|
||||
protected int bip32HeaderPriv;
|
||||
|
||||
/**
|
||||
* See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically
|
||||
* by looking at the port number.
|
||||
*/
|
||||
protected String id;
|
||||
|
||||
/**
|
||||
* The depth of blocks required for a coinbase transaction to be spendable.
|
||||
*/
|
||||
protected int spendableCoinbaseDepth;
|
||||
protected int subsidyDecreaseBlockCount;
|
||||
|
||||
protected int[] acceptableAddressCodes;
|
||||
protected String[] dnsSeeds;
|
||||
protected Map<Integer, Sha256Hash> checkpoints = new HashMap<Integer, Sha256Hash>();
|
||||
|
||||
protected NetworkParameters() {
|
||||
alertSigningKey = SATOSHI_KEY;
|
||||
genesisBlock = createGenesis(this);
|
||||
}
|
||||
|
||||
private static Block createGenesis(NetworkParameters n) {
|
||||
Block genesisBlock = new Block(n);
|
||||
Transaction t = new Transaction(n);
|
||||
try {
|
||||
// A script containing the difficulty bits and the following message:
|
||||
//
|
||||
// "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
|
||||
byte[] bytes = Utils.HEX.decode
|
||||
("04ffff001d0104084e696e746f6e646f");
|
||||
t.addInput(new TransactionInput(n, t, bytes));
|
||||
ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
|
||||
Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode
|
||||
("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"));
|
||||
scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG);
|
||||
t.addOutput(new TransactionOutput(n, t, COIN.multiply(88), scriptPubKeyBytes.toByteArray()));
|
||||
} catch (Exception e) {
|
||||
// Cannot happen.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
genesisBlock.addTransaction(t);
|
||||
return genesisBlock;
|
||||
}
|
||||
|
||||
public static final int TARGET_TIMESPAN = 4 * 60 * 60; // 2 weeks per difficulty cycle, on average.
|
||||
public static final int TARGET_TIMESPAN_NEW = 60; // 60s per difficulty cycle, on average. Kicks in after block 145k.
|
||||
public static final int TARGET_SPACING = 1 * 60; // 10 minutes per block.
|
||||
public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
|
||||
public static final int INTERVAL_NEW = TARGET_TIMESPAN_NEW / TARGET_SPACING;
|
||||
|
||||
/**
|
||||
* Blocks with a timestamp after this should enforce BIP 16, aka "Pay to script hash". This BIP changed the
|
||||
* network rules in a soft-forking manner, that is, blocks that don't follow the rules are accepted but not
|
||||
* mined upon and thus will be quickly re-orged out as long as the majority are enforcing the rule.
|
||||
*/
|
||||
public static final int BIP16_ENFORCE_TIME = 1333238400;
|
||||
|
||||
/**
|
||||
* The maximum number of coins to be generated
|
||||
* This is 92bn! We don't expect single transactions or blocks with that value.
|
||||
* Therefore we can assume this will suffice.
|
||||
*/
|
||||
public static final long MAX_COINS = Long.MAX_VALUE / LongMath.pow(10, Coin.SMALLEST_UNIT_EXPONENT);
|
||||
|
||||
/**
|
||||
* The maximum money to be generated
|
||||
*/
|
||||
public static final Coin MAX_MONEY = Coin.valueOf(Long.MAX_VALUE);
|
||||
|
||||
/** Alias for TestNet3Params.get(), use that instead. */
|
||||
@Deprecated
|
||||
public static NetworkParameters testNet() {
|
||||
return TestNet3Params.get();
|
||||
}
|
||||
|
||||
/** Alias for TestNet2Params.get(), use that instead. */
|
||||
@Deprecated
|
||||
public static NetworkParameters testNet2() {
|
||||
return TestNet2Params.get();
|
||||
}
|
||||
|
||||
/** Alias for TestNet3Params.get(), use that instead. */
|
||||
@Deprecated
|
||||
public static NetworkParameters testNet3() {
|
||||
return TestNet3Params.get();
|
||||
}
|
||||
|
||||
/** Alias for MainNetParams.get(), use that instead */
|
||||
@Deprecated
|
||||
public static NetworkParameters prodNet() {
|
||||
return MainNetParams.get();
|
||||
}
|
||||
|
||||
/** Returns a testnet params modified to allow any difficulty target. */
|
||||
@Deprecated
|
||||
public static NetworkParameters unitTests() {
|
||||
return UnitTestParams.get();
|
||||
}
|
||||
|
||||
/** Returns a standard regression test params (similar to unitTests) */
|
||||
@Deprecated
|
||||
public static NetworkParameters regTests() {
|
||||
return RegTestParams.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Java package style string acting as unique ID for these parameters
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public abstract String getPaymentProtocolId();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
NetworkParameters other = (NetworkParameters) o;
|
||||
return getId().equals(other.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(getId());
|
||||
}
|
||||
|
||||
/** Returns the network parameters for the given string ID or NULL if not recognized. */
|
||||
@Nullable
|
||||
public static NetworkParameters fromID(String id) {
|
||||
if (id.equals(ID_MAINNET)) {
|
||||
return MainNetParams.get();
|
||||
} else if (id.equals(ID_TESTNET)) {
|
||||
return TestNet3Params.get();
|
||||
} else if (id.equals(ID_UNITTESTNET)) {
|
||||
return UnitTestParams.get();
|
||||
} else if (id.equals(ID_REGTEST)) {
|
||||
return RegTestParams.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the network parameters for the given string paymentProtocolID or NULL if not recognized. */
|
||||
@Nullable
|
||||
public static NetworkParameters fromPmtProtocolID(String pmtProtocolId) {
|
||||
if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_MAINNET)) {
|
||||
return MainNetParams.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_TESTNET)) {
|
||||
return TestNet3Params.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_UNIT_TESTS)) {
|
||||
return UnitTestParams.get();
|
||||
} else if (pmtProtocolId.equals(PAYMENT_PROTOCOL_ID_REGTEST)) {
|
||||
return RegTestParams.get();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getSpendableCoinbaseDepth() {
|
||||
return spendableCoinbaseDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the block height is either not a checkpoint, or is a checkpoint and the hash matches.
|
||||
*/
|
||||
public boolean passesCheckpoint(int height, Sha256Hash hash) {
|
||||
Sha256Hash checkpointHash = checkpoints.get(height);
|
||||
return checkpointHash == null || checkpointHash.equals(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given height has a recorded checkpoint.
|
||||
*/
|
||||
public boolean isCheckpoint(int height) {
|
||||
Sha256Hash checkpointHash = checkpoints.get(height);
|
||||
return checkpointHash != null;
|
||||
}
|
||||
|
||||
public int getSubsidyDecreaseBlockCount() {
|
||||
return subsidyDecreaseBlockCount;
|
||||
}
|
||||
|
||||
/** Returns DNS names that when resolved, give IP addresses of active peers. */
|
||||
public String[] getDnsSeeds() {
|
||||
return dnsSeeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Genesis block for this chain.</p>
|
||||
*
|
||||
* <p>The first block in every chain is a well known constant shared between all Bitcoin implemenetations. For a
|
||||
* block to be valid, it must be eventually possible to work backwards to the genesis block by following the
|
||||
* prevBlockHash pointers in the block headers.</p>
|
||||
*
|
||||
* <p>The genesis blocks for both test and main networks contain the timestamp of when they were created,
|
||||
* and a message in the coinbase transaction. It says, <i>"The Times 03/Jan/2009 Chancellor on brink of second
|
||||
* bailout for banks"</i>.</p>
|
||||
*/
|
||||
public Block getGenesisBlock() {
|
||||
return genesisBlock;
|
||||
}
|
||||
|
||||
/** Default TCP port on which to connect to nodes. */
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/** The header bytes that identify the start of a packet on this network. */
|
||||
public long getPacketMagic() {
|
||||
return packetMagic;
|
||||
}
|
||||
|
||||
/**
|
||||
* First byte of a base58 encoded address. See {@link com.dogecoin.dogecoinj.core.Address}. This is the same as acceptableAddressCodes[0] and
|
||||
* is the one used for "normal" addresses. Other types of address may be encountered with version codes found in
|
||||
* the acceptableAddressCodes array.
|
||||
*/
|
||||
public int getAddressHeader() {
|
||||
return addressHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* First byte of a base58 encoded P2SH address. P2SH addresses are defined as part of BIP0013.
|
||||
*/
|
||||
public int getP2SHHeader() {
|
||||
return p2shHeader;
|
||||
}
|
||||
|
||||
/** First byte of a base58 encoded dumped private key. See {@link com.dogecoin.dogecoinj.core.DumpedPrivateKey}. */
|
||||
public int getDumpedPrivateKeyHeader() {
|
||||
return dumpedPrivateKeyHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much time in seconds is supposed to pass between "interval" blocks. If the actual elapsed time is
|
||||
* significantly different from this value, the network difficulty formula will produce a different value.
|
||||
* Dogecoin before block 145k used 4 hours.
|
||||
*/
|
||||
public int getTargetTimespan() {
|
||||
return targetTimespan;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much time in seconds is supposed to pass between "interval" blocks. If the actual elapsed time is
|
||||
* significantly different from this value, the network difficulty formula will produce a different value.
|
||||
* Dogecoin after block 145k uses 60 seconds.
|
||||
*/
|
||||
public int getNewTargetTimespan() {
|
||||
return newTargetTimespan;
|
||||
}
|
||||
|
||||
/**
|
||||
* The version codes that prefix addresses which are acceptable on this network. Although Satoshi intended these to
|
||||
* be used for "versioning", in fact they are today used to discriminate what kind of data is contained in the
|
||||
* address and to prevent accidentally sending coins across chains which would destroy them.
|
||||
*/
|
||||
public int[] getAcceptableAddressCodes() {
|
||||
return acceptableAddressCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are running in testnet-in-a-box mode, we allow connections to nodes with 0 non-genesis blocks.
|
||||
*/
|
||||
public boolean allowEmptyPeerChain() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** How many blocks pass between difficulty adjustment periods. Bitcoin standardises this to be 2015. */
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/** How many blocks pass between difficulty adjustment periods. After new diff algo. */
|
||||
public int getNewInterval() {
|
||||
return newInterval;
|
||||
}
|
||||
|
||||
/** Target for switch to new diff algo */
|
||||
public int getDiffChangeTarget() {
|
||||
return diffChangeTarget;
|
||||
}
|
||||
|
||||
/** Maximum target represents the easiest allowable proof of work. */
|
||||
public BigInteger getMaxTarget() {
|
||||
return maxTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* The key used to sign {@link com.dogecoin.dogecoinj.core.AlertMessage}s. You can use {@link com.dogecoin.dogecoinj.core.ECKey#verify(byte[], byte[], byte[])} to verify
|
||||
* signatures using it.
|
||||
*/
|
||||
public byte[] getAlertSigningKey() {
|
||||
return alertSigningKey;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 (HD) wallet - public key part. */
|
||||
public int getBip32HeaderPub() {
|
||||
return bip32HeaderPub;
|
||||
}
|
||||
|
||||
/** Returns the 4 byte header for BIP32 (HD) wallet - private key part. */
|
||||
public int getBip32HeaderPriv() {
|
||||
return bip32HeaderPriv;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sent by a peer when a getdata request doesn't find the requested data in the mempool. It has the same format
|
||||
* as an inventory message and lists the hashes of the missing items.
|
||||
*/
|
||||
public class NotFoundMessage extends InventoryMessage {
|
||||
public static int MIN_PROTOCOL_VERSION = 70001;
|
||||
|
||||
public NotFoundMessage(NetworkParameters params) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
public NotFoundMessage(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes);
|
||||
}
|
||||
|
||||
public NotFoundMessage(NetworkParameters params, List<InventoryItem> items) {
|
||||
super(params);
|
||||
this.items = new ArrayList<InventoryItem>(items);
|
||||
}
|
||||
}
|
@ -1,298 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 The Bitcoin Developers
|
||||
* Copyright 2012 Matt Corallo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.*;
|
||||
|
||||
/**
|
||||
* <p>A data structure that contains proofs of block inclusion for one or more transactions, in an efficient manner.</p>
|
||||
*
|
||||
* <p>The encoding works as follows: we traverse the tree in depth-first order, storing a bit for each traversed node,
|
||||
* signifying whether the node is the parent of at least one matched leaf txid (or a matched txid itself). In case we
|
||||
* are at the leaf level, or this bit is 0, its merkle node hash is stored, and its children are not explored further.
|
||||
* Otherwise, no hash is stored, but we recurse into both (or the only) child branch. During decoding, the same
|
||||
* depth-first traversal is performed, consuming bits and hashes as they were written during encoding.</p>
|
||||
*
|
||||
* <p>The serialization is fixed and provides a hard guarantee about the encoded size,
|
||||
* <tt>SIZE <= 10 + ceil(32.25*N)</tt> where N represents the number of leaf nodes of the partial tree. N itself
|
||||
* is bounded by:</p>
|
||||
*
|
||||
* <p>
|
||||
* N <= total_transactions<br>
|
||||
* N <= 1 + matched_transactions*tree_height
|
||||
* </p>
|
||||
*
|
||||
* <p><pre>The serialization format:
|
||||
* - uint32 total_transactions (4 bytes)
|
||||
* - varint number of hashes (1-3 bytes)
|
||||
* - uint256[] hashes in depth-first order (<= 32*N bytes)
|
||||
* - varint number of bytes of flag bits (1-3 bytes)
|
||||
* - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits)
|
||||
* The size constraints follow from this.</pre></p>
|
||||
*/
|
||||
public class PartialMerkleTree extends Message {
|
||||
// the total number of transactions in the block
|
||||
private int transactionCount;
|
||||
|
||||
// node-is-parent-of-matched-txid bits
|
||||
private byte[] matchedChildBits;
|
||||
|
||||
// txids and internal hashes
|
||||
private List<Sha256Hash> hashes;
|
||||
|
||||
public PartialMerkleTree(NetworkParameters params, byte[] payloadBytes, int offset) throws ProtocolException {
|
||||
super(params, payloadBytes, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new PMT with the given bit set (little endian) and the raw list of hashes including internal hashes,
|
||||
* taking ownership of the list.
|
||||
*/
|
||||
public PartialMerkleTree(NetworkParameters params, byte[] bits, List<Sha256Hash> hashes, int origTxCount) {
|
||||
super(params);
|
||||
this.matchedChildBits = bits;
|
||||
this.hashes = hashes;
|
||||
this.transactionCount = origTxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a PMT given the list of leaf hashes and which leaves need to be included. The relevant interior hashes
|
||||
* are calculated and a new PMT returned.
|
||||
*/
|
||||
public static PartialMerkleTree buildFromLeaves(NetworkParameters params, byte[] includeBits, List<Sha256Hash> allLeafHashes) {
|
||||
// Calculate height of the tree.
|
||||
int height = 0;
|
||||
while (getTreeWidth(allLeafHashes.size(), height) > 1)
|
||||
height++;
|
||||
List<Boolean> bitList = new ArrayList<Boolean>();
|
||||
List<Sha256Hash> hashes = new ArrayList<Sha256Hash>();
|
||||
traverseAndBuild(height, 0, allLeafHashes, includeBits, bitList, hashes);
|
||||
byte[] bits = new byte[(int)Math.ceil(bitList.size() / 8.0)];
|
||||
for (int i = 0; i < bitList.size(); i++)
|
||||
if (bitList.get(i))
|
||||
Utils.setBitLE(bits, i);
|
||||
return new PartialMerkleTree(params, bits, hashes, allLeafHashes.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
uint32ToByteStreamLE(transactionCount, stream);
|
||||
|
||||
stream.write(new VarInt(hashes.size()).encode());
|
||||
for (Sha256Hash hash : hashes)
|
||||
stream.write(reverseBytes(hash.getBytes()));
|
||||
|
||||
stream.write(new VarInt(matchedChildBits.length).encode());
|
||||
stream.write(matchedChildBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
transactionCount = (int)readUint32();
|
||||
|
||||
int nHashes = (int) readVarInt();
|
||||
hashes = new ArrayList<Sha256Hash>(nHashes);
|
||||
for (int i = 0; i < nHashes; i++)
|
||||
hashes.add(readHash());
|
||||
|
||||
int nFlagBytes = (int) readVarInt();
|
||||
matchedChildBits = readBytes(nFlagBytes);
|
||||
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
// Based on CPartialMerkleTree::TraverseAndBuild in Bitcoin Core.
|
||||
private static void traverseAndBuild(int height, int pos, List<Sha256Hash> allLeafHashes, byte[] includeBits,
|
||||
List<Boolean> matchedChildBits, List<Sha256Hash> resultHashes) {
|
||||
boolean parentOfMatch = false;
|
||||
// Is this node a parent of at least one matched hash?
|
||||
for (int p = pos << height; p < (pos+1) << height && p < allLeafHashes.size(); p++) {
|
||||
if (Utils.checkBitLE(includeBits, p)) {
|
||||
parentOfMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Store as a flag bit.
|
||||
matchedChildBits.add(parentOfMatch);
|
||||
if (height == 0 || !parentOfMatch) {
|
||||
// If at height 0, or nothing interesting below, store hash and stop.
|
||||
resultHashes.add(calcHash(height, pos, allLeafHashes));
|
||||
} else {
|
||||
// Otherwise descend into the subtrees.
|
||||
int h = height - 1;
|
||||
int p = pos * 2;
|
||||
traverseAndBuild(h, p, allLeafHashes, includeBits, matchedChildBits, resultHashes);
|
||||
if (p + 1 < getTreeWidth(allLeafHashes.size(), h))
|
||||
traverseAndBuild(h, p + 1, allLeafHashes, includeBits, matchedChildBits, resultHashes);
|
||||
}
|
||||
}
|
||||
|
||||
private static Sha256Hash calcHash(int height, int pos, List<Sha256Hash> hashes) {
|
||||
if (height == 0) {
|
||||
// Hash at height 0 is just the regular tx hash itself.
|
||||
return hashes.get(pos);
|
||||
}
|
||||
int h = height - 1;
|
||||
int p = pos * 2;
|
||||
Sha256Hash left = calcHash(h, p, hashes);
|
||||
// Calculate right hash if not beyond the end of the array - copy left hash otherwise.
|
||||
Sha256Hash right;
|
||||
if (p + 1 < getTreeWidth(hashes.size(), h)) {
|
||||
right = calcHash(h, p + 1, hashes);
|
||||
} else {
|
||||
right = left;
|
||||
}
|
||||
return combineLeftRight(left.getBytes(), right.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() {
|
||||
|
||||
}
|
||||
|
||||
// helper function to efficiently calculate the number of nodes at given height in the merkle tree
|
||||
private static int getTreeWidth(int transactionCount, int height) {
|
||||
return (transactionCount + (1 << height) - 1) >> height;
|
||||
}
|
||||
|
||||
private static class ValuesUsed {
|
||||
public int bitsUsed = 0, hashesUsed = 0;
|
||||
}
|
||||
|
||||
// recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild.
|
||||
// it returns the hash of the respective node.
|
||||
private Sha256Hash recursiveExtractHashes(int height, int pos, ValuesUsed used, List<Sha256Hash> matchedHashes) throws VerificationException {
|
||||
if (used.bitsUsed >= matchedChildBits.length*8) {
|
||||
// overflowed the bits array - failure
|
||||
throw new VerificationException("PartialMerkleTree overflowed its bits array");
|
||||
}
|
||||
boolean parentOfMatch = checkBitLE(matchedChildBits, used.bitsUsed++);
|
||||
if (height == 0 || !parentOfMatch) {
|
||||
// if at height 0, or nothing interesting below, use stored hash and do not descend
|
||||
if (used.hashesUsed >= hashes.size()) {
|
||||
// overflowed the hash array - failure
|
||||
throw new VerificationException("PartialMerkleTree overflowed its hash array");
|
||||
}
|
||||
Sha256Hash hash = hashes.get(used.hashesUsed++);
|
||||
if (height == 0 && parentOfMatch) // in case of height 0, we have a matched txid
|
||||
matchedHashes.add(hash);
|
||||
return hash;
|
||||
} else {
|
||||
// otherwise, descend into the subtrees to extract matched txids and hashes
|
||||
byte[] left = recursiveExtractHashes(height - 1, pos * 2, used, matchedHashes).getBytes(), right;
|
||||
if (pos * 2 + 1 < getTreeWidth(transactionCount, height-1)) {
|
||||
right = recursiveExtractHashes(height - 1, pos * 2 + 1, used, matchedHashes).getBytes();
|
||||
if (Arrays.equals(right, left))
|
||||
throw new VerificationException("Invalid merkle tree with duplicated left/right branches");
|
||||
} else {
|
||||
right = left;
|
||||
}
|
||||
// and combine them before returning
|
||||
return combineLeftRight(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
private static Sha256Hash combineLeftRight(byte[] left, byte[] right) {
|
||||
return new Sha256Hash(reverseBytes(doubleDigestTwoBuffers(
|
||||
reverseBytes(left), 0, 32,
|
||||
reverseBytes(right), 0, 32)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts tx hashes that are in this merkle tree
|
||||
* and returns the merkle root of this tree.
|
||||
*
|
||||
* The returned root should be checked against the
|
||||
* merkle root contained in the block header for security.
|
||||
*
|
||||
* @param matchedHashesOut A list which will contain the matched txn (will be cleared).
|
||||
* @return the merkle root of this merkle tree
|
||||
* @throws ProtocolException if this partial merkle tree is invalid
|
||||
*/
|
||||
public Sha256Hash getTxnHashAndMerkleRoot(List<Sha256Hash> matchedHashesOut) throws VerificationException {
|
||||
matchedHashesOut.clear();
|
||||
|
||||
// An empty set will not work
|
||||
if (transactionCount == 0)
|
||||
throw new VerificationException("Got a CPartialMerkleTree with 0 transactions");
|
||||
// check for excessively high numbers of transactions
|
||||
if (transactionCount > Block.MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction
|
||||
throw new VerificationException("Got a CPartialMerkleTree with more transactions than is possible");
|
||||
// there can never be more hashes provided than one for every txid
|
||||
if (hashes.size() > transactionCount)
|
||||
throw new VerificationException("Got a CPartialMerkleTree with more hashes than transactions");
|
||||
// there must be at least one bit per node in the partial tree, and at least one node per hash
|
||||
if (matchedChildBits.length*8 < hashes.size())
|
||||
throw new VerificationException("Got a CPartialMerkleTree with fewer matched bits than hashes");
|
||||
// calculate height of tree
|
||||
int height = 0;
|
||||
while (getTreeWidth(transactionCount, height) > 1)
|
||||
height++;
|
||||
// traverse the partial tree
|
||||
ValuesUsed used = new ValuesUsed();
|
||||
Sha256Hash merkleRoot = recursiveExtractHashes(height, 0, used, matchedHashesOut);
|
||||
// verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
|
||||
if ((used.bitsUsed+7)/8 != matchedChildBits.length ||
|
||||
// verify that all hashes were consumed
|
||||
used.hashesUsed != hashes.size())
|
||||
throw new VerificationException("Got a CPartialMerkleTree that didn't need all the data it provided");
|
||||
|
||||
return merkleRoot;
|
||||
}
|
||||
|
||||
public int getTransactionCount() {
|
||||
return transactionCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
PartialMerkleTree tree = (PartialMerkleTree) o;
|
||||
|
||||
if (transactionCount != tree.transactionCount) return false;
|
||||
if (!hashes.equals(tree.hashes)) return false;
|
||||
if (!Arrays.equals(matchedChildBits, tree.matchedChildBits)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = transactionCount;
|
||||
result = 31 * result + Arrays.hashCode(matchedChildBits);
|
||||
result = 31 * result + hashes.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PartialMerkleTree{" +
|
||||
"transactionCount=" + transactionCount +
|
||||
", matchedChildBits=" + Arrays.toString(matchedChildBits) +
|
||||
", hashes=" + hashes +
|
||||
'}';
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,241 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.google.common.net.InetAddresses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.uint32ToByteStreamLE;
|
||||
import static com.dogecoin.dogecoinj.core.Utils.uint64ToByteStreamLE;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A PeerAddress holds an IP address and port number representing the network location of
|
||||
* a peer in the Bitcoin P2P network. It exists primarily for serialization purposes.
|
||||
*/
|
||||
public class PeerAddress extends ChildMessage {
|
||||
private static final long serialVersionUID = 7501293709324197411L;
|
||||
static final int MESSAGE_SIZE = 30;
|
||||
|
||||
private InetAddress addr;
|
||||
private int port;
|
||||
private BigInteger services;
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* Construct a peer address from a serialized payload.
|
||||
*/
|
||||
public PeerAddress(NetworkParameters params, byte[] payload, int offset, int protocolVersion) throws ProtocolException {
|
||||
super(params, payload, offset, protocolVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a peer address from a serialized payload.
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param protocolVersion Bitcoin protocol version.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public PeerAddress(NetworkParameters params, byte[] payload, int offset, int protocolVersion, Message parent, boolean parseLazy,
|
||||
boolean parseRetain) throws ProtocolException {
|
||||
super(params, payload, offset, protocolVersion, parent, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||
// Message length is calculated in parseLite which is guaranteed to be called before it is ever read.
|
||||
// Even though message length is static for a PeerAddress it is safer to leave it there
|
||||
// as it will be set regardless of which constructor was used.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a peer address from a memorized or hardcoded address.
|
||||
*/
|
||||
public PeerAddress(InetAddress addr, int port, int protocolVersion) {
|
||||
this.addr = checkNotNull(addr);
|
||||
this.port = port;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.services = BigInteger.ZERO;
|
||||
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a peer address from the given IP address and port. Protocol version is the default.
|
||||
*/
|
||||
public PeerAddress(InetAddress addr, int port) {
|
||||
this(addr, port, NetworkParameters.PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a peer address from the given IP address. Port and protocol version are default for the mainnet.
|
||||
*/
|
||||
public PeerAddress(InetAddress addr) {
|
||||
this(addr, MainNetParams.get().getPort());
|
||||
}
|
||||
|
||||
public PeerAddress(InetSocketAddress addr) {
|
||||
this(addr.getAddress(), addr.getPort());
|
||||
}
|
||||
|
||||
public static PeerAddress localhost(NetworkParameters params) {
|
||||
return new PeerAddress(InetAddresses.forString("127.0.0.1"), params.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
if (protocolVersion >= 31402) {
|
||||
//TODO this appears to be dynamic because the client only ever sends out it's own address
|
||||
//so assumes itself to be up. For a fuller implementation this needs to be dynamic only if
|
||||
//the address refers to this client.
|
||||
int secs = (int) (Utils.currentTimeSeconds());
|
||||
uint32ToByteStreamLE(secs, stream);
|
||||
}
|
||||
uint64ToByteStreamLE(services, stream); // nServices.
|
||||
// Java does not provide any utility to map an IPv4 address into IPv6 space, so we have to do it by hand.
|
||||
byte[] ipBytes = addr.getAddress();
|
||||
if (ipBytes.length == 4) {
|
||||
byte[] v6addr = new byte[16];
|
||||
System.arraycopy(ipBytes, 0, v6addr, 12, 4);
|
||||
v6addr[10] = (byte) 0xFF;
|
||||
v6addr[11] = (byte) 0xFF;
|
||||
ipBytes = v6addr;
|
||||
}
|
||||
stream.write(ipBytes);
|
||||
// And write out the port. Unlike the rest of the protocol, address and port is in big endian byte order.
|
||||
stream.write((byte) (0xFF & port >> 8));
|
||||
stream.write((byte) (0xFF & port));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() {
|
||||
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parse() throws ProtocolException {
|
||||
// Format of a serialized address:
|
||||
// uint32 timestamp
|
||||
// uint64 services (flags determining what the node can do)
|
||||
// 16 bytes ip address
|
||||
// 2 bytes port num
|
||||
if (protocolVersion > 31402)
|
||||
time = readUint32();
|
||||
else
|
||||
time = -1;
|
||||
services = readUint64();
|
||||
byte[] addrBytes = readBytes(16);
|
||||
try {
|
||||
addr = InetAddress.getByAddress(addrBytes);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
port = ((0xFF & payload[cursor++]) << 8) | (0xFF & payload[cursor++]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMessageSize() {
|
||||
// The 4 byte difference is the uint32 timestamp that was introduced in version 31402
|
||||
length = protocolVersion > 31402 ? MESSAGE_SIZE : MESSAGE_SIZE - 4;
|
||||
return length;
|
||||
}
|
||||
|
||||
public InetAddress getAddr() {
|
||||
maybeParse();
|
||||
return addr;
|
||||
}
|
||||
|
||||
public InetSocketAddress getSocketAddress() {
|
||||
return new InetSocketAddress(getAddr(), getPort());
|
||||
}
|
||||
|
||||
public void setAddr(InetAddress addr) {
|
||||
unCache();
|
||||
this.addr = addr;
|
||||
}
|
||||
|
||||
|
||||
public int getPort() {
|
||||
maybeParse();
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
public void setPort(int port) {
|
||||
unCache();
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
|
||||
public BigInteger getServices() {
|
||||
maybeParse();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
public void setServices(BigInteger services) {
|
||||
unCache();
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
|
||||
public long getTime() {
|
||||
maybeParse();
|
||||
return time;
|
||||
}
|
||||
|
||||
|
||||
public void setTime(long time) {
|
||||
unCache();
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + addr.getHostAddress() + "]:" + port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PeerAddress other = (PeerAddress) o;
|
||||
return other.addr.equals(addr) &&
|
||||
other.port == port &&
|
||||
other.services.equals(services) &&
|
||||
other.time == time;
|
||||
//TODO: including services and time could cause same peer to be added multiple times in collections
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return addr.hashCode() ^ port ^ (int) time ^ services.hashCode();
|
||||
}
|
||||
|
||||
public InetSocketAddress toSocketAddress() {
|
||||
return new InetSocketAddress(addr, port);
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>Implementors can listen to events like blocks being downloaded/transactions being broadcast/connect/disconnects,
|
||||
* they can pre-filter messages before they are procesesed by a {@link Peer} or {@link PeerGroup}, and they can
|
||||
* provide transactions to remote peers when they ask for them.</p>
|
||||
*/
|
||||
public interface PeerEventListener {
|
||||
/**
|
||||
* <p>Called when peers are discovered, this happens at startup of {@link PeerGroup} or if we run out of
|
||||
* suitable {@link Peer}s to connect to.</p>
|
||||
*
|
||||
* @param peerAddresses the set of discovered {@link PeerAddress}es
|
||||
*/
|
||||
public void onPeersDiscovered(Set<PeerAddress> peerAddresses);
|
||||
|
||||
/**
|
||||
* Called on a Peer thread when a block is received.<p>
|
||||
*
|
||||
* The block may have transactions or may be a header only once getheaders is implemented.
|
||||
*
|
||||
* @param peer the peer receiving the block
|
||||
* @param block the downloaded block
|
||||
* @param blocksLeft the number of blocks left to download
|
||||
*/
|
||||
public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft);
|
||||
|
||||
/**
|
||||
* Called when a download is started with the initial number of blocks to be downloaded.
|
||||
*
|
||||
* @param peer the peer receiving the block
|
||||
* @param blocksLeft the number of blocks left to download
|
||||
*/
|
||||
public void onChainDownloadStarted(Peer peer, int blocksLeft);
|
||||
|
||||
/**
|
||||
* Called when a peer is connected. If this listener is registered to a {@link Peer} instead of a {@link PeerGroup},
|
||||
* peerCount will always be 1.
|
||||
*
|
||||
* @param peer
|
||||
* @param peerCount the total number of connected peers
|
||||
*/
|
||||
public void onPeerConnected(Peer peer, int peerCount);
|
||||
|
||||
/**
|
||||
* Called when a peer is disconnected. Note that this won't be called if the listener is registered on a
|
||||
* {@link PeerGroup} and the group is in the process of shutting down. If this listener is registered to a
|
||||
* {@link Peer} instead of a {@link PeerGroup}, peerCount will always be 0. This handler can be called without
|
||||
* a corresponding invocation of onPeerConnected if the initial connection is never successful.
|
||||
*
|
||||
* @param peer
|
||||
* @param peerCount the total number of connected peers
|
||||
*/
|
||||
public void onPeerDisconnected(Peer peer, int peerCount);
|
||||
|
||||
/**
|
||||
* <p>Called when a message is received by a peer, before the message is processed. The returned message is
|
||||
* processed instead. Returning null will cause the message to be ignored by the Peer returning the same message
|
||||
* object allows you to see the messages received but not change them. The result from one event listeners
|
||||
* callback is passed as "m" to the next, forming a chain.</p>
|
||||
*
|
||||
* <p>Note that this will never be called if registered with any executor other than
|
||||
* {@link com.dogecoin.dogecoinj.utils.Threading#SAME_THREAD}</p>
|
||||
*/
|
||||
public Message onPreMessageReceived(Peer peer, Message m);
|
||||
|
||||
/**
|
||||
* Called when a new transaction is broadcast over the network.
|
||||
*/
|
||||
public void onTransaction(Peer peer, Transaction t);
|
||||
|
||||
/**
|
||||
* <p>Called when a peer receives a getdata message, usually in response to an "inv" being broadcast. Return as many
|
||||
* items as possible which appear in the {@link GetDataMessage}, or null if you're not interested in responding.</p>
|
||||
*
|
||||
* <p>Note that this will never be called if registered with any executor other than
|
||||
* {@link com.dogecoin.dogecoinj.utils.Threading#SAME_THREAD}</p>
|
||||
*/
|
||||
@Nullable
|
||||
public List<Message> getData(Peer peer, GetDataMessage m);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* Thrown when a problem occurs in communicating with a peer, and we should
|
||||
* retry.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class PeerException extends Exception {
|
||||
public PeerException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public PeerException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public PeerException(String msg, Exception e) {
|
||||
super(msg, e);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
/**
|
||||
* An interface which provides the information required to properly filter data downloaded from Peers.
|
||||
* Note that an implementer is responsible for calling {@link PeerGroup#recalculateFastCatchupAndFilter(com.dogecoin.dogecoinj.core.PeerGroup.FilterRecalculateMode)}
|
||||
* whenever a change occurs which effects the data provided via this interface.
|
||||
*/
|
||||
public interface PeerFilterProvider {
|
||||
/**
|
||||
* Returns the earliest timestamp (seconds since epoch) for which full/bloom-filtered blocks must be downloaded.
|
||||
* Blocks with timestamps before this time will only have headers downloaded. 0 requires that all blocks be
|
||||
* downloaded, and thus this should default to {@link System#currentTimeMillis()}/1000.
|
||||
*/
|
||||
public long getEarliestKeyCreationTime();
|
||||
|
||||
/**
|
||||
* Called on all registered filter providers before getBloomFilterElementCount and getBloomFilter are called.
|
||||
* Once called, the provider should ensure that the items it will want to insert into the filter don't change.
|
||||
* The reason is that all providers will have their element counts queried, and then a filter big enough for
|
||||
* all of them will be specified. So the provider must use consistent state. There is guaranteed to be a matching
|
||||
* call to endBloomFilterCalculation that can be used to e.g. unlock a lock.
|
||||
*/
|
||||
public void beginBloomFilterCalculation();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of elements that will be added to a bloom filter returned by
|
||||
* {@link PeerFilterProvider#getBloomFilter(int, double, long)}
|
||||
*/
|
||||
public int getBloomFilterElementCount();
|
||||
|
||||
/**
|
||||
* Gets a bloom filter that contains all the necessary elements for the listener to receive relevant transactions.
|
||||
* Default value should be an empty bloom filter with the given size, falsePositiveRate, and nTweak.
|
||||
*/
|
||||
public BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak);
|
||||
|
||||
/** Whether this filter provider depends on the server updating the filter on all matches */
|
||||
public boolean isRequiringUpdateAllBloomFilter();
|
||||
|
||||
public void endBloomFilterCalculation();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,235 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.net.AbstractTimeoutHandler;
|
||||
import com.dogecoin.dogecoinj.net.MessageWriteTarget;
|
||||
import com.dogecoin.dogecoinj.net.StreamParser;
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* Handles high-level message (de)serialization for peers, acting as the bridge between the
|
||||
* {@link com.dogecoin.dogecoinj.net} classes and {@link Peer}.
|
||||
*/
|
||||
public abstract class PeerSocketHandler extends AbstractTimeoutHandler implements StreamParser {
|
||||
private static final Logger log = LoggerFactory.getLogger(PeerSocketHandler.class);
|
||||
|
||||
private final BitcoinSerializer serializer;
|
||||
protected PeerAddress peerAddress;
|
||||
// If we close() before we know our writeTarget, set this to true to call writeTarget.closeConnection() right away.
|
||||
private boolean closePending = false;
|
||||
// writeTarget will be thread-safe, and may call into PeerGroup, which calls us, so we should call it unlocked
|
||||
@VisibleForTesting protected MessageWriteTarget writeTarget = null;
|
||||
|
||||
// The ByteBuffers passed to us from the writeTarget are static in size, and usually smaller than some messages we
|
||||
// will receive. For SPV clients, this should be rare (ie we're mostly dealing with small transactions), but for
|
||||
// messages which are larger than the read buffer, we have to keep a temporary buffer with its bytes.
|
||||
private byte[] largeReadBuffer;
|
||||
private int largeReadBufferPos;
|
||||
private BitcoinSerializer.BitcoinPacketHeader header;
|
||||
|
||||
private Lock lock = Threading.lock("PeerSocketHandler");
|
||||
|
||||
public PeerSocketHandler(NetworkParameters params, InetSocketAddress remoteIp) {
|
||||
serializer = new BitcoinSerializer(checkNotNull(params));
|
||||
this.peerAddress = new PeerAddress(remoteIp);
|
||||
}
|
||||
|
||||
public PeerSocketHandler(NetworkParameters params, PeerAddress peerAddress) {
|
||||
serializer = new BitcoinSerializer(checkNotNull(params));
|
||||
this.peerAddress = checkNotNull(peerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given message to the peer. Due to the asynchronousness of network programming, there is no guarantee
|
||||
* the peer will have received it. Throws NotYetConnectedException if we are not yet connected to the remote peer.
|
||||
* TODO: Maybe use something other than the unchecked NotYetConnectedException here
|
||||
*/
|
||||
public void sendMessage(Message message) throws NotYetConnectedException {
|
||||
lock.lock();
|
||||
try {
|
||||
if (writeTarget == null)
|
||||
throw new NotYetConnectedException();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
// TODO: Some round-tripping could be avoided here
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
serializer.serialize(message, out);
|
||||
writeTarget.writeBytes(out.toByteArray());
|
||||
} catch (IOException e) {
|
||||
exceptionCaught(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the peer if one exists, or immediately closes the connection as soon as it opens
|
||||
*/
|
||||
public void close() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (writeTarget == null) {
|
||||
closePending = true;
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
writeTarget.closeConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void timeoutOccurred() {
|
||||
log.info("{}: Timed out", getAddress());
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every time a message is received from the network
|
||||
*/
|
||||
protected abstract void processMessage(Message m) throws Exception;
|
||||
|
||||
@Override
|
||||
public int receiveBytes(ByteBuffer buff) {
|
||||
checkArgument(buff.position() == 0 &&
|
||||
buff.capacity() >= BitcoinSerializer.BitcoinPacketHeader.HEADER_LENGTH + 4);
|
||||
try {
|
||||
// Repeatedly try to deserialize messages until we hit a BufferUnderflowException
|
||||
for (int i = 0; true; i++) {
|
||||
// If we are in the middle of reading a message, try to fill that one first, before we expect another
|
||||
if (largeReadBuffer != null) {
|
||||
// This can only happen in the first iteration
|
||||
checkState(i == 0);
|
||||
// Read new bytes into the largeReadBuffer
|
||||
int bytesToGet = Math.min(buff.remaining(), largeReadBuffer.length - largeReadBufferPos);
|
||||
buff.get(largeReadBuffer, largeReadBufferPos, bytesToGet);
|
||||
largeReadBufferPos += bytesToGet;
|
||||
// Check the largeReadBuffer's status
|
||||
if (largeReadBufferPos == largeReadBuffer.length) {
|
||||
// ...processing a message if one is available
|
||||
processMessage(serializer.deserializePayload(header, ByteBuffer.wrap(largeReadBuffer)));
|
||||
largeReadBuffer = null;
|
||||
header = null;
|
||||
} else // ...or just returning if we don't have enough bytes yet
|
||||
return buff.position();
|
||||
}
|
||||
// Now try to deserialize any messages left in buff
|
||||
Message message;
|
||||
int preSerializePosition = buff.position();
|
||||
try {
|
||||
message = serializer.deserialize(buff);
|
||||
} catch (BufferUnderflowException e) {
|
||||
// If we went through the whole buffer without a full message, we need to use the largeReadBuffer
|
||||
if (i == 0 && buff.limit() == buff.capacity()) {
|
||||
// ...so reposition the buffer to 0 and read the next message header
|
||||
buff.position(0);
|
||||
try {
|
||||
serializer.seekPastMagicBytes(buff);
|
||||
header = serializer.deserializeHeader(buff);
|
||||
// Initialize the largeReadBuffer with the next message's size and fill it with any bytes
|
||||
// left in buff
|
||||
largeReadBuffer = new byte[header.size];
|
||||
largeReadBufferPos = buff.remaining();
|
||||
buff.get(largeReadBuffer, 0, largeReadBufferPos);
|
||||
} catch (BufferUnderflowException e1) {
|
||||
// If we went through a whole buffer's worth of bytes without getting a header, give up
|
||||
// In cases where the buff is just really small, we could create a second largeReadBuffer
|
||||
// that we use to deserialize the magic+header, but that is rather complicated when the buff
|
||||
// should probably be at least that big anyway (for efficiency)
|
||||
throw new ProtocolException("No magic bytes+header after reading " + buff.capacity() + " bytes");
|
||||
}
|
||||
} else {
|
||||
// Reposition the buffer to its original position, which saves us from skipping messages by
|
||||
// seeking past part of the magic bytes before all of them are in the buffer
|
||||
buff.position(preSerializePosition);
|
||||
}
|
||||
return buff.position();
|
||||
}
|
||||
// Process our freshly deserialized message
|
||||
processMessage(message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
exceptionCaught(e);
|
||||
return -1; // Returning -1 also throws an IllegalStateException upstream and kills the connection
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link MessageWriteTarget} used to write messages to the peer. This should almost never be called, it is
|
||||
* called automatically by {@link com.dogecoin.dogecoinj.net.NioClient} or
|
||||
* {@link com.dogecoin.dogecoinj.net.NioClientManager} once the socket finishes initialization.
|
||||
*/
|
||||
@Override
|
||||
public void setWriteTarget(MessageWriteTarget writeTarget) {
|
||||
checkArgument(writeTarget != null);
|
||||
lock.lock();
|
||||
boolean closeNow = false;
|
||||
try {
|
||||
checkArgument(this.writeTarget == null);
|
||||
closeNow = closePending;
|
||||
this.writeTarget = writeTarget;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
if (closeNow)
|
||||
writeTarget.closeConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMessageSize() {
|
||||
return Message.MAX_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the IP address and port of peer.
|
||||
*/
|
||||
public PeerAddress getAddress() {
|
||||
return peerAddress;
|
||||
}
|
||||
|
||||
/** Catch any exceptions, logging them and then closing the channel. */
|
||||
private void exceptionCaught(Exception e) {
|
||||
PeerAddress addr = getAddress();
|
||||
String s = addr == null ? "?" : addr.toString();
|
||||
if (e instanceof ConnectException || e instanceof IOException) {
|
||||
// Short message for network errors
|
||||
log.info(s + " - " + e.getMessage());
|
||||
} else {
|
||||
log.warn(s + " - ", e);
|
||||
Thread.UncaughtExceptionHandler handler = Threading.uncaughtExceptionHandler;
|
||||
if (handler != null)
|
||||
handler.uncaughtException(Thread.currentThread(), e);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Noa Resare
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class Ping extends Message {
|
||||
private long nonce;
|
||||
private boolean hasNonce;
|
||||
|
||||
public Ping(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Ping with a nonce value.
|
||||
* Only use this if the remote node has a protocol version > 60000
|
||||
*/
|
||||
public Ping(long nonce) {
|
||||
this.nonce = nonce;
|
||||
this.hasNonce = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Ping without a nonce value.
|
||||
* Only use this if the remote node has a protocol version <= 60000
|
||||
*/
|
||||
public Ping() {
|
||||
this.hasNonce = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
if (hasNonce)
|
||||
Utils.int64ToByteStreamLE(nonce, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
try {
|
||||
nonce = readInt64();
|
||||
hasNonce = true;
|
||||
} catch(ProtocolException e) {
|
||||
hasNonce = false;
|
||||
}
|
||||
length = hasNonce ? 8 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() {
|
||||
|
||||
}
|
||||
|
||||
public boolean hasNonce() {
|
||||
return hasNonce;
|
||||
}
|
||||
|
||||
public long getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 Matt Corallo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class Pong extends Message {
|
||||
/** The smallest protocol version that supports the pong response (BIP 31). Anything beyond version 60000. */
|
||||
public static final int MIN_PROTOCOL_VERSION = 60003;
|
||||
|
||||
private long nonce;
|
||||
|
||||
public Pong(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Pong with a nonce value.
|
||||
* Only use this if the remote node has a protocol version > 60000
|
||||
*/
|
||||
public Pong(long nonce) {
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
nonce = readInt64();
|
||||
length = 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
Utils.int64ToByteStreamLE(nonce, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() {
|
||||
}
|
||||
|
||||
/** Returns the nonce sent by the remote peer. */
|
||||
public long getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ProtocolException extends VerificationException {
|
||||
|
||||
public ProtocolException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ProtocolException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public ProtocolException(String msg, Exception e) {
|
||||
super(msg, e);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
// TODO: Rename PrunedException to something like RequiredDataWasPrunedException
|
||||
|
||||
/**
|
||||
* PrunedException is thrown in cases where a fully verifying node has deleted (pruned) old block data that turned
|
||||
* out to be necessary for handling a re-org. Normally this should never happen unless you're playing with the testnet
|
||||
* as the pruning parameters should be set very conservatively, such that an absolutely enormous re-org would be
|
||||
* required to trigger it.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class PrunedException extends Exception {
|
||||
private Sha256Hash hash;
|
||||
public PrunedException(Sha256Hash hash) {
|
||||
super(hash.toString());
|
||||
this.hash = hash;
|
||||
}
|
||||
public Sha256Hash getHash() {
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matt Corallo
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* A message sent by nodes when a message we sent was rejected (ie a transaction had too little fee/was invalid/etc)
|
||||
*/
|
||||
public class RejectMessage extends Message {
|
||||
private static final long serialVersionUID = -5246995579800334336L;
|
||||
|
||||
private String message, reason;
|
||||
public static enum RejectCode {
|
||||
/** The message was not able to be parsed */
|
||||
MALFORMED((byte) 0x01),
|
||||
/** The message described an invalid object */
|
||||
INVALID((byte) 0x10),
|
||||
/** The message was obsolete or described an object which is obsolete (eg unsupported, old version, v1 block) */
|
||||
OBSOLETE((byte) 0x11),
|
||||
/**
|
||||
* The message was relayed multiple times or described an object which is in conflict with another.
|
||||
* This message can describe errors in protocol implementation or the presence of an attempt to DOUBLE SPEND.
|
||||
*/
|
||||
DUPLICATE((byte) 0x12),
|
||||
/**
|
||||
* The message described an object was not standard and was thus not accepted.
|
||||
* The reference client has a concept of standard transaction forms, which describe scripts and encodings which
|
||||
* it is willing to relay further. Other transactions are neither relayed nor mined, though they are considered
|
||||
* valid if they appear in a block.
|
||||
*/
|
||||
NONSTANDARD((byte) 0x40),
|
||||
/**
|
||||
* This refers to a specific form of NONSTANDARD transactions, which have an output smaller than some constant
|
||||
* defining them as dust (this is no longer used).
|
||||
*/
|
||||
DUST((byte) 0x41),
|
||||
/** The messages described an object which did not have sufficient fee to be relayed further. */
|
||||
INSUFFICIENTFEE((byte) 0x42),
|
||||
/** The message described a block which was invalid according to hard-coded checkpoint blocks. */
|
||||
CHECKPOINT((byte) 0x43),
|
||||
OTHER((byte) 0xff);
|
||||
|
||||
byte code;
|
||||
RejectCode(byte code) { this.code = code; }
|
||||
static RejectCode fromCode(byte code) {
|
||||
for (RejectCode rejectCode : RejectCode.values())
|
||||
if (rejectCode.code == code)
|
||||
return rejectCode;
|
||||
return OTHER;
|
||||
}
|
||||
}
|
||||
private RejectCode code;
|
||||
private Sha256Hash messageHash;
|
||||
|
||||
public RejectMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0);
|
||||
}
|
||||
|
||||
/** Constructs a reject message that fingers the object with the given hash as rejected for the given reason. */
|
||||
public RejectMessage(NetworkParameters params, RejectCode code, Sha256Hash hash, String message, String reason) throws ProtocolException {
|
||||
super(params);
|
||||
this.code = code;
|
||||
this.messageHash = hash;
|
||||
this.message = message;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
message = readStr();
|
||||
code = RejectCode.fromCode(readBytes(1)[0]);
|
||||
reason = readStr();
|
||||
if (message.equals("block") || message.equals("tx"))
|
||||
messageHash = readHash();
|
||||
length = cursor - offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws ProtocolException {
|
||||
if (length == UNKNOWN_LENGTH)
|
||||
parseLite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
byte[] messageBytes = message.getBytes("UTF-8");
|
||||
stream.write(new VarInt(messageBytes.length).encode());
|
||||
stream.write(messageBytes);
|
||||
stream.write(code.code);
|
||||
byte[] reasonBytes = reason.getBytes("UTF-8");
|
||||
stream.write(new VarInt(reasonBytes.length).encode());
|
||||
stream.write(reasonBytes);
|
||||
if (message.equals("block") || message.equals("tx"))
|
||||
stream.write(Utils.reverseBytes(messageHash.getBytes()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the type of message which was rejected by the peer.
|
||||
* Note that this is ENTIRELY UNTRUSTED and should be sanity-checked before it is printed or processed.
|
||||
*/
|
||||
public String getRejectedMessage() {
|
||||
ensureParsed();
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the hash of the rejected object (if getRejectedMessage() is either "tx" or "block"), otherwise null.
|
||||
*/
|
||||
public Sha256Hash getRejectedObjectHash() {
|
||||
ensureParsed();
|
||||
return messageHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason code given for why the peer rejected the message.
|
||||
*/
|
||||
public RejectCode getReasonCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason message given for rejection.
|
||||
* Note that this is ENTIRELY UNTRUSTED and should be sanity-checked before it is printed or processed.
|
||||
*/
|
||||
public String getReasonString() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A String representation of the relevant details of this reject message.
|
||||
* Be aware that the value returned by this method includes the value returned by
|
||||
* {@link #getReasonString() getReasonString}, which is taken from the reject message unchecked.
|
||||
* Through malice or otherwise, it might contain control characters or other harmful content.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
Sha256Hash hash = getRejectedObjectHash();
|
||||
if (hash != null)
|
||||
return String.format("Reject: %s %s for reason '%s' (%d)", getRejectedMessage(), getRejectedObjectHash(),
|
||||
getReasonString(), getReasonCode().code);
|
||||
else
|
||||
return String.format("Reject: %s for reason '%s' (%d)", getRejectedMessage(),
|
||||
getReasonString(), getReasonCode().code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RejectMessage other = (RejectMessage) o;
|
||||
return message.equals(other.message) &&
|
||||
code.equals(other.code) &&
|
||||
reason.equals(other.reason) &&
|
||||
messageHash.equals(other.messageHash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = message.hashCode();
|
||||
result = 31 * result + reason.hashCode();
|
||||
result = 31 * result + code.hashCode();
|
||||
result = 31 * result + messageHash.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Adam Mackler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* This exception is used by the TransactionBroadcast class to indicate that a broadcast
|
||||
* Transaction has been rejected by the network, for example because it violates a
|
||||
* protocol rule. Note that not all invalid transactions generate a reject message, and
|
||||
* some peers may never do so.
|
||||
*/
|
||||
public class RejectedTransactionException extends Exception {
|
||||
private Transaction tx;
|
||||
private RejectMessage rejectMessage;
|
||||
|
||||
public RejectedTransactionException(Transaction tx, RejectMessage rejectMessage) {
|
||||
super(rejectMessage.toString());
|
||||
this.tx = tx;
|
||||
this.rejectMessage = rejectMessage;
|
||||
}
|
||||
|
||||
/** Return the original Transaction object whose broadcast was rejected. */
|
||||
public Transaction getTransaction() { return tx; }
|
||||
|
||||
/** Return the RejectMessage object representing the broadcast rejection. */
|
||||
public RejectMessage getRejectMessage() { return rejectMessage; }
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ScriptException extends VerificationException {
|
||||
|
||||
public ScriptException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ScriptException(String msg, Exception e) {
|
||||
super(msg, e);
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* A Sha256Hash just wraps a byte[] so that equals and hashcode work correctly, allowing it to be used as keys in a
|
||||
* map. It also checks that the length is correct and provides a bit more type safety.
|
||||
*/
|
||||
public class Sha256Hash implements Serializable, Comparable<Sha256Hash> {
|
||||
private byte[] bytes;
|
||||
public static final Sha256Hash ZERO_HASH = new Sha256Hash(new byte[32]);
|
||||
|
||||
/**
|
||||
* Creates a Sha256Hash by wrapping the given byte array. It must be 32 bytes long. Takes ownership!
|
||||
*/
|
||||
public Sha256Hash(byte[] rawHashBytes) {
|
||||
checkArgument(rawHashBytes.length == 32);
|
||||
this.bytes = rawHashBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Sha256Hash by decoding the given hex string. It must be 64 characters long.
|
||||
*/
|
||||
public Sha256Hash(String hexString) {
|
||||
checkArgument(hexString.length() == 64);
|
||||
this.bytes = Utils.HEX.decode(hexString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the (one-time) hash of contents and returns it as a new wrapped hash.
|
||||
*/
|
||||
public static Sha256Hash create(byte[] contents) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
return new Sha256Hash(digest.digest(contents));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of the hash of the contents. This is a standard operation in Bitcoin.
|
||||
*/
|
||||
public static Sha256Hash createDouble(byte[] contents) {
|
||||
return new Sha256Hash(Utils.doubleDigest(contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash of the given files contents. Reads the file fully into memory before hashing so only use with
|
||||
* small files.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Sha256Hash hashFileContents(File f) throws IOException {
|
||||
FileInputStream in = new FileInputStream(f);
|
||||
try {
|
||||
return create(ByteStreams.toByteArray(in));
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Sha256Hash other = (Sha256Hash) o;
|
||||
return Arrays.equals(bytes, other.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash code of the byte array as calculated by {@link Arrays#hashCode()}. Note the difference between a SHA256
|
||||
* secure bytes and the type of quick/dirty bytes used by the Java hashCode method which is designed for use in
|
||||
* bytes tables.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Use the last 4 bytes, not the first 4 which are often zeros in Bitcoin.
|
||||
return (bytes[31] & 0xFF) | ((bytes[30] & 0xFF) << 8) | ((bytes[29] & 0xFF) << 16) | ((bytes[28] & 0xFF) << 24);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Utils.HEX.encode(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bytes interpreted as a positive integer.
|
||||
*/
|
||||
public BigInteger toBigInteger() {
|
||||
return new BigInteger(1, bytes);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public Sha256Hash duplicate() {
|
||||
return new Sha256Hash(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Sha256Hash o) {
|
||||
int thisCode = this.hashCode();
|
||||
int oCode = ((Sha256Hash)o).hashCode();
|
||||
return thisCode > oCode ? 1 : (thisCode == oCode ? 0 : -1);
|
||||
}
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.store.BlockStore;
|
||||
import com.dogecoin.dogecoinj.store.BlockStoreException;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* Wraps a {@link Block} object with extra data that can be derived from the block chain but is slow or inconvenient to
|
||||
* calculate. By storing it alongside the block header we reduce the amount of work required significantly.
|
||||
* Recalculation is slow because the fields are cumulative - to find the chainWork you have to iterate over every
|
||||
* block in the chain back to the genesis block, which involves lots of seeking/loading etc. So we just keep a
|
||||
* running total: it's a disk space vs cpu/io tradeoff.<p>
|
||||
*
|
||||
* StoredBlocks are put inside a {@link BlockStore} which saves them to memory or disk.
|
||||
*/
|
||||
public class StoredBlock implements Serializable {
|
||||
private static final long serialVersionUID = -6097565241243701771L;
|
||||
|
||||
// A BigInteger representing the total amount of work done so far on this chain. As of May 2011 it takes 8
|
||||
// bytes to represent this field, so 12 bytes should be plenty for now.
|
||||
public static final int CHAIN_WORK_BYTES = 12;
|
||||
public static final byte[] EMPTY_BYTES = new byte[CHAIN_WORK_BYTES];
|
||||
public static final int COMPACT_SERIALIZED_SIZE = Block.HEADER_SIZE + CHAIN_WORK_BYTES + 4; // for height
|
||||
|
||||
private Block header;
|
||||
private BigInteger chainWork;
|
||||
private int height;
|
||||
|
||||
public StoredBlock(Block header, BigInteger chainWork, int height) {
|
||||
this.header = header;
|
||||
this.chainWork = chainWork;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The block header this object wraps. The referenced block object must not have any transactions in it.
|
||||
*/
|
||||
public Block getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total sum of work done in this block, and all the blocks below it in the chain. Work is a measure of how
|
||||
* many tries are needed to solve a block. If the target is set to cover 10% of the total hash value space,
|
||||
* then the work represented by a block is 10.
|
||||
*/
|
||||
public BigInteger getChainWork() {
|
||||
return chainWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position in the chain for this block. The genesis block has a height of zero.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/** Returns true if this objects chainWork is higher than the others. */
|
||||
public boolean moreWorkThan(StoredBlock other) {
|
||||
return chainWork.compareTo(other.chainWork) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
StoredBlock other = (StoredBlock) o;
|
||||
return header.equals(other.header) &&
|
||||
chainWork.equals(other.chainWork) &&
|
||||
height == other.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// A better hashCode is possible, but this works for now.
|
||||
return header.hashCode() ^ chainWork.hashCode() ^ height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new StoredBlock, calculating the additional fields by adding to the values in this block.
|
||||
*/
|
||||
public StoredBlock build(Block block) throws VerificationException {
|
||||
// Stored blocks track total work done in this chain, because the canonical chain is the one that represents
|
||||
// the largest amount of work done not the tallest.
|
||||
BigInteger chainWork = this.chainWork.add(block.getWork());
|
||||
int height = this.height + 1;
|
||||
return new StoredBlock(block, chainWork, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/** Serializes the stored block to a custom packed format. Used by {@link CheckpointManager}. */
|
||||
public void serializeCompact(ByteBuffer buffer) {
|
||||
byte[] chainWorkBytes = getChainWork().toByteArray();
|
||||
checkState(chainWorkBytes.length <= CHAIN_WORK_BYTES, "Ran out of space to store chain work!");
|
||||
if (chainWorkBytes.length < CHAIN_WORK_BYTES) {
|
||||
// Pad to the right size.
|
||||
buffer.put(EMPTY_BYTES, 0, CHAIN_WORK_BYTES - chainWorkBytes.length);
|
||||
}
|
||||
buffer.put(chainWorkBytes);
|
||||
buffer.putInt(getHeight());
|
||||
// Using unsafeBitcoinSerialize here can give us direct access to the same bytes we read off the wire,
|
||||
// avoiding serialization round-trips.
|
||||
byte[] bytes = getHeader().unsafeBitcoinSerialize();
|
||||
buffer.put(bytes, 0, Block.HEADER_SIZE); // Trim the trailing 00 byte (zero transactions).
|
||||
}
|
||||
|
||||
/** De-serializes the stored block from a custom packed format. Used by {@link CheckpointManager}. */
|
||||
public static StoredBlock deserializeCompact(NetworkParameters params, ByteBuffer buffer) throws ProtocolException {
|
||||
byte[] chainWorkBytes = new byte[StoredBlock.CHAIN_WORK_BYTES];
|
||||
buffer.get(chainWorkBytes);
|
||||
BigInteger chainWork = new BigInteger(1, chainWorkBytes);
|
||||
int height = buffer.getInt(); // +4 bytes
|
||||
byte[] header = new byte[Block.HEADER_SIZE + 1]; // Extra byte for the 00 transactions length.
|
||||
buffer.get(header, 0, Block.HEADER_SIZE);
|
||||
return new StoredBlock(new Block(params, header), chainWork, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Block %s at height %d: %s",
|
||||
getHeader().getHashAsString(), getHeight(), getHeader().toString());
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 Matt Corallo.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* A StoredTransactionOutput message contains the information necessary to check a spending transaction.
|
||||
* It avoids having to store the entire parentTransaction just to get the hash and index.
|
||||
* Its only really useful for MemoryFullPrunedBlockStore, and should probably be moved there
|
||||
*/
|
||||
public class StoredTransactionOutput implements Serializable {
|
||||
private static final long serialVersionUID = -8744924157056340509L;
|
||||
|
||||
/**
|
||||
* A transaction output has some value and a script used for authenticating that the redeemer is allowed to spend
|
||||
* this output.
|
||||
*/
|
||||
private Coin value;
|
||||
private byte[] scriptBytes;
|
||||
|
||||
/** Hash of the transaction to which we refer. */
|
||||
private Sha256Hash hash;
|
||||
/** Which output of that transaction we are talking about. */
|
||||
private long index;
|
||||
|
||||
/** arbitrary value lower than -{@link NetworkParameters#spendableCoinbaseDepth}
|
||||
* (not too low to get overflows when we do blockHeight - NONCOINBASE_HEIGHT, though) */
|
||||
private static final int NONCOINBASE_HEIGHT = -200;
|
||||
/** The height of the creating block (for coinbases, NONCOINBASE_HEIGHT otherwise) */
|
||||
private int height;
|
||||
|
||||
/**
|
||||
* Creates a stored transaction output
|
||||
* @param hash the hash of the containing transaction
|
||||
* @param index the outpoint
|
||||
* @param value the value available
|
||||
* @param height the height this output was created in
|
||||
* @param scriptBytes
|
||||
*/
|
||||
public StoredTransactionOutput(Sha256Hash hash, long index, Coin value, int height, boolean isCoinbase, byte[] scriptBytes) {
|
||||
this.hash = hash;
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
|
||||
this.scriptBytes = scriptBytes;
|
||||
}
|
||||
|
||||
public StoredTransactionOutput(Sha256Hash hash, TransactionOutput out, int height, boolean isCoinbase) {
|
||||
this.hash = hash;
|
||||
this.index = out.getIndex();
|
||||
this.value = out.getValue();
|
||||
this.height = isCoinbase ? height : NONCOINBASE_HEIGHT;
|
||||
this.scriptBytes = out.getScriptBytes();
|
||||
}
|
||||
|
||||
public StoredTransactionOutput(InputStream in) throws IOException {
|
||||
byte[] valueBytes = new byte[8];
|
||||
if (in.read(valueBytes, 0, 8) != 8)
|
||||
throw new EOFException();
|
||||
value = Coin.valueOf(Utils.readInt64(valueBytes, 0));
|
||||
|
||||
int scriptBytesLength = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
scriptBytes = new byte[scriptBytesLength];
|
||||
if (in.read(scriptBytes) != scriptBytesLength)
|
||||
throw new EOFException();
|
||||
|
||||
byte[] hashBytes = new byte[32];
|
||||
if (in.read(hashBytes) != 32)
|
||||
throw new EOFException();
|
||||
hash = new Sha256Hash(hashBytes);
|
||||
|
||||
byte[] indexBytes = new byte[4];
|
||||
if (in.read(indexBytes) != 4)
|
||||
throw new EOFException();
|
||||
index = Utils.readUint32(indexBytes, 0);
|
||||
|
||||
height = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* The value which this Transaction output holds
|
||||
* @return the value
|
||||
*/
|
||||
public Coin getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The backing script bytes which can be turned into a Script object.
|
||||
* @return the scriptBytes
|
||||
*/
|
||||
public byte[] getScriptBytes() {
|
||||
return scriptBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash of the transaction which holds this output
|
||||
* @return the hash
|
||||
*/
|
||||
public Sha256Hash getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index of this output in the transaction which holds it
|
||||
* @return the index
|
||||
*/
|
||||
public long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of the block that created this output (or -1 if this output was not created by a coinbase)
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Stored TxOut of %s (%s:%d)", value.toFriendlyString(), hash.toString(), index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash.hashCode() + (int)index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
StoredTransactionOutput other = (StoredTransactionOutput) o;
|
||||
return getHash().equals(other.getHash()) &&
|
||||
getIndex() == other.getIndex();
|
||||
}
|
||||
|
||||
public void serializeToStream(OutputStream bos) throws IOException {
|
||||
Utils.uint64ToByteStreamLE(BigInteger.valueOf(value.value), bos);
|
||||
|
||||
bos.write(0xFF & scriptBytes.length >> 0);
|
||||
bos.write(0xFF & scriptBytes.length >> 8);
|
||||
bos.write(0xFF & (scriptBytes.length >> 16));
|
||||
bos.write(0xFF & (scriptBytes.length >> 24));
|
||||
bos.write(scriptBytes);
|
||||
|
||||
bos.write(hash.getBytes());
|
||||
Utils.uint32ToByteStreamLE(index, bos);
|
||||
|
||||
bos.write(0xFF & (height >> 0));
|
||||
bos.write(0xFF & (height >> 8));
|
||||
bos.write(0xFF & (height >> 16));
|
||||
bos.write(0xFF & (height >> 24));
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2012 Matt Corallo.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains minimal data neccessary to disconnect/connect the transactions
|
||||
* in the stored block at will. Can either store the full set of
|
||||
* transactions (if the inputs for the block have not been tested to work)
|
||||
* or the set of transaction outputs created/destroyed when the block is
|
||||
* connected.
|
||||
*/
|
||||
public class StoredUndoableBlock implements Serializable {
|
||||
private static final long serialVersionUID = 5127353027086786117L;
|
||||
|
||||
Sha256Hash blockHash;
|
||||
|
||||
// Only one of either txOutChanges or transactions will be set
|
||||
private TransactionOutputChanges txOutChanges;
|
||||
private List<Transaction> transactions;
|
||||
|
||||
public StoredUndoableBlock(Sha256Hash hash, TransactionOutputChanges txOutChanges) {
|
||||
this.blockHash = hash;
|
||||
this.transactions = null;
|
||||
this.txOutChanges = txOutChanges;
|
||||
}
|
||||
|
||||
public StoredUndoableBlock(Sha256Hash hash, List<Transaction> transactions) {
|
||||
this.blockHash = hash;
|
||||
this.txOutChanges = null;
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transaction output changes if they have been calculated, otherwise null.
|
||||
* Only one of this and getTransactions() will return a non-null value.
|
||||
*/
|
||||
public TransactionOutputChanges getTxOutChanges() {
|
||||
return txOutChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full list of transactions if it is stored, otherwise null.
|
||||
* Only one of this and getTxOutChanges() will return a non-null value.
|
||||
*/
|
||||
public List<Transaction> getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hash of the represented block
|
||||
*/
|
||||
public Sha256Hash getHash() {
|
||||
return blockHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return blockHash.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
StoredUndoableBlock other = (StoredUndoableBlock) o;
|
||||
return getHash().equals(other.getHash());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Undoable Block " + blockHash.toString();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Giannis Dzegoutanis
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.wallet.WalletTransaction;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface is used to abstract the {@link com.dogecoin.dogecoinj.core.Wallet} and the {@link com.dogecoin.dogecoinj.core.Transaction}
|
||||
*/
|
||||
public interface TransactionBag {
|
||||
/** Returns true if this wallet contains a public key which hashes to the given hash. */
|
||||
public boolean isPubKeyHashMine(byte[] pubkeyHash);
|
||||
|
||||
/** Returns true if this wallet is watching transactions for outputs with the script. */
|
||||
public boolean isWatchedScript(Script script);
|
||||
|
||||
/** Returns true if this wallet contains a keypair with the given public key. */
|
||||
public boolean isPubKeyMine(byte[] pubkey);
|
||||
|
||||
/** Returns true if this wallet knows the script corresponding to the given hash. */
|
||||
public boolean isPayToScriptHashMine(byte[] payToScriptHash);
|
||||
|
||||
/** Returns transactions from a specific pool. */
|
||||
public Map<Sha256Hash, Transaction> getTransactionPool(WalletTransaction.Pool pool);
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.annotations.*;
|
||||
import com.google.common.base.*;
|
||||
import com.google.common.util.concurrent.*;
|
||||
import com.dogecoin.dogecoinj.utils.*;
|
||||
import org.slf4j.*;
|
||||
|
||||
import javax.annotation.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a single transaction broadcast that we are performing. A broadcast occurs after a new transaction is created
|
||||
* (typically by a {@link Wallet} and needs to be sent to the network. A broadcast can succeed or fail. A success is
|
||||
* defined as seeing the transaction be announced by peers via inv messages, thus indicating their acceptance. A failure
|
||||
* is defined as not reaching acceptance within a timeout period, or getting an explicit reject message from a peer
|
||||
* indicating that the transaction was not acceptable.
|
||||
*/
|
||||
public class TransactionBroadcast {
|
||||
private static final Logger log = LoggerFactory.getLogger(TransactionBroadcast.class);
|
||||
|
||||
private final SettableFuture<Transaction> future = SettableFuture.create();
|
||||
private final PeerGroup peerGroup;
|
||||
private final Transaction tx;
|
||||
@Nullable private final Context context;
|
||||
private int minConnections;
|
||||
private int numWaitingFor;
|
||||
|
||||
/** Used for shuffling the peers before broadcast: unit tests can replace this to make themselves deterministic. */
|
||||
@VisibleForTesting
|
||||
public static Random random = new Random();
|
||||
|
||||
private Transaction pinnedTx;
|
||||
|
||||
// Tracks which nodes sent us a reject message about this broadcast, if any. Useful for debugging.
|
||||
private Map<Peer, RejectMessage> rejects = Collections.synchronizedMap(new HashMap<Peer, RejectMessage>());
|
||||
|
||||
// TODO: Context being owned by BlockChain isn't right w.r.t future intentions so it shouldn't really be optional here.
|
||||
TransactionBroadcast(PeerGroup peerGroup, @Nullable Context context, Transaction tx) {
|
||||
this.peerGroup = peerGroup;
|
||||
this.context = context;
|
||||
this.tx = tx;
|
||||
this.minConnections = Math.max(1, peerGroup.getMinBroadcastConnections());
|
||||
}
|
||||
|
||||
public ListenableFuture<Transaction> future() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public void setMinConnections(int minConnections) {
|
||||
this.minConnections = minConnections;
|
||||
}
|
||||
|
||||
private PeerEventListener rejectionListener = new AbstractPeerEventListener() {
|
||||
@Override
|
||||
public Message onPreMessageReceived(Peer peer, Message m) {
|
||||
if (m instanceof RejectMessage) {
|
||||
RejectMessage rejectMessage = (RejectMessage)m;
|
||||
if (tx.getHash().equals(rejectMessage.getRejectedObjectHash())) {
|
||||
rejects.put(peer, rejectMessage);
|
||||
int size = rejects.size();
|
||||
long threshold = Math.round(numWaitingFor / 2.0);
|
||||
if (size > threshold) {
|
||||
log.warn("Threshold for considering broadcast rejected has been reached ({}/{})", size, threshold);
|
||||
future.setException(new RejectedTransactionException(tx, rejectMessage));
|
||||
peerGroup.removeEventListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
public ListenableFuture<Transaction> broadcast() {
|
||||
peerGroup.addEventListener(rejectionListener, Threading.SAME_THREAD);
|
||||
log.info("Waiting for {} peers required for broadcast, we have {} ...", minConnections, peerGroup.getConnectedPeers().size());
|
||||
peerGroup.waitForPeers(minConnections).addListener(new EnoughAvailablePeers(), Threading.SAME_THREAD);
|
||||
return future;
|
||||
}
|
||||
|
||||
private class EnoughAvailablePeers implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
// We now have enough connected peers to send the transaction.
|
||||
// This can be called immediately if we already have enough. Otherwise it'll be called from a peer
|
||||
// thread.
|
||||
|
||||
// We will send the tx simultaneously to half the connected peers and wait to hear back from at least half
|
||||
// of the other half, i.e., with 4 peers connected we will send the tx to 2 randomly chosen peers, and then
|
||||
// wait for it to show up on one of the other two. This will be taken as sign of network acceptance. As can
|
||||
// be seen, 4 peers is probably too little - it doesn't taken many broken peers for tx propagation to have
|
||||
// a big effect.
|
||||
List<Peer> peers = peerGroup.getConnectedPeers(); // snapshots
|
||||
// We intern the tx here so we are using a canonical version of the object (as it's unfortunately mutable).
|
||||
// TODO: Once confidence state is moved out of Transaction we can kill off this step.
|
||||
pinnedTx = context != null ? context.getConfidenceTable().intern(tx) : tx;
|
||||
// Prepare to send the transaction by adding a listener that'll be called when confidence changes.
|
||||
// Only bother with this if we might actually hear back:
|
||||
if (minConnections > 1)
|
||||
pinnedTx.getConfidence().addEventListener(new ConfidenceChange());
|
||||
// Satoshis code sends an inv in this case and then lets the peer request the tx data. We just
|
||||
// blast out the TX here for a couple of reasons. Firstly it's simpler: in the case where we have
|
||||
// just a single connection we don't have to wait for getdata to be received and handled before
|
||||
// completing the future in the code immediately below. Secondly, it's faster. The reason the
|
||||
// Satoshi client sends an inv is privacy - it means you can't tell if the peer originated the
|
||||
// transaction or not. However, we are not a fully validating node and this is advertised in
|
||||
// our version message, as SPV nodes cannot relay it doesn't give away any additional information
|
||||
// to skip the inv here - we wouldn't send invs anyway.
|
||||
int numConnected = peers.size();
|
||||
int numToBroadcastTo = (int) Math.max(1, Math.round(Math.ceil(peers.size() / 2.0)));
|
||||
numWaitingFor = (int) Math.ceil((peers.size() - numToBroadcastTo) / 2.0);
|
||||
Collections.shuffle(peers, random);
|
||||
peers = peers.subList(0, numToBroadcastTo);
|
||||
log.info("broadcastTransaction: We have {} peers, adding {} to the memory pool and sending to {} peers, will wait for {}, sending to: {}",
|
||||
numConnected, tx.getHashAsString(), numToBroadcastTo, numWaitingFor, Joiner.on(",").join(peers));
|
||||
for (Peer peer : peers) {
|
||||
try {
|
||||
peer.sendMessage(pinnedTx);
|
||||
// We don't record the peer as having seen the tx in the memory pool because we want to track only
|
||||
// how many peers announced to us.
|
||||
} catch (Exception e) {
|
||||
log.error("Caught exception sending to {}", peer, e);
|
||||
}
|
||||
}
|
||||
// If we've been limited to talk to only one peer, we can't wait to hear back because the
|
||||
// remote peer won't tell us about transactions we just announced to it for obvious reasons.
|
||||
// So we just have to assume we're done, at that point. This happens when we're not given
|
||||
// any peer discovery source and the user just calls connectTo() once.
|
||||
if (minConnections == 1) {
|
||||
peerGroup.removeEventListener(rejectionListener);
|
||||
future.set(pinnedTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfidenceChange implements TransactionConfidence.Listener {
|
||||
@Override
|
||||
public void onConfidenceChanged(TransactionConfidence conf, ChangeReason reason) {
|
||||
// The number of peers that announced this tx has gone up.
|
||||
int numSeenPeers = conf.numBroadcastPeers() + rejects.size();
|
||||
boolean mined = tx.getAppearsInHashes() != null;
|
||||
log.info("broadcastTransaction: {}: TX {} seen by {} peers{}", reason, pinnedTx.getHashAsString(),
|
||||
numSeenPeers, mined ? " and mined" : "");
|
||||
if (numSeenPeers >= numWaitingFor || mined) {
|
||||
// We've seen the min required number of peers announce the transaction, or it was included
|
||||
// in a block. Normally we'd expect to see it fully propagate before it gets mined, but
|
||||
// it can be that a block is solved very soon after broadcast, and it's also possible that
|
||||
// due to version skew and changes in the relay rules our transaction is not going to
|
||||
// fully propagate yet can get mined anyway.
|
||||
//
|
||||
// Note that we can't wait for the current number of connected peers right now because we
|
||||
// could have added more peers after the broadcast took place, which means they won't
|
||||
// have seen the transaction. In future when peers sync up their memory pools after they
|
||||
// connect we could come back and change this.
|
||||
//
|
||||
// We're done! It's important that the PeerGroup lock is not held (by this thread) at this
|
||||
// point to avoid triggering inversions when the Future completes.
|
||||
log.info("broadcastTransaction: {} complete", pinnedTx.getHashAsString());
|
||||
peerGroup.removeEventListener(rejectionListener);
|
||||
conf.removeEventListener(this);
|
||||
future.set(pinnedTx); // RE-ENTRANCY POINT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* A general interface which declares the ability to broadcast transactions. This is implemented
|
||||
* by {@link com.dogecoin.dogecoinj.core.PeerGroup}.
|
||||
*/
|
||||
public interface TransactionBroadcaster {
|
||||
/** Broadcast the given transaction on the network */
|
||||
public ListenableFuture<Transaction> broadcastTransaction(final Transaction tx);
|
||||
}
|
@ -1,461 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.dogecoin.dogecoinj.utils.ListenerRegistration;
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serializable;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* <p>A TransactionConfidence object tracks data you can use to make a confidence decision about a transaction.
|
||||
* It also contains some pre-canned rules for common scenarios: if you aren't really sure what level of confidence
|
||||
* you need, these should prove useful. You can get a confidence object using {@link Transaction#getConfidence()}.
|
||||
* They cannot be constructed directly.</p>
|
||||
*
|
||||
* <p>Confidence in a transaction can come in multiple ways:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>Because you created it yourself and only you have the necessary keys.</li>
|
||||
* <li>Receiving it from a fully validating peer you know is trustworthy, for instance, because it's run by yourself.</li>
|
||||
* <li>Receiving it from a peer on the network you randomly chose. If your network connection is not being
|
||||
* intercepted, you have a pretty good chance of connecting to a node that is following the rules.</li>
|
||||
* <li>Receiving it from multiple peers on the network. If your network connection is not being intercepted,
|
||||
* hearing about a transaction from multiple peers indicates the network has accepted the transaction and
|
||||
* thus miners likely have too (miners have the final say in whether a transaction becomes valid or not).</li>
|
||||
* <li>Seeing the transaction appear appear in a block on the main chain. Your confidence increases as the transaction
|
||||
* becomes further buried under work. Work can be measured either in blocks (roughly, units of time), or
|
||||
* amount of work done.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Alternatively, you may know that the transaction is "dead", that is, one or more of its inputs have
|
||||
* been double spent and will never confirm unless there is another re-org.</p>
|
||||
*
|
||||
* <p>TransactionConfidence is updated via the {@link com.dogecoin.dogecoinj.core.TransactionConfidence#incrementDepthInBlocks()}
|
||||
* method to ensure the block depth is up to date.</p>
|
||||
* To make a copy that won't be changed, use {@link com.dogecoin.dogecoinj.core.TransactionConfidence#duplicate()}.
|
||||
*/
|
||||
public class TransactionConfidence implements Serializable {
|
||||
private static final long serialVersionUID = 4577920141400556444L;
|
||||
|
||||
/**
|
||||
* The peers that have announced the transaction to us. Network nodes don't have stable identities, so we use
|
||||
* IP address as an approximation. It's obviously vulnerable to being gamed if we allow arbitrary people to connect
|
||||
* to us, so only peers we explicitly connected to should go here.
|
||||
*/
|
||||
private CopyOnWriteArrayList<PeerAddress> broadcastBy;
|
||||
/** The Transaction that this confidence object is associated with. */
|
||||
private final Sha256Hash hash;
|
||||
// Lazily created listeners array.
|
||||
private transient CopyOnWriteArrayList<ListenerRegistration<Listener>> listeners;
|
||||
|
||||
// The depth of the transaction on the best chain in blocks. An unconfirmed block has depth 0.
|
||||
private int depth;
|
||||
|
||||
/** Describes the state of the transaction in general terms. Properties can be read to learn specifics. */
|
||||
public enum ConfidenceType {
|
||||
/** If BUILDING, then the transaction is included in the best chain and your confidence in it is increasing. */
|
||||
BUILDING(1),
|
||||
|
||||
/**
|
||||
* If PENDING, then the transaction is unconfirmed and should be included shortly, as long as it is being
|
||||
* announced and is considered valid by the network. A pending transaction will be announced if the containing
|
||||
* wallet has been attached to a live {@link PeerGroup} using {@link PeerGroup#addWallet(Wallet)}.
|
||||
* You can estimate how likely the transaction is to be included by connecting to a bunch of nodes then measuring
|
||||
* how many announce it, using {@link com.dogecoin.dogecoinj.core.TransactionConfidence#numBroadcastPeers()}.
|
||||
* Or if you saw it from a trusted peer, you can assume it's valid and will get mined sooner or later as well.
|
||||
*/
|
||||
PENDING(2),
|
||||
|
||||
/**
|
||||
* If DEAD, then it means the transaction won't confirm unless there is another re-org,
|
||||
* because some other transaction is spending one of its inputs. Such transactions should be alerted to the user
|
||||
* so they can take action, eg, suspending shipment of goods if they are a merchant.
|
||||
* It can also mean that a coinbase transaction has been made dead from it being moved onto a side chain.
|
||||
*/
|
||||
DEAD(4),
|
||||
|
||||
/**
|
||||
* If a transaction hasn't been broadcast yet, or there's no record of it, its confidence is UNKNOWN.
|
||||
*/
|
||||
UNKNOWN(0);
|
||||
|
||||
private int value;
|
||||
ConfidenceType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfidenceType confidenceType = ConfidenceType.UNKNOWN;
|
||||
private int appearedAtChainHeight = -1;
|
||||
// The transaction that double spent this one, if any.
|
||||
private Transaction overridingTransaction;
|
||||
|
||||
/**
|
||||
* Information about where the transaction was first seen (network, sent direct from peer, created by ourselves).
|
||||
* Useful for risk analyzing pending transactions. Probably not that useful after a tx is included in the chain,
|
||||
* unless re-org double spends start happening frequently.
|
||||
*/
|
||||
public enum Source {
|
||||
/** We don't know where the transaction came from. */
|
||||
UNKNOWN,
|
||||
/** We got this transaction from a network peer. */
|
||||
NETWORK,
|
||||
/** This transaction was created by our own wallet, so we know it's not a double spend. */
|
||||
SELF
|
||||
}
|
||||
private Source source = Source.UNKNOWN;
|
||||
|
||||
public TransactionConfidence(Sha256Hash hash) {
|
||||
// Assume a default number of peers for our set.
|
||||
broadcastBy = new CopyOnWriteArrayList<PeerAddress>();
|
||||
listeners = new CopyOnWriteArrayList<ListenerRegistration<Listener>>();
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A confidence listener is informed when the level of {@link TransactionConfidence} is updated by something, like
|
||||
* for example a {@link Wallet}. You can add listeners to update your user interface or manage your order tracking
|
||||
* system when confidence levels pass a certain threshold. <b>Note that confidence can go down as well as up.</b>
|
||||
* For example, this can happen if somebody is doing a double-spend attack against you. Whilst it's unlikely, your
|
||||
* code should be able to handle that in order to be correct.</p>
|
||||
*
|
||||
* <p>During listener execution, it's safe to remove the current listener but not others.</p>
|
||||
*/
|
||||
public interface Listener {
|
||||
/** An enum that describes why a transaction confidence listener is being invoked (i.e. the class of change). */
|
||||
public enum ChangeReason {
|
||||
/**
|
||||
* Occurs when the type returned by {@link com.dogecoin.dogecoinj.core.TransactionConfidence#getConfidenceType()}
|
||||
* has changed. For example, if a PENDING transaction changes to BUILDING or DEAD, then this reason will
|
||||
* be given. It's a high level summary.
|
||||
*/
|
||||
TYPE,
|
||||
|
||||
/**
|
||||
* Occurs when a transaction that is in the best known block chain gets buried by another block. If you're
|
||||
* waiting for a certain number of confirmations, this is the reason to watch out for.
|
||||
*/
|
||||
DEPTH,
|
||||
|
||||
/**
|
||||
* Occurs when a pending transaction (not in the chain) was announced by another connected peers. By
|
||||
* watching the number of peers that announced a transaction go up, you can see whether it's being
|
||||
* accepted by the network or not. If all your peers announce, it's a pretty good bet the transaction
|
||||
* is considered relayable and has thus reached the miners.
|
||||
*/
|
||||
SEEN_PEERS,
|
||||
}
|
||||
public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds an event listener that will be run when this confidence object is updated. The listener will be locked and
|
||||
* is likely to be invoked on a peer thread.</p>
|
||||
*
|
||||
* <p>Note that this is NOT called when every block arrives. Instead it is called when the transaction
|
||||
* transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in
|
||||
* the best chain). If you want to know when the transaction gets buried under another block, consider using
|
||||
* a future from {@link #getDepthFuture(int)}.</p>
|
||||
*/
|
||||
public void addEventListener(Listener listener, Executor executor) {
|
||||
checkNotNull(listener);
|
||||
listeners.addIfAbsent(new ListenerRegistration<Listener>(listener, executor));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Adds an event listener that will be run when this confidence object is updated. The listener will be locked and
|
||||
* is likely to be invoked on a peer thread.</p>
|
||||
*
|
||||
* <p>Note that this is NOT called when every block arrives. Instead it is called when the transaction
|
||||
* transitions between confidence states, ie, from not being seen in the chain to being seen (not necessarily in
|
||||
* the best chain). If you want to know when the transaction gets buried under another block, implement a
|
||||
* {@link BlockChainListener}, attach it to a {@link BlockChain} and then use the getters on the
|
||||
* confidence object to determine the new depth.</p>
|
||||
*/
|
||||
public void addEventListener(Listener listener) {
|
||||
addEventListener(listener, Threading.USER_THREAD);
|
||||
}
|
||||
|
||||
public boolean removeEventListener(Listener listener) {
|
||||
checkNotNull(listener);
|
||||
return ListenerRegistration.removeFromList(listener, listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chain height at which the transaction appeared if confidence type is BUILDING.
|
||||
* @throws IllegalStateException if the confidence type is not BUILDING.
|
||||
*/
|
||||
public synchronized int getAppearedAtChainHeight() {
|
||||
if (getConfidenceType() != ConfidenceType.BUILDING)
|
||||
throw new IllegalStateException("Confidence type is " + getConfidenceType() + ", not BUILDING");
|
||||
return appearedAtChainHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* The chain height at which the transaction appeared, if it has been seen in the best chain. Automatically sets
|
||||
* the current type to {@link ConfidenceType#BUILDING} and depth to one.
|
||||
*/
|
||||
public synchronized void setAppearedAtChainHeight(int appearedAtChainHeight) {
|
||||
if (appearedAtChainHeight < 0)
|
||||
throw new IllegalArgumentException("appearedAtChainHeight out of range");
|
||||
this.appearedAtChainHeight = appearedAtChainHeight;
|
||||
this.depth = 1;
|
||||
setConfidenceType(ConfidenceType.BUILDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a general statement of the level of confidence you can have in this transaction.
|
||||
*/
|
||||
public synchronized ConfidenceType getConfidenceType() {
|
||||
return confidenceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by other objects in the system, like a {@link Wallet}, when new information about the confidence of a
|
||||
* transaction becomes available.
|
||||
*/
|
||||
public synchronized void setConfidenceType(ConfidenceType confidenceType) {
|
||||
if (confidenceType == this.confidenceType)
|
||||
return;
|
||||
this.confidenceType = confidenceType;
|
||||
if (confidenceType != ConfidenceType.DEAD) {
|
||||
overridingTransaction = null;
|
||||
}
|
||||
if (confidenceType == ConfidenceType.PENDING) {
|
||||
depth = 0;
|
||||
appearedAtChainHeight = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by a {@link Peer} when a transaction is pending and announced by a peer. The more peers announce the
|
||||
* transaction, the more peers have validated it (assuming your internet connection is not being intercepted).
|
||||
* If confidence is currently unknown, sets it to {@link ConfidenceType#PENDING}. Listeners will be
|
||||
* invoked in this case.
|
||||
*
|
||||
* @param address IP address of the peer, used as a proxy for identity.
|
||||
*/
|
||||
public synchronized boolean markBroadcastBy(PeerAddress address) {
|
||||
if (!broadcastBy.addIfAbsent(address))
|
||||
return false; // Duplicate.
|
||||
if (getConfidenceType() == ConfidenceType.UNKNOWN) {
|
||||
this.confidenceType = ConfidenceType.PENDING;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many peers have been passed to {@link TransactionConfidence#markBroadcastBy}.
|
||||
*/
|
||||
public int numBroadcastPeers() {
|
||||
return broadcastBy.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of {@link PeerAddress}es that announced the transaction.
|
||||
*/
|
||||
public Set<PeerAddress> getBroadcastBy() {
|
||||
ListIterator<PeerAddress> iterator = broadcastBy.listIterator();
|
||||
return Sets.newHashSet(iterator);
|
||||
}
|
||||
|
||||
/** Returns true if the given address has been seen via markBroadcastBy() */
|
||||
public boolean wasBroadcastBy(PeerAddress address) {
|
||||
return broadcastBy.contains(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int peers = numBroadcastPeers();
|
||||
if (peers > 0) {
|
||||
builder.append("Seen by ");
|
||||
builder.append(peers);
|
||||
if (peers > 1)
|
||||
builder.append(" peers. ");
|
||||
else
|
||||
builder.append(" peer. ");
|
||||
}
|
||||
switch (getConfidenceType()) {
|
||||
case UNKNOWN:
|
||||
builder.append("Unknown confidence level.");
|
||||
break;
|
||||
case DEAD:
|
||||
builder.append("Dead: overridden by double spend and will not confirm.");
|
||||
break;
|
||||
case PENDING:
|
||||
builder.append("Pending/unconfirmed.");
|
||||
break;
|
||||
case BUILDING:
|
||||
builder.append(String.format("Appeared in best chain at height %d, depth %d.",
|
||||
getAppearedAtChainHeight(), getDepthInBlocks()));
|
||||
break;
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the wallet when the tx appears on the best chain and a new block is added to the top. Updates the
|
||||
* internal counter that tracks how deeply buried the block is.
|
||||
*/
|
||||
public synchronized void incrementDepthInBlocks() {
|
||||
this.depth++;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Depth in the chain is an approximation of how much time has elapsed since the transaction has been confirmed.
|
||||
* On average there is supposed to be a new block every 10 minutes, but the actual rate may vary. The reference
|
||||
* (Satoshi) implementation considers a transaction impractical to reverse after 6 blocks, but as of EOY 2011 network
|
||||
* security is high enough that often only one block is considered enough even for high value transactions. For low
|
||||
* value transactions like songs, or other cheap items, no blocks at all may be necessary.</p>
|
||||
*
|
||||
* <p>If the transaction appears in the top block, the depth is one. If it's anything else (pending, dead, unknown)
|
||||
* the depth is zero.</p>
|
||||
*/
|
||||
public synchronized int getDepthInBlocks() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the depth in blocks. Having one block confirmation is a depth of one.
|
||||
*/
|
||||
public synchronized void setDepthInBlocks(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this transaction has been overridden by a double spend (is dead), this call returns the overriding transaction.
|
||||
* Note that this call <b>can return null</b> if you have migrated an old wallet, as pre-Jan 2012 wallets did not
|
||||
* store this information.
|
||||
*
|
||||
* @return the transaction that double spent this one
|
||||
* @throws IllegalStateException if confidence type is not OVERRIDDEN_BY_DOUBLE_SPEND.
|
||||
*/
|
||||
public synchronized Transaction getOverridingTransaction() {
|
||||
if (getConfidenceType() != ConfidenceType.DEAD)
|
||||
throw new IllegalStateException("Confidence type is " + getConfidenceType() +
|
||||
", not OVERRIDDEN_BY_DOUBLE_SPEND");
|
||||
return overridingTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the transaction becomes newly dead, that is, we learn that one of its inputs has already been spent
|
||||
* in such a way that the double-spending transaction takes precedence over this one. It will not become valid now
|
||||
* unless there is a re-org. Automatically sets the confidence type to DEAD. The overriding transaction may not
|
||||
* directly double spend this one, but could also have double spent a dependency of this tx.
|
||||
*/
|
||||
public synchronized void setOverridingTransaction(@Nullable Transaction overridingTransaction) {
|
||||
this.overridingTransaction = overridingTransaction;
|
||||
setConfidenceType(ConfidenceType.DEAD);
|
||||
}
|
||||
|
||||
/** Returns a copy of this object. Event listeners are not duplicated. */
|
||||
public synchronized TransactionConfidence duplicate() {
|
||||
TransactionConfidence c = new TransactionConfidence(hash);
|
||||
// There is no point in this sync block, it's just to help FindBugs.
|
||||
synchronized (c) {
|
||||
c.broadcastBy.addAll(broadcastBy);
|
||||
c.confidenceType = confidenceType;
|
||||
c.overridingTransaction = overridingTransaction;
|
||||
c.appearedAtChainHeight = appearedAtChainHeight;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this after adjusting the confidence, for cases where listeners should be notified. This has to be done
|
||||
* explicitly rather than being done automatically because sometimes complex changes to transaction states can
|
||||
* result in a series of confidence changes that are not really useful to see separately. By invoking listeners
|
||||
* explicitly, more precise control is available. Note that this will run the listeners on the user code thread.
|
||||
*/
|
||||
public void queueListeners(final Listener.ChangeReason reason) {
|
||||
for (final ListenerRegistration<Listener> registration : listeners) {
|
||||
registration.executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
registration.listener.onConfidenceChanged(TransactionConfidence.this, reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source of a transaction tries to identify where it came from originally. For instance, did we download it
|
||||
* from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app,
|
||||
* and so on. This information is useful for {@link com.dogecoin.dogecoinj.wallet.CoinSelector} implementations to risk analyze
|
||||
* transactions and decide when to spend them.
|
||||
*/
|
||||
public synchronized Source getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source of a transaction tries to identify where it came from originally. For instance, did we download it
|
||||
* from the peer to peer network, or make it ourselves, or receive it via Bluetooth, or import it from another app,
|
||||
* and so on. This information is useful for {@link com.dogecoin.dogecoinj.wallet.CoinSelector} implementations to risk analyze
|
||||
* transactions and decide when to spend them.
|
||||
*/
|
||||
public synchronized void setSource(Source source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a future that completes when the transaction has been confirmed by "depth" blocks. For instance setting
|
||||
* depth to one will wait until it appears in a block on the best chain, and zero will wait until it has been seen
|
||||
* on the network.
|
||||
*/
|
||||
public synchronized ListenableFuture<TransactionConfidence> getDepthFuture(final int depth, Executor executor) {
|
||||
final SettableFuture<TransactionConfidence> result = SettableFuture.create();
|
||||
if (getDepthInBlocks() >= depth) {
|
||||
result.set(this);
|
||||
}
|
||||
addEventListener(new Listener() {
|
||||
@Override public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) {
|
||||
if (getDepthInBlocks() >= depth) {
|
||||
removeEventListener(this);
|
||||
result.set(confidence);
|
||||
}
|
||||
}
|
||||
}, executor);
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized ListenableFuture<TransactionConfidence> getDepthFuture(final int depth) {
|
||||
return getDepthFuture(depth, Threading.USER_THREAD);
|
||||
}
|
||||
|
||||
public Sha256Hash getTransactionHash() {
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -1,499 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.wallet.DefaultRiskAnalysis;
|
||||
import com.dogecoin.dogecoinj.wallet.KeyBag;
|
||||
import com.dogecoin.dogecoinj.wallet.RedeemData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkElementIndex;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A transfer of coins from one address to another creates a transaction in which the outputs
|
||||
* can be claimed by the recipient in the input of another transaction. You can imagine a
|
||||
* transaction as being a module which is wired up to others, the inputs of one have to be wired
|
||||
* to the outputs of another. The exceptions are coinbase transactions, which create new coins.
|
||||
*/
|
||||
public class TransactionInput extends ChildMessage implements Serializable {
|
||||
public static final long NO_SEQUENCE = 0xFFFFFFFFL;
|
||||
private static final long serialVersionUID = 2;
|
||||
public static final byte[] EMPTY_ARRAY = new byte[0];
|
||||
|
||||
// Allows for altering transactions after they were broadcast. Tx replacement is currently disabled in the C++
|
||||
// client so this is always the UINT_MAX.
|
||||
// TODO: Document this in more detail and build features that use it.
|
||||
private long sequence;
|
||||
// Data needed to connect to the output of the transaction we're gathering coins from.
|
||||
private TransactionOutPoint outpoint;
|
||||
// The "script bytes" might not actually be a script. In coinbase transactions where new coins are minted there
|
||||
// is no input transaction, so instead the scriptBytes contains some extra stuff (like a rollover nonce) that we
|
||||
// don't care about much. The bytes are turned into a Script object (cached below) on demand via a getter.
|
||||
private byte[] scriptBytes;
|
||||
// The Script object obtained from parsing scriptBytes. Only filled in on demand and if the transaction is not
|
||||
// coinbase.
|
||||
transient private WeakReference<Script> scriptSig;
|
||||
/** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */
|
||||
@Nullable
|
||||
private Coin value;
|
||||
|
||||
/**
|
||||
* Creates an input that connects to nothing - used only in creation of coinbase transactions.
|
||||
*/
|
||||
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes) {
|
||||
this(params, parentTransaction, scriptBytes, new TransactionOutPoint(params, NO_SEQUENCE, (Transaction) null));
|
||||
}
|
||||
|
||||
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes,
|
||||
TransactionOutPoint outpoint) {
|
||||
this(params, parentTransaction, scriptBytes, outpoint, null);
|
||||
}
|
||||
|
||||
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes,
|
||||
TransactionOutPoint outpoint, @Nullable Coin value) {
|
||||
super(params);
|
||||
this.scriptBytes = scriptBytes;
|
||||
this.outpoint = outpoint;
|
||||
this.sequence = NO_SEQUENCE;
|
||||
this.value = value;
|
||||
setParent(parentTransaction);
|
||||
length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an UNSIGNED input that links to the given output
|
||||
*/
|
||||
TransactionInput(NetworkParameters params, Transaction parentTransaction, TransactionOutput output) {
|
||||
super(params);
|
||||
long outputIndex = output.getIndex();
|
||||
if(output.getParentTransaction() != null ) {
|
||||
outpoint = new TransactionOutPoint(params, outputIndex, output.getParentTransaction());
|
||||
} else {
|
||||
outpoint = new TransactionOutPoint(params, output);
|
||||
}
|
||||
scriptBytes = EMPTY_ARRAY;
|
||||
sequence = NO_SEQUENCE;
|
||||
setParent(parentTransaction);
|
||||
this.value = output.getValue();
|
||||
length = 41;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes an input message. This is usually part of a transaction message.
|
||||
*/
|
||||
public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] payload, int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
setParent(parentTransaction);
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes an input message. This is usually part of a transaction message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] payload, int offset,
|
||||
boolean parseLazy, boolean parseRetain)
|
||||
throws ProtocolException {
|
||||
super(params, payload, offset, parentTransaction, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
int curs = cursor;
|
||||
int scriptLen = (int) readVarInt(36);
|
||||
length = cursor - offset + scriptLen + 4;
|
||||
cursor = curs;
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
outpoint = new TransactionOutPoint(params, payload, cursor, this, parseLazy, parseRetain);
|
||||
cursor += outpoint.getMessageSize();
|
||||
int scriptLen = (int) readVarInt();
|
||||
scriptBytes = readBytes(scriptLen);
|
||||
sequence = readUint32();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
outpoint.bitcoinSerialize(stream);
|
||||
stream.write(new VarInt(scriptBytes.length).encode());
|
||||
stream.write(scriptBytes);
|
||||
Utils.uint32ToByteStreamLE(sequence, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true.
|
||||
*/
|
||||
public boolean isCoinBase() {
|
||||
maybeParse();
|
||||
return outpoint.getHash().equals(Sha256Hash.ZERO_HASH) &&
|
||||
(outpoint.getIndex() & 0xFFFFFFFFL) == 0xFFFFFFFFL; // -1 but all is serialized to the wire as unsigned int.
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the script that is fed to the referenced output (scriptPubKey) script in order to satisfy it: usually
|
||||
* contains signatures and maybe keys, but can contain arbitrary data if the output script accepts it.
|
||||
*/
|
||||
public Script getScriptSig() throws ScriptException {
|
||||
// Transactions that generate new coins don't actually have a script. Instead this
|
||||
// parameter is overloaded to be something totally different.
|
||||
Script script = scriptSig == null ? null : scriptSig.get();
|
||||
if (script == null) {
|
||||
maybeParse();
|
||||
script = new Script(scriptBytes);
|
||||
scriptSig = new WeakReference<Script>(script);
|
||||
return script;
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
/** Set the given program as the scriptSig that is supposed to satisfy the connected output script. */
|
||||
public void setScriptSig(Script scriptSig) {
|
||||
this.scriptSig = new WeakReference<Script>(checkNotNull(scriptSig));
|
||||
// TODO: This should all be cleaned up so we have a consistent internal representation.
|
||||
setScriptBytes(scriptSig.getProgram());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that returns the from address of this input by parsing the scriptSig. The concept of a
|
||||
* "from address" is not well defined in Bitcoin and you should not assume that senders of a transaction can
|
||||
* actually receive coins on the same address they used to sign (e.g. this is not true for shared wallets).
|
||||
*/
|
||||
@Deprecated
|
||||
public Address getFromAddress() throws ScriptException {
|
||||
if (isCoinBase()) {
|
||||
throw new ScriptException(
|
||||
"This is a coinbase transaction which generates new coins. It does not have a from address.");
|
||||
}
|
||||
return getScriptSig().getFromAddress(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence numbers allow participants in a multi-party transaction signing protocol to create new versions of the
|
||||
* transaction independently of each other. Newer versions of a transaction can replace an existing version that's
|
||||
* in nodes memory pools if the existing version is time locked. See the Contracts page on the Bitcoin wiki for
|
||||
* examples of how you can use this feature to build contract protocols. Note that as of 2012 the tx replacement
|
||||
* feature is disabled so sequence numbers are unusable.
|
||||
*/
|
||||
public long getSequenceNumber() {
|
||||
maybeParse();
|
||||
return sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequence numbers allow participants in a multi-party transaction signing protocol to create new versions of the
|
||||
* transaction independently of each other. Newer versions of a transaction can replace an existing version that's
|
||||
* in nodes memory pools if the existing version is time locked. See the Contracts page on the Bitcoin wiki for
|
||||
* examples of how you can use this feature to build contract protocols. Note that as of 2012 the tx replacement
|
||||
* feature is disabled so sequence numbers are unusable.
|
||||
*/
|
||||
public void setSequenceNumber(long sequence) {
|
||||
unCache();
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The previous output transaction reference, as an OutPoint structure. This contains the
|
||||
* data needed to connect to the output of the transaction we're gathering coins from.
|
||||
*/
|
||||
public TransactionOutPoint getOutpoint() {
|
||||
maybeParse();
|
||||
return outpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "script bytes" might not actually be a script. In coinbase transactions where new coins are minted there
|
||||
* is no input transaction, so instead the scriptBytes contains some extra stuff (like a rollover nonce) that we
|
||||
* don't care about much. The bytes are turned into a Script object (cached below) on demand via a getter.
|
||||
* @return the scriptBytes
|
||||
*/
|
||||
public byte[] getScriptBytes() {
|
||||
maybeParse();
|
||||
return scriptBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scriptBytes the scriptBytes to set
|
||||
*/
|
||||
void setScriptBytes(byte[] scriptBytes) {
|
||||
unCache();
|
||||
this.scriptSig = null;
|
||||
int oldLength = length;
|
||||
this.scriptBytes = scriptBytes;
|
||||
// 40 = previous_outpoint (36) + sequence (4)
|
||||
int newLength = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length);
|
||||
adjustLength(newLength - oldLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Transaction that owns this input.
|
||||
*/
|
||||
public Transaction getParentTransaction() {
|
||||
return (Transaction) parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value of the output connected to this input, if known. Null if unknown.
|
||||
*/
|
||||
@Nullable
|
||||
public Coin getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable debug string.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isCoinBase())
|
||||
return "TxIn: COINBASE";
|
||||
try {
|
||||
return "TxIn for [" + outpoint + "]: " + getScriptSig();
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConnectionResult {
|
||||
NO_SUCH_TX,
|
||||
ALREADY_SPENT,
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
// TODO: Clean all this up once TransactionOutPoint disappears.
|
||||
|
||||
/**
|
||||
* Locates the referenced output from the given pool of transactions.
|
||||
*
|
||||
* @return The TransactionOutput or null if the transactions map doesn't contain the referenced tx.
|
||||
*/
|
||||
@Nullable
|
||||
TransactionOutput getConnectedOutput(Map<Sha256Hash, Transaction> transactions) {
|
||||
Transaction tx = transactions.get(outpoint.getHash());
|
||||
if (tx == null)
|
||||
return null;
|
||||
return tx.getOutputs().get((int) outpoint.getIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for getOutpoint().getConnectedRedeemData(keyBag)
|
||||
* @see TransactionOutPoint#getConnectedRedeemData(com.dogecoin.dogecoinj.wallet.KeyBag)
|
||||
*/
|
||||
@Nullable
|
||||
public RedeemData getConnectedRedeemData(KeyBag keyBag) throws ScriptException {
|
||||
return getOutpoint().getConnectedRedeemData(keyBag);
|
||||
}
|
||||
|
||||
|
||||
public enum ConnectMode {
|
||||
DISCONNECT_ON_CONFLICT,
|
||||
ABORT_ON_CONFLICT
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects this input to the relevant output of the referenced transaction if it's in the given map.
|
||||
* Connecting means updating the internal pointers and spent flags. If the mode is to ABORT_ON_CONFLICT then
|
||||
* the spent output won't be changed, but the outpoint.fromTx pointer will still be updated.
|
||||
*
|
||||
* @param transactions Map of txhash->transaction.
|
||||
* @param mode Whether to abort if there's a pre-existing connection or not.
|
||||
* @return NO_SUCH_TX if the prevtx wasn't found, ALREADY_SPENT if there was a conflict, SUCCESS if not.
|
||||
*/
|
||||
public ConnectionResult connect(Map<Sha256Hash, Transaction> transactions, ConnectMode mode) {
|
||||
Transaction tx = transactions.get(outpoint.getHash());
|
||||
if (tx == null) {
|
||||
return TransactionInput.ConnectionResult.NO_SUCH_TX;
|
||||
}
|
||||
return connect(tx, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects this input to the relevant output of the referenced transaction.
|
||||
* Connecting means updating the internal pointers and spent flags. If the mode is to ABORT_ON_CONFLICT then
|
||||
* the spent output won't be changed, but the outpoint.fromTx pointer will still be updated.
|
||||
*
|
||||
* @param transaction The transaction to try.
|
||||
* @param mode Whether to abort if there's a pre-existing connection or not.
|
||||
* @return NO_SUCH_TX if transaction is not the prevtx, ALREADY_SPENT if there was a conflict, SUCCESS if not.
|
||||
*/
|
||||
public ConnectionResult connect(Transaction transaction, ConnectMode mode) {
|
||||
if (!transaction.getHash().equals(outpoint.getHash()))
|
||||
return ConnectionResult.NO_SUCH_TX;
|
||||
checkElementIndex((int) outpoint.getIndex(), transaction.getOutputs().size(), "Corrupt transaction");
|
||||
TransactionOutput out = transaction.getOutput((int) outpoint.getIndex());
|
||||
if (!out.isAvailableForSpending()) {
|
||||
if (getParentTransaction().equals(outpoint.fromTx)) {
|
||||
// Already connected.
|
||||
return ConnectionResult.SUCCESS;
|
||||
} else if (mode == ConnectMode.DISCONNECT_ON_CONFLICT) {
|
||||
out.markAsUnspent();
|
||||
} else if (mode == ConnectMode.ABORT_ON_CONFLICT) {
|
||||
outpoint.fromTx = out.getParentTransaction();
|
||||
return TransactionInput.ConnectionResult.ALREADY_SPENT;
|
||||
}
|
||||
}
|
||||
connect(out);
|
||||
return TransactionInput.ConnectionResult.SUCCESS;
|
||||
}
|
||||
|
||||
/** Internal use only: connects this TransactionInput to the given output (updates pointers and spent flags) */
|
||||
public void connect(TransactionOutput out) {
|
||||
outpoint.fromTx = out.getParentTransaction();
|
||||
out.markAsSpent(this);
|
||||
value = out.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this input is connected, check the output is connected back to this input and release it if so, making
|
||||
* it spendable once again.
|
||||
*
|
||||
* @return true if the disconnection took place, false if it was not connected.
|
||||
*/
|
||||
public boolean disconnect() {
|
||||
if (outpoint.fromTx == null) return false;
|
||||
TransactionOutput output = outpoint.fromTx.getOutput((int) outpoint.getIndex());
|
||||
if (output.getSpentBy() == this) {
|
||||
output.markAsUnspent();
|
||||
outpoint.fromTx = null;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure object is fully parsed before invoking java serialization. The backing byte array
|
||||
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
|
||||
* then data will be lost during serialization.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
maybeParse();
|
||||
out.defaultWriteObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this transaction's sequence number is set (ie it may be a part of a time-locked transaction)
|
||||
*/
|
||||
public boolean hasSequence() {
|
||||
return sequence != NO_SEQUENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a connected transaction, runs the script against the connected pubkey and verifies they are correct.
|
||||
* @throws ScriptException if the script did not verify.
|
||||
* @throws VerificationException If the outpoint doesn't match the given output.
|
||||
*/
|
||||
public void verify() throws VerificationException {
|
||||
final Transaction fromTx = getOutpoint().fromTx;
|
||||
long spendingIndex = getOutpoint().getIndex();
|
||||
checkNotNull(fromTx, "Not connected");
|
||||
final TransactionOutput output = fromTx.getOutput((int) spendingIndex);
|
||||
verify(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that this input can spend the given output. Note that this input must be a part of a transaction.
|
||||
* Also note that the consistency of the outpoint will be checked, even if this input has not been connected.
|
||||
*
|
||||
* @param output the output that this input is supposed to spend.
|
||||
* @throws ScriptException If the script doesn't verify.
|
||||
* @throws VerificationException If the outpoint doesn't match the given output.
|
||||
*/
|
||||
public void verify(TransactionOutput output) throws VerificationException {
|
||||
if (output.parent != null) {
|
||||
if (!getOutpoint().getHash().equals(output.getParentTransaction().getHash()))
|
||||
throw new VerificationException("This input does not refer to the tx containing the output.");
|
||||
if (getOutpoint().getIndex() != output.getIndex())
|
||||
throw new VerificationException("This input refers to a different output on the given tx.");
|
||||
}
|
||||
Script pubKey = output.getScriptPubKey();
|
||||
int myIndex = getParentTransaction().getInputs().indexOf(this);
|
||||
getScriptSig().correctlySpends(getParentTransaction(), myIndex, pubKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connected output, assuming the input was connected with
|
||||
* {@link TransactionInput#connect(TransactionOutput)} or variants at some point. If it wasn't connected, then
|
||||
* this method returns null.
|
||||
*/
|
||||
@Nullable
|
||||
public TransactionOutput getConnectedOutput() {
|
||||
return getOutpoint().getConnectedOutput();
|
||||
}
|
||||
|
||||
/** Returns a copy of the input detached from its containing transaction, if need be. */
|
||||
public TransactionInput duplicateDetached() {
|
||||
return new TransactionInput(params, null, bitcoinSerialize(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns either RuleViolation.NONE if the input is standard, or which rule makes it non-standard if so.
|
||||
* The "IsStandard" rules control whether the default Bitcoin Core client blocks relay of a tx / refuses to mine it,
|
||||
* however, non-standard transactions can still be included in blocks and will be accepted as valid if so.</p>
|
||||
*
|
||||
* <p>This method simply calls <tt>DefaultRiskAnalysis.isInputStandard(this)</tt>.</p>
|
||||
*/
|
||||
public DefaultRiskAnalysis.RuleViolation isStandard() {
|
||||
return DefaultRiskAnalysis.isInputStandard(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TransactionInput input = (TransactionInput) o;
|
||||
|
||||
if (sequence != input.sequence) return false;
|
||||
if (!outpoint.equals(input.outpoint)) return false;
|
||||
if (!Arrays.equals(scriptBytes, input.scriptBytes)) return false;
|
||||
if (scriptSig != null ? !scriptSig.equals(input.scriptSig) : input.scriptSig != null) return false;
|
||||
if (parent != input.parent) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (sequence ^ (sequence >>> 32));
|
||||
result = 31 * result + outpoint.hashCode();
|
||||
result = 31 * result + (scriptBytes != null ? Arrays.hashCode(scriptBytes) : 0);
|
||||
result = 31 * result + (scriptSig != null ? scriptSig.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,250 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.wallet.KeyBag;
|
||||
import com.dogecoin.dogecoinj.wallet.RedeemData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* This message is a reference or pointer to an output of a different transaction.
|
||||
*/
|
||||
public class TransactionOutPoint extends ChildMessage implements Serializable {
|
||||
private static final long serialVersionUID = -6320880638344662579L;
|
||||
|
||||
static final int MESSAGE_LENGTH = 36;
|
||||
|
||||
/** Hash of the transaction to which we refer. */
|
||||
private Sha256Hash hash;
|
||||
/** Which output of that transaction we are talking about. */
|
||||
private long index;
|
||||
|
||||
// This is not part of Bitcoin serialization. It's included in Java serialization.
|
||||
// It points to the connected transaction.
|
||||
Transaction fromTx;
|
||||
|
||||
// The connected output.
|
||||
private TransactionOutput connectedOutput;
|
||||
|
||||
public TransactionOutPoint(NetworkParameters params, long index, @Nullable Transaction fromTx) {
|
||||
super(params);
|
||||
this.index = index;
|
||||
if (fromTx != null) {
|
||||
this.hash = fromTx.getHash();
|
||||
this.fromTx = fromTx;
|
||||
} else {
|
||||
// This happens when constructing the genesis block.
|
||||
hash = Sha256Hash.ZERO_HASH;
|
||||
}
|
||||
length = MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
public TransactionOutPoint(NetworkParameters params, long index, Sha256Hash hash) {
|
||||
super(params);
|
||||
this.index = index;
|
||||
this.hash = hash;
|
||||
length = MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
public TransactionOutPoint(NetworkParameters params, TransactionOutput connectedOutput) {
|
||||
this(params, connectedOutput.getIndex(), connectedOutput.getParentTransactionHash());
|
||||
this.connectedOutput = connectedOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Deserializes the message. This is usually part of a transaction message.
|
||||
*/
|
||||
public TransactionOutPoint(NetworkParameters params, byte[] payload, int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the message. This is usually part of a transaction message.
|
||||
* @param params NetworkParameters object.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public TransactionOutPoint(NetworkParameters params, byte[] payload, int offset, Message parent, boolean parseLazy, boolean parseRetain) throws ProtocolException {
|
||||
super(params, payload, offset, parent, parseLazy, parseRetain, MESSAGE_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
length = MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
hash = readHash();
|
||||
index = readUint32();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see Message#getMessageSize()
|
||||
*/
|
||||
@Override
|
||||
public int getMessageSize() {
|
||||
return MESSAGE_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(Utils.reverseBytes(hash.getBytes()));
|
||||
Utils.uint32ToByteStreamLE(index, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* An outpoint is a part of a transaction input that points to the output of another transaction. If we have both
|
||||
* sides in memory, and they have been linked together, this returns a pointer to the connected output, or null
|
||||
* if there is no such connection.
|
||||
*/
|
||||
@Nullable
|
||||
public TransactionOutput getConnectedOutput() {
|
||||
if (fromTx != null) {
|
||||
return fromTx.getOutputs().get((int) index);
|
||||
} else if (connectedOutput != null) {
|
||||
return connectedOutput;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pubkey script from the connected output.
|
||||
* @throws java.lang.NullPointerException if there is no connected output.
|
||||
*/
|
||||
public byte[] getConnectedPubKeyScript() {
|
||||
byte[] result = checkNotNull(getConnectedOutput()).getScriptBytes();
|
||||
checkState(result.length > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ECKey identified in the connected output, for either pay-to-address scripts or pay-to-key scripts.
|
||||
* For P2SH scripts you can use {@link #getConnectedRedeemData(com.dogecoin.dogecoinj.wallet.KeyBag)} and then get the
|
||||
* key from RedeemData.
|
||||
* If the script form cannot be understood, throws ScriptException.
|
||||
*
|
||||
* @return an ECKey or null if the connected key cannot be found in the wallet.
|
||||
*/
|
||||
@Nullable
|
||||
public ECKey getConnectedKey(KeyBag keyBag) throws ScriptException {
|
||||
TransactionOutput connectedOutput = getConnectedOutput();
|
||||
checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key");
|
||||
Script connectedScript = connectedOutput.getScriptPubKey();
|
||||
if (connectedScript.isSentToAddress()) {
|
||||
byte[] addressBytes = connectedScript.getPubKeyHash();
|
||||
return keyBag.findKeyFromPubHash(addressBytes);
|
||||
} else if (connectedScript.isSentToRawPubKey()) {
|
||||
byte[] pubkeyBytes = connectedScript.getPubKey();
|
||||
return keyBag.findKeyFromPubKey(pubkeyBytes);
|
||||
} else {
|
||||
throw new ScriptException("Could not understand form of connected output script: " + connectedScript);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RedeemData identified in the connected output, for either pay-to-address scripts, pay-to-key
|
||||
* or P2SH scripts.
|
||||
* If the script forms cannot be understood, throws ScriptException.
|
||||
*
|
||||
* @return a RedeemData or null if the connected data cannot be found in the wallet.
|
||||
*/
|
||||
@Nullable
|
||||
public RedeemData getConnectedRedeemData(KeyBag keyBag) throws ScriptException {
|
||||
TransactionOutput connectedOutput = getConnectedOutput();
|
||||
checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key");
|
||||
Script connectedScript = connectedOutput.getScriptPubKey();
|
||||
if (connectedScript.isSentToAddress()) {
|
||||
byte[] addressBytes = connectedScript.getPubKeyHash();
|
||||
return RedeemData.of(keyBag.findKeyFromPubHash(addressBytes), connectedScript);
|
||||
} else if (connectedScript.isSentToRawPubKey()) {
|
||||
byte[] pubkeyBytes = connectedScript.getPubKey();
|
||||
return RedeemData.of(keyBag.findKeyFromPubKey(pubkeyBytes), connectedScript);
|
||||
} else if (connectedScript.isPayToScriptHash()) {
|
||||
byte[] scriptHash = connectedScript.getPubKeyHash();
|
||||
return keyBag.findRedeemDataFromScriptHash(scriptHash);
|
||||
} else {
|
||||
throw new ScriptException("Could not understand form of connected output script: " + connectedScript);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return hash.toString() + ":" + index;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the hash of the transaction this outpoint references/spends/is connected to.
|
||||
*/
|
||||
@Override
|
||||
public Sha256Hash getHash() {
|
||||
maybeParse();
|
||||
return hash;
|
||||
}
|
||||
|
||||
void setHash(Sha256Hash hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public long getIndex() {
|
||||
maybeParse();
|
||||
return index;
|
||||
}
|
||||
|
||||
public void setIndex(long index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure object is fully parsed before invoking java serialization. The backing byte array
|
||||
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
|
||||
* then data will be lost during serialization.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
maybeParse();
|
||||
out.defaultWriteObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TransactionOutPoint other = (TransactionOutPoint) o;
|
||||
return getIndex() == other.getIndex() &&
|
||||
getHash().equals(other.getHash());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getHash().hashCode();
|
||||
}
|
||||
}
|
@ -1,470 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.script.ScriptBuilder;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* A TransactionOutput message contains a scriptPubKey that controls who is able to spend its value. It is a sub-part
|
||||
* of the Transaction message.
|
||||
*/
|
||||
public class TransactionOutput extends ChildMessage implements Serializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(TransactionOutput.class);
|
||||
private static final long serialVersionUID = -590332479859256824L;
|
||||
|
||||
// The output's value is kept as a native type in order to save class instances.
|
||||
private long value;
|
||||
|
||||
// A transaction output has a script used for authenticating that the redeemer is allowed to spend
|
||||
// this output.
|
||||
private byte[] scriptBytes;
|
||||
|
||||
// The script bytes are parsed and turned into a Script on demand.
|
||||
private transient WeakReference<Script> scriptPubKey;
|
||||
|
||||
// These fields are Java serialized but not Bitcoin serialized. They are used for tracking purposes in our wallet
|
||||
// only. If set to true, this output is counted towards our balance. If false and spentBy is null the tx output
|
||||
// was owned by us and was sent to somebody else. If false and spentBy is set it means this output was owned by
|
||||
// us and used in one of our own transactions (eg, because it is a change output).
|
||||
private boolean availableForSpending;
|
||||
@Nullable private TransactionInput spentBy;
|
||||
|
||||
private transient int scriptLen;
|
||||
|
||||
/**
|
||||
* Deserializes a transaction output message. This is usually part of a transaction message.
|
||||
*/
|
||||
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload,
|
||||
int offset) throws ProtocolException {
|
||||
super(params, payload, offset);
|
||||
setParent(parent);
|
||||
availableForSpending = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a transaction output message. This is usually part of a transaction message.
|
||||
*
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset,
|
||||
boolean parseLazy, boolean parseRetain) throws ProtocolException {
|
||||
super(params, payload, offset, parent, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||
availableForSpending = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output that sends 'value' to the given address (public key hash). The amount should be created with
|
||||
* something like {@link Coin#valueOf(int, int)}. Typically you would use
|
||||
* {@link Transaction#addOutput(Coin, Address)} instead of creating a TransactionOutput directly.
|
||||
*/
|
||||
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, Coin value, Address to) {
|
||||
this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output that sends 'value' to the given public key using a simple CHECKSIG script (no addresses). The
|
||||
* amount should be created with something like {@link Coin#valueOf(int, int)}. Typically you would use
|
||||
* {@link Transaction#addOutput(Coin, ECKey)} instead of creating an output directly.
|
||||
*/
|
||||
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, Coin value, ECKey to) {
|
||||
this(params, parent, value, ScriptBuilder.createOutputScript(to).getProgram());
|
||||
}
|
||||
|
||||
public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, Coin value, byte[] scriptBytes) {
|
||||
super(params);
|
||||
// Negative values obviously make no sense, except for -1 which is used as a sentinel value when calculating
|
||||
// SIGHASH_SINGLE signatures, so unfortunately we have to allow that here.
|
||||
checkArgument(value.signum() >= 0 || value.equals(Coin.NEGATIVE_SATOSHI), "Negative values not allowed");
|
||||
checkArgument(value.compareTo(NetworkParameters.MAX_MONEY) < 0, "Values larger than MAX_MONEY not allowed");
|
||||
this.value = value.value;
|
||||
this.scriptBytes = scriptBytes;
|
||||
setParent(parent);
|
||||
availableForSpending = true;
|
||||
length = 8 + VarInt.sizeOf(scriptBytes.length) + scriptBytes.length;
|
||||
}
|
||||
|
||||
public Script getScriptPubKey() throws ScriptException {
|
||||
// Quick hack to try and reduce memory consumption on Androids. SoftReference is the same as WeakReference
|
||||
// on Dalvik (by design), so this arrangement just means that we can avoid the cost of re-parsing the script
|
||||
// bytes if getScriptPubKey is called multiple times in quick succession in between garbage collections.
|
||||
Script script = scriptPubKey == null ? null : scriptPubKey.get();
|
||||
if (script == null) {
|
||||
maybeParse();
|
||||
script = new Script(scriptBytes);
|
||||
scriptPubKey = new WeakReference<Script>(script);
|
||||
return script;
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>If the output script pays to an address as in <a href="https://bitcoin.org/en/developer-guide#term-p2pkh">
|
||||
* P2PKH</a>, return the address of the receiver, i.e., a base58 encoded hash of the public key in the script. </p>
|
||||
*
|
||||
* @param networkParameters needed to specify an address
|
||||
* @return null, if the output script is not the form <i>OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG</i>,
|
||||
* i.e., not P2PKH
|
||||
* @return an address made out of the public key hash
|
||||
*/
|
||||
@Nullable
|
||||
public Address getAddressFromP2PKHScript(NetworkParameters networkParameters) throws ScriptException{
|
||||
if (getScriptPubKey().isSentToAddress())
|
||||
return getScriptPubKey().getToAddress(networkParameters);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>If the output script pays to a redeem script, return the address of the redeem script as described by,
|
||||
* i.e., a base58 encoding of [one-byte version][20-byte hash][4-byte checksum], where the 20-byte hash refers to
|
||||
* the redeem script.</p>
|
||||
*
|
||||
* <p>P2SH is described by <a href="https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki">BIP 16</a> and
|
||||
* <a href="https://bitcoin.org/en/developer-guide#p2sh-scripts">documented in the Bitcoin Developer Guide</a>.</p>
|
||||
*
|
||||
* @param networkParameters needed to specify an address
|
||||
* @return null if the output script does not pay to a script hash
|
||||
* @return an address that belongs to the redeem script
|
||||
*/
|
||||
@Nullable
|
||||
public Address getAddressFromP2SH(NetworkParameters networkParameters) throws ScriptException{
|
||||
if (getScriptPubKey().isPayToScriptHash())
|
||||
return getScriptPubKey().getToAddress(networkParameters);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
value = readInt64();
|
||||
scriptLen = (int) readVarInt();
|
||||
length = cursor - offset + scriptLen;
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
scriptBytes = readBytes(scriptLen);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
checkNotNull(scriptBytes);
|
||||
maybeParse();
|
||||
Utils.int64ToByteStreamLE(value, stream);
|
||||
// TODO: Move script serialization into the Script class, where it belongs.
|
||||
stream.write(new VarInt(scriptBytes.length).encode());
|
||||
stream.write(scriptBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this output. This is the amount of currency that the destination address
|
||||
* receives.
|
||||
*/
|
||||
public Coin getValue() {
|
||||
maybeParse();
|
||||
try {
|
||||
return Coin.valueOf(value);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalStateException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this output.
|
||||
*/
|
||||
public void setValue(Coin value) {
|
||||
checkNotNull(value);
|
||||
unCache();
|
||||
this.value = value.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of this output in the parent transaction, or throws if this output is free standing. Iterates
|
||||
* over the parents list to discover this.
|
||||
*/
|
||||
public int getIndex() {
|
||||
for (int i = 0; i < getParentTransaction().getOutputs().size(); i++) {
|
||||
if (getParentTransaction().getOutputs().get(i) == this)
|
||||
return i;
|
||||
}
|
||||
throw new IllegalStateException("Output linked to wrong parent transaction?");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the minimum value for a txout of this size to be considered non-dust by a reference client
|
||||
* (and thus relayed). See: CTxOut::IsDust() in the reference client. The assumption is that any output that would
|
||||
* consume more than a third of its value in fees is not something the Bitcoin system wants to deal with right now,
|
||||
* so we call them "dust outputs" and they're made non standard. The choice of one third is somewhat arbitrary and
|
||||
* may change in future.</p>
|
||||
*
|
||||
* <p>You probably should use {@link com.dogecoin.dogecoinj.core.TransactionOutput#getMinNonDustValue()} which uses
|
||||
* a safe fee-per-kb by default.</p>
|
||||
*
|
||||
* @param feePerKbRequired The fee required per kilobyte. Note that this is the same as the reference client's -minrelaytxfee * 3
|
||||
* If you want a safe default, use {@link Transaction#REFERENCE_DEFAULT_MIN_TX_FEE}*3
|
||||
*/
|
||||
public Coin getMinNonDustValue(Coin feePerKbRequired) {
|
||||
// A typical output is 33 bytes (pubkey hash + opcodes) and requires an input of 148 bytes to spend so we add
|
||||
// that together to find out the total amount of data used to transfer this amount of value. Note that this
|
||||
// formula is wrong for anything that's not a pay-to-address output, unfortunately, we must follow the reference
|
||||
// clients wrongness in order to ensure we're considered standard. A better formula would either estimate the
|
||||
// size of data needed to satisfy all different script types, or just hard code 33 below.
|
||||
|
||||
// DOGE doesn't enforce these rules. Therefore we consider each output as valid.
|
||||
return Coin.ZERO;
|
||||
/**final BigInteger size = BigInteger.valueOf(this.bitcoinSerialize().length + 148);
|
||||
BigInteger[] nonDustAndRemainder = feePerKbRequired.multiply(size).divideAndRemainder(BigInteger.valueOf(1000));
|
||||
return nonDustAndRemainder[1].equals(BigInteger.ZERO) ? nonDustAndRemainder[0] : nonDustAndRemainder[0].add(BigInteger.ONE);**/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum value for this output to be considered "not dust", i.e. the transaction will be relayable
|
||||
* and mined by default miners. For normal pay to address outputs, this is 546 satoshis, the same as
|
||||
* {@link Transaction#MIN_NONDUST_OUTPUT}.
|
||||
*/
|
||||
public Coin getMinNonDustValue() {
|
||||
return getMinNonDustValue(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this objects availableForSpending flag to false and the spentBy pointer to the given input.
|
||||
* If the input is null, it means this output was signed over to somebody else rather than one of our own keys.
|
||||
* @throws IllegalStateException if the transaction was already marked as spent.
|
||||
*/
|
||||
public void markAsSpent(TransactionInput input) {
|
||||
checkState(availableForSpending);
|
||||
availableForSpending = false;
|
||||
spentBy = input;
|
||||
if (parent != null)
|
||||
if (log.isDebugEnabled()) log.debug("Marked {}:{} as spent by {}", getParentTransaction().getHash(), getIndex(), input);
|
||||
else
|
||||
if (log.isDebugEnabled()) log.debug("Marked floating output as spent by {}", input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the spent pointer / availableForSpending flag to null.
|
||||
*/
|
||||
public void markAsUnspent() {
|
||||
if (parent != null)
|
||||
if (log.isDebugEnabled()) log.debug("Un-marked {}:{} as spent by {}", getParentTransaction().getHash(), getIndex(), spentBy);
|
||||
else
|
||||
if (log.isDebugEnabled()) log.debug("Un-marked floating output as spent by {}", spentBy);
|
||||
availableForSpending = true;
|
||||
spentBy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@link TransactionOutput#markAsSpent(TransactionInput)} has been called on this class. A
|
||||
* {@link Wallet} will mark a transaction output as spent once it sees a transaction input that is connected to it.
|
||||
* Note that this flag can be false when an output has in fact been spent according to the rest of the network if
|
||||
* the spending transaction wasn't downloaded yet, and it can be marked as spent when in reality the rest of the
|
||||
* network believes it to be unspent if the signature or script connecting to it was not actually valid.
|
||||
*/
|
||||
public boolean isAvailableForSpending() {
|
||||
return availableForSpending;
|
||||
}
|
||||
|
||||
/**
|
||||
* The backing script bytes which can be turned into a Script object.
|
||||
* @return the scriptBytes
|
||||
*/
|
||||
public byte[] getScriptBytes() {
|
||||
maybeParse();
|
||||
return scriptBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this output is to a key in the wallet or to an address/script we are watching.
|
||||
*/
|
||||
public boolean isMineOrWatched(TransactionBag transactionBag) {
|
||||
return isMine(transactionBag) || isWatched(transactionBag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this output is to a key, or an address we have the keys for, in the wallet.
|
||||
*/
|
||||
public boolean isWatched(TransactionBag transactionBag) {
|
||||
try {
|
||||
Script script = getScriptPubKey();
|
||||
return transactionBag.isWatchedScript(script);
|
||||
} catch (ScriptException e) {
|
||||
// Just means we didn't understand the output of this transaction: ignore it.
|
||||
log.debug("Could not parse tx output script: {}", e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this output is to a key, or an address we have the keys for, in the wallet.
|
||||
*/
|
||||
public boolean isMine(TransactionBag transactionBag) {
|
||||
try {
|
||||
Script script = getScriptPubKey();
|
||||
if (script.isSentToRawPubKey()) {
|
||||
byte[] pubkey = script.getPubKey();
|
||||
return transactionBag.isPubKeyMine(pubkey);
|
||||
} if (script.isPayToScriptHash()) {
|
||||
return transactionBag.isPayToScriptHashMine(script.getPubKeyHash());
|
||||
} else {
|
||||
byte[] pubkeyHash = script.getPubKeyHash();
|
||||
return transactionBag.isPubKeyHashMine(pubkeyHash);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
// Just means we didn't understand the output of this transaction: ignore it.
|
||||
log.debug("Could not parse tx output script: {}", e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human readable debug string.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
Script script = getScriptPubKey();
|
||||
StringBuilder buf = new StringBuilder("TxOut of ");
|
||||
buf.append(Coin.valueOf(value).toFriendlyString());
|
||||
if (script.isSentToAddress() || script.isPayToScriptHash())
|
||||
buf.append(" to ").append(script.getToAddress(params));
|
||||
else if (script.isSentToRawPubKey())
|
||||
buf.append(" to pubkey ").append(Utils.HEX.encode(script.getPubKey()));
|
||||
else if (script.isSentToMultiSig())
|
||||
buf.append(" to multisig");
|
||||
else
|
||||
buf.append(" (unknown type)");
|
||||
buf.append(" script:");
|
||||
buf.append(script);
|
||||
return buf.toString();
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connected input.
|
||||
*/
|
||||
@Nullable
|
||||
public TransactionInput getSpentBy() {
|
||||
return spentBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction that owns this output.
|
||||
*/
|
||||
@Nullable
|
||||
public Transaction getParentTransaction() {
|
||||
if(parent != null) {
|
||||
return (Transaction) parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transaction hash that owns this output.
|
||||
*/
|
||||
@Nullable
|
||||
public Sha256Hash getParentTransactionHash() {
|
||||
if (getParentTransaction() != null) {
|
||||
return getParentTransaction().getHash();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the depth in blocks of the parent tx.
|
||||
*
|
||||
* <p>If the transaction appears in the top block, the depth is one. If it's anything else (pending, dead, unknown)
|
||||
* then -1.</p>
|
||||
* @return The tx depth or -1.
|
||||
*/
|
||||
public int getParentTransactionDepthInBlocks() {
|
||||
if (getParentTransaction() != null) {
|
||||
TransactionConfidence confidence = getParentTransaction().getConfidence();
|
||||
if (confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
|
||||
return confidence.getDepthInBlocks();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure object is fully parsed before invoking java serialization. The backing byte array
|
||||
* is transient so if the object has parseLazy = true and hasn't invoked checkParse yet
|
||||
* then data will be lost during serialization.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
maybeParse();
|
||||
out.defaultWriteObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link TransactionOutPoint}, which is essentially a structure pointing to this output.
|
||||
* Requires that this output is not detached.
|
||||
*/
|
||||
public TransactionOutPoint getOutPointFor() {
|
||||
return new TransactionOutPoint(params, getIndex(), getParentTransaction());
|
||||
}
|
||||
|
||||
/** Returns a copy of the output detached from its containing transaction, if need be. */
|
||||
public TransactionOutput duplicateDetached() {
|
||||
return new TransactionOutput(params, null, Coin.valueOf(value), org.spongycastle.util.Arrays.clone(scriptBytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TransactionOutput other = (TransactionOutput) o;
|
||||
|
||||
if (!Arrays.equals(scriptBytes, other.scriptBytes)) return false;
|
||||
if (value != other.value) return false;
|
||||
if (parent != null && parent != other.parent) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * (int) value + (scriptBytes != null ? Arrays.hashCode(scriptBytes) : 0);
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 Matt Corallo.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>TransactionOutputChanges represents a delta to the set of unspent outputs. It used as a return value for
|
||||
* {@link AbstractBlockChain#connectTransactions(int, Block)}. It contains the full list of transaction outputs created
|
||||
* and spent in a block. It DOES contain outputs created that were spent later in the block, as those are needed for
|
||||
* BIP30 (no duplicate txid creation if the previous one was not fully spent prior to this block) verification.</p>
|
||||
*/
|
||||
public class TransactionOutputChanges {
|
||||
public final List<UTXO> txOutsCreated;
|
||||
public final List<UTXO> txOutsSpent;
|
||||
|
||||
public TransactionOutputChanges(List<UTXO> txOutsCreated, List<UTXO> txOutsSpent) {
|
||||
this.txOutsCreated = txOutsCreated;
|
||||
this.txOutsSpent = txOutsSpent;
|
||||
}
|
||||
|
||||
public TransactionOutputChanges(InputStream in) throws IOException {
|
||||
int numOutsCreated = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
txOutsCreated = new LinkedList<UTXO>();
|
||||
for (int i = 0; i < numOutsCreated; i++)
|
||||
txOutsCreated.add(new UTXO(in));
|
||||
|
||||
int numOutsSpent = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
txOutsSpent = new LinkedList<UTXO>();
|
||||
for (int i = 0; i < numOutsSpent; i++)
|
||||
txOutsSpent.add(new UTXO(in));
|
||||
}
|
||||
|
||||
public void serializeToStream(OutputStream bos) throws IOException {
|
||||
int numOutsCreated = txOutsCreated.size();
|
||||
bos.write(0xFF & (numOutsCreated >> 0));
|
||||
bos.write(0xFF & (numOutsCreated >> 8));
|
||||
bos.write(0xFF & (numOutsCreated >> 16));
|
||||
bos.write(0xFF & (numOutsCreated >> 24));
|
||||
for (UTXO output : txOutsCreated) {
|
||||
output.serializeToStream(bos);
|
||||
}
|
||||
|
||||
int numOutsSpent = txOutsSpent.size();
|
||||
bos.write(0xFF & (numOutsSpent >> 0));
|
||||
bos.write(0xFF & (numOutsSpent >> 8));
|
||||
bos.write(0xFF & (numOutsSpent >> 16));
|
||||
bos.write(0xFF & (numOutsSpent >> 24));
|
||||
for (UTXO output : txOutsSpent) {
|
||||
output.serializeToStream(bos);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.utils.Threading;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* <p>Tracks transactions that are being announced across the network. Typically one is created for you by a
|
||||
* {@link PeerGroup} and then given to each Peer to update. The current purpose is to let Peers update the confidence
|
||||
* (number of peers broadcasting). It helps address an attack scenario in which a malicious remote peer (or several)
|
||||
* feeds you invalid transactions, eg, ones that spend coins which don't exist. If you don't see most of the peers
|
||||
* announce the transaction within a reasonable time, it may be that the TX is not valid. Alternatively, an attacker
|
||||
* may control your entire internet connection: in this scenario counting broadcasting peers does not help you.</p>
|
||||
*
|
||||
* <p>It is <b>not</b> at this time directly equivalent to the Satoshi clients memory pool, which tracks
|
||||
* all transactions not currently included in the best chain - it's simply a cache.</p>
|
||||
*/
|
||||
public class TxConfidenceTable {
|
||||
private static final Logger log = LoggerFactory.getLogger(TxConfidenceTable.class);
|
||||
protected ReentrantLock lock = Threading.lock("txconfidencetable");
|
||||
|
||||
// For each transaction we may have seen:
|
||||
// - only its hash in an inv packet
|
||||
// - the full transaction itself, if we asked for it to be sent to us (or a peer sent it regardless), or if we
|
||||
// sent it.
|
||||
//
|
||||
// Before we see the full transaction, we need to track how many peers advertised it, so we can estimate its
|
||||
// confidence pre-chain inclusion assuming an un-tampered with network connection. After we see the full transaction
|
||||
// we need to switch from tracking that data in the Entry to tracking it in the TransactionConfidence object itself.
|
||||
private static class WeakTransactionReference extends WeakReference<Transaction> {
|
||||
public Sha256Hash hash;
|
||||
public WeakTransactionReference(Transaction tx, ReferenceQueue<Transaction> queue) {
|
||||
super(tx, queue);
|
||||
hash = tx.getHash();
|
||||
}
|
||||
}
|
||||
private static class Entry {
|
||||
// Invariants: one of the two fields must be null, to indicate which is used.
|
||||
Set<PeerAddress> addresses;
|
||||
// We keep a weak reference to the transaction. This means that if no other bit of code finds the transaction
|
||||
// worth keeping around it will drop out of memory and we will, at some point, forget about it, which means
|
||||
// both addresses and tx.get() will be null. When this happens the WeakTransactionReference appears in the queue
|
||||
// allowing us to delete the associated entry (the tx itself has already gone away).
|
||||
WeakTransactionReference tx;
|
||||
}
|
||||
private LinkedHashMap<Sha256Hash, Entry> table;
|
||||
|
||||
// This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the TxConfidenceTable is the
|
||||
// only thing that is tracking the transaction anymore. We check it from time to time and delete table entries
|
||||
// corresponding to expired transactions. In this way memory usage of the system is in line with however many
|
||||
// transactions you actually care to track the confidence of. We can still end up with lots of hashes being stored
|
||||
// if our peers flood us with invs but the MAX_SIZE param caps this.
|
||||
private ReferenceQueue<Transaction> referenceQueue;
|
||||
|
||||
/** The max size of a table created with the no-args constructor. */
|
||||
public static final int MAX_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* Creates a table that will track at most the given number of transactions (allowing you to bound memory
|
||||
* usage).
|
||||
* @param size Max number of transactions to track. The table will fill up to this size then stop growing.
|
||||
*/
|
||||
public TxConfidenceTable(final int size) {
|
||||
table = new LinkedHashMap<Sha256Hash, Entry>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, TxConfidenceTable.Entry> entry) {
|
||||
// An arbitrary choice to stop the memory used by tracked transactions getting too huge in the event
|
||||
// of some kind of DoS attack.
|
||||
return size() > size;
|
||||
}
|
||||
};
|
||||
referenceQueue = new ReferenceQueue<Transaction>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table that will track at most {@link TxConfidenceTable#MAX_SIZE} entries. You should normally use
|
||||
* this constructor.
|
||||
*/
|
||||
public TxConfidenceTable() {
|
||||
this(MAX_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* If any transactions have expired due to being only weakly reachable through us, go ahead and delete their
|
||||
* table entries - it means we downloaded the transaction and sent it to various event listeners, none of
|
||||
* which bothered to keep a reference. Typically, this is because the transaction does not involve any keys that
|
||||
* are relevant to any of our wallets.
|
||||
*/
|
||||
private void cleanTable() {
|
||||
lock.lock();
|
||||
try {
|
||||
Reference<? extends Transaction> ref;
|
||||
while ((ref = referenceQueue.poll()) != null) {
|
||||
// Find which transaction got deleted by the GC.
|
||||
WeakTransactionReference txRef = (WeakTransactionReference) ref;
|
||||
// And remove the associated map entry so the other bits of memory can also be reclaimed.
|
||||
table.remove(txRef.hash);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of peers that have seen the given hash recently.
|
||||
*/
|
||||
public int numBroadcastPeers(Sha256Hash txHash) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanTable();
|
||||
Entry entry = table.get(txHash);
|
||||
if (entry == null) {
|
||||
// No such TX known.
|
||||
return 0;
|
||||
} else if (entry.tx == null) {
|
||||
// We've seen at least one peer announce with an inv.
|
||||
checkNotNull(entry.addresses);
|
||||
return entry.addresses.size();
|
||||
} else {
|
||||
final Transaction tx = entry.tx.get();
|
||||
if (tx == null) {
|
||||
// We previously downloaded this transaction, but nothing cared about it so the garbage collector threw
|
||||
// it away. We also deleted the set that tracked which peers had seen it. Treat this case as a zero and
|
||||
// just delete it from the map.
|
||||
table.remove(txHash);
|
||||
return 0;
|
||||
} else {
|
||||
checkState(entry.addresses == null);
|
||||
return tx.getConfidence().numBroadcastPeers();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the tx into the table and returns either it, or a different Transaction object that has the same hash.
|
||||
* Unlike seen and the other methods, this one does not imply that a tx has been announced by a peer and does
|
||||
* not mark it as such.
|
||||
*/
|
||||
public Transaction intern(Transaction tx) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanTable();
|
||||
Entry entry = table.get(tx.getHash());
|
||||
if (entry != null) {
|
||||
// This TX or its hash have been previously interned.
|
||||
if (entry.tx != null) {
|
||||
// We already interned it (but may have thrown it away).
|
||||
checkState(entry.addresses == null);
|
||||
// We only want one canonical object instance for a transaction no matter how many times it is
|
||||
// deserialized.
|
||||
Transaction transaction = entry.tx.get();
|
||||
if (transaction != null) {
|
||||
// We saw it before and kept it around. Hand back the canonical copy.
|
||||
tx = transaction;
|
||||
}
|
||||
return tx;
|
||||
} else {
|
||||
// We received a transaction that we have previously seen announced but not downloaded until now.
|
||||
checkNotNull(entry.addresses);
|
||||
entry.tx = new WeakTransactionReference(tx, referenceQueue);
|
||||
Set<PeerAddress> addrs = entry.addresses;
|
||||
entry.addresses = null;
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
log.debug("Adding tx [{}] {} to the confidence table",
|
||||
confidence.numBroadcastPeers(), tx.getHashAsString());
|
||||
for (PeerAddress a : addrs) {
|
||||
markBroadcast(a, tx);
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
} else {
|
||||
// This often happens when we are downloading a Bloom filtered chain, or recursively downloading
|
||||
// dependencies of a relevant transaction (see Peer.downloadDependencies).
|
||||
log.debug("Provided with a downloaded transaction we didn't see announced yet: {}", tx.getHashAsString());
|
||||
entry = new Entry();
|
||||
entry.tx = new WeakTransactionReference(tx, referenceQueue);
|
||||
table.put(tx.getHash(), entry);
|
||||
return tx;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by peers when they receive a "tx" message containing a valid serialized transaction.
|
||||
* @param tx The TX deserialized from the wire.
|
||||
* @param byPeer The Peer that received it.
|
||||
* @return An object that is semantically the same TX but may be a different object instance.
|
||||
*/
|
||||
public Transaction seen(Transaction tx, PeerAddress byPeer) {
|
||||
lock.lock();
|
||||
try {
|
||||
final Transaction interned = intern(tx);
|
||||
markBroadcast(byPeer, interned);
|
||||
return interned;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by peers when they see a transaction advertised in an "inv" message. It either will increase the
|
||||
* confidence of the pre-existing transaction or will just keep a record of the address for future usage.
|
||||
*/
|
||||
public void seen(Sha256Hash hash, PeerAddress byPeer) {
|
||||
lock.lock();
|
||||
try {
|
||||
cleanTable();
|
||||
Entry entry = table.get(hash);
|
||||
if (entry != null) {
|
||||
// This TX or its hash have been previously announced.
|
||||
if (entry.tx != null) {
|
||||
checkState(entry.addresses == null);
|
||||
Transaction tx = entry.tx.get();
|
||||
if (tx != null) {
|
||||
markBroadcast(byPeer, tx);
|
||||
log.debug("{}: Peer announced transaction we have seen before [{}] {}",
|
||||
byPeer, tx.getConfidence().numBroadcastPeers(), tx.getHashAsString());
|
||||
} else {
|
||||
// The inv is telling us about a transaction that we previously downloaded, and threw away
|
||||
// because nothing found it interesting enough to keep around. So do nothing.
|
||||
}
|
||||
} else {
|
||||
checkNotNull(entry.addresses);
|
||||
entry.addresses.add(byPeer);
|
||||
log.debug("{}: Peer announced transaction we have seen announced before [{}] {}",
|
||||
byPeer, entry.addresses.size(), hash);
|
||||
}
|
||||
} else {
|
||||
// This TX has never been seen before.
|
||||
entry = new Entry();
|
||||
// TODO: Using hashsets here is inefficient compared to just having an array.
|
||||
entry.addresses = new HashSet<PeerAddress>();
|
||||
entry.addresses.add(byPeer);
|
||||
table.put(hash, entry);
|
||||
log.info("{}: Peer announced new transaction [1] {}", byPeer, hash);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void markBroadcast(PeerAddress byPeer, Transaction tx) {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
final TransactionConfidence confidence = tx.getConfidence();
|
||||
if (confidence.markBroadcastBy(byPeer))
|
||||
confidence.queueListeners(TransactionConfidence.Listener.ChangeReason.SEEN_PEERS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Transaction} for the given hash if we have downloaded it, or null if that hash is unknown or
|
||||
* we only saw advertisements for it yet or it has been downloaded but garbage collected due to nowhere else
|
||||
* holding a reference to it.
|
||||
*/
|
||||
@Nullable
|
||||
public Transaction get(Sha256Hash hash) {
|
||||
lock.lock();
|
||||
try {
|
||||
Entry entry = table.get(hash);
|
||||
if (entry == null) return null; // Unknown.
|
||||
if (entry.tx == null) return null; // Seen but only in advertisements.
|
||||
if (entry.tx.get() == null) return null; // Was downloaded but garbage collected.
|
||||
Transaction tx = entry.tx.get();
|
||||
checkNotNull(tx);
|
||||
return tx;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the TX identified by hash has been seen before (ie, in an inv). Note that a transaction that
|
||||
* was broadcast, downloaded and nothing kept a reference to it will eventually be cleared out by the garbage
|
||||
* collector and wasSeen() will return false - it does not keep a permanent record of every hash ever broadcast.
|
||||
*/
|
||||
public boolean maybeWasSeen(Sha256Hash hash) {
|
||||
lock.lock();
|
||||
try {
|
||||
Entry entry = table.get(hash);
|
||||
return entry != null;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
/**
|
||||
* Copyright 2012 Matt Corallo.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* A UTXO message contains the information necessary to check a spending transaction.
|
||||
* It avoids having to store the entire parentTransaction just to get the hash and index.
|
||||
* Useful when working with free standing outputs.
|
||||
*/
|
||||
public class UTXO implements Serializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(UTXO.class);
|
||||
private static final long serialVersionUID = -8744924157056340509L;
|
||||
|
||||
/**
|
||||
* A transaction output has some value and a script used for authenticating that the redeemer is allowed to spend
|
||||
* this output.
|
||||
*/
|
||||
private Coin value;
|
||||
private byte[] scriptBytes;
|
||||
|
||||
/** Hash of the transaction to which we refer. */
|
||||
private Sha256Hash hash;
|
||||
/** Which output of that transaction we are talking about. */
|
||||
private long index;
|
||||
/** The height of the tx of this output */
|
||||
private int height;
|
||||
/** If this output is from a coinbase tx */
|
||||
private boolean coinbase;
|
||||
/** The address of this output */
|
||||
private String address;
|
||||
/** The type of this address */
|
||||
private int addressType;
|
||||
|
||||
/**
|
||||
* Creates a stored transaction output.
|
||||
* @param hash The hash of the containing transaction.
|
||||
* @param index The outpoint.
|
||||
* @param value The value available.
|
||||
* @param height The height this output was created in.
|
||||
* @param coinbase The coinbase flag.
|
||||
* @param scriptBytes The script bytes.
|
||||
*/
|
||||
public UTXO(Sha256Hash hash,
|
||||
long index,
|
||||
Coin value,
|
||||
int height,
|
||||
boolean coinbase,
|
||||
byte[] scriptBytes) {
|
||||
this.hash = hash;
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.height = height;
|
||||
this.scriptBytes = scriptBytes;
|
||||
this.coinbase = coinbase;
|
||||
this.address = "";
|
||||
this.addressType = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a stored transaction output.
|
||||
* @param hash The hash of the containing transaction.
|
||||
* @param index The outpoint.
|
||||
* @param value The value available.
|
||||
* @param height The height this output was created in.
|
||||
* @param coinbase The coinbase flag.
|
||||
* @param scriptBytes The script bytes.
|
||||
* @param address The address.
|
||||
* @param addressType The address type.
|
||||
*/
|
||||
public UTXO(Sha256Hash hash,
|
||||
long index,
|
||||
Coin value,
|
||||
int height,
|
||||
boolean coinbase,
|
||||
byte[] scriptBytes,
|
||||
String address,
|
||||
int addressType) {
|
||||
this(hash, index, value, height, coinbase, scriptBytes);
|
||||
this.address = address;
|
||||
this.addressType = addressType;
|
||||
}
|
||||
|
||||
public UTXO(InputStream in) throws IOException {
|
||||
byte[] valueBytes = new byte[8];
|
||||
if (in.read(valueBytes, 0, 8) != 8)
|
||||
throw new EOFException();
|
||||
value = Coin.valueOf(Utils.readInt64(valueBytes, 0));
|
||||
|
||||
int scriptBytesLength = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
scriptBytes = new byte[scriptBytesLength];
|
||||
if (in.read(scriptBytes) != scriptBytesLength)
|
||||
throw new EOFException();
|
||||
|
||||
byte[] hashBytes = new byte[32];
|
||||
if (in.read(hashBytes) != 32)
|
||||
throw new EOFException();
|
||||
hash = new Sha256Hash(hashBytes);
|
||||
|
||||
byte[] indexBytes = new byte[4];
|
||||
if (in.read(indexBytes) != 4)
|
||||
throw new EOFException();
|
||||
index = Utils.readUint32(indexBytes, 0);
|
||||
|
||||
height = ((in.read() & 0xFF) << 0) |
|
||||
((in.read() & 0xFF) << 8) |
|
||||
((in.read() & 0xFF) << 16) |
|
||||
((in.read() & 0xFF) << 24);
|
||||
|
||||
byte[] coinbaseByte = new byte[1];
|
||||
in.read(coinbaseByte);
|
||||
if (coinbaseByte[0] == 1) {
|
||||
coinbase = true;
|
||||
} else {
|
||||
coinbase = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value which this Transaction output holds.
|
||||
* @return the value.
|
||||
*/
|
||||
public Coin getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The backing script bytes which can be turned into a Script object.
|
||||
* @return the scriptBytes.
|
||||
*/
|
||||
public byte[] getScriptBytes() {
|
||||
return scriptBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash of the transaction which holds this output.
|
||||
* @return the hash.
|
||||
*/
|
||||
public Sha256Hash getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The index of this output in the transaction which holds it.
|
||||
* @return the index.
|
||||
*/
|
||||
public long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of the block that created this output.
|
||||
* @return The height.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flag of whether this was created by a coinbase tx.
|
||||
* @return The coinbase flag.
|
||||
*/
|
||||
public boolean isCoinbase() {
|
||||
return coinbase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The address of this output.
|
||||
* @return The address.
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the address.
|
||||
* @return The address type.
|
||||
*/
|
||||
public int getAddressType() {
|
||||
return addressType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Stored TxOut of %s (%s:%d)", value.toFriendlyString(), hash.toString(), index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash.hashCode() + (int)index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UTXO other = (UTXO) o;
|
||||
return getHash().equals(other.getHash()) &&
|
||||
getIndex() == other.getIndex();
|
||||
}
|
||||
|
||||
public void serializeToStream(OutputStream bos) throws IOException {
|
||||
Utils.uint64ToByteStreamLE(BigInteger.valueOf(value.value), bos);
|
||||
|
||||
bos.write(0xFF & scriptBytes.length >> 0);
|
||||
bos.write(0xFF & scriptBytes.length >> 8);
|
||||
bos.write(0xFF & (scriptBytes.length >> 16));
|
||||
bos.write(0xFF & (scriptBytes.length >> 24));
|
||||
bos.write(scriptBytes);
|
||||
|
||||
bos.write(hash.getBytes());
|
||||
Utils.uint32ToByteStreamLE(index, bos);
|
||||
|
||||
bos.write(0xFF & (height >> 0));
|
||||
bos.write(0xFF & (height >> 8));
|
||||
bos.write(0xFF & (height >> 16));
|
||||
bos.write(0xFF & (height >> 24));
|
||||
|
||||
byte[] coinbaseByte = new byte[1];
|
||||
if(coinbase) {
|
||||
coinbaseByte[0] = 1;
|
||||
} else {
|
||||
coinbaseByte[0] = 0;
|
||||
}
|
||||
bos.write(coinbaseByte);
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Kalpesh Parmar.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A UTXOProvider encapsulates functionality for returning unspent transaction outputs,
|
||||
* for use by the wallet or other code that crafts spends.
|
||||
*
|
||||
* <p>A {@link com.dogecoin.dogecoinj.store.FullPrunedBlockStore} is an internal implementation within bitcoinj.</p>
|
||||
*/
|
||||
public interface UTXOProvider {
|
||||
|
||||
/**
|
||||
* // TODO currently the access to outputs is by address. Change to ECKey
|
||||
* Get the list of {@link UTXO}'s for a given address.
|
||||
* @param addresses List of address.
|
||||
* @return The list of transaction outputs.
|
||||
* @throws UTXOProvider If there is an error.
|
||||
*/
|
||||
List<UTXO> getOpenTransactionOutputs(List<Address> addresses) throws UTXOProviderException;
|
||||
|
||||
/**
|
||||
* Get the height of the chain head.
|
||||
* @return The chain head height.
|
||||
* @throws UTXOProvider If there is an error.
|
||||
*/
|
||||
int getChainHeadHeight() throws UTXOProviderException;
|
||||
|
||||
/**
|
||||
* The {@link NetworkParameters} of this provider.
|
||||
* @return The network parameters.
|
||||
*/
|
||||
NetworkParameters getParams();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Kalpesh Parmar.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
public class UTXOProviderException extends Exception {
|
||||
public UTXOProviderException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UTXOProviderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UTXOProviderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UTXOProviderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 the bitcoinj authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/** Message representing a list of unspent transaction outputs, returned in response to sending a GetUTXOsMessage. */
|
||||
public class UTXOsMessage extends Message {
|
||||
private long height;
|
||||
private Sha256Hash chainHead;
|
||||
private byte[] hits; // little-endian bitset indicating whether an output was found or not.
|
||||
|
||||
private List<TransactionOutput> outputs;
|
||||
private long[] heights;
|
||||
|
||||
/** This is a special sentinel value that can appear in the heights field if the given tx is in the mempool. */
|
||||
public static long MEMPOOL_HEIGHT = 0x7FFFFFFFL;
|
||||
|
||||
public UTXOsMessage(NetworkParameters params, byte[] payloadBytes) {
|
||||
super(params, payloadBytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an array of output objects, with nulls indicating that the output was missing. The bitset will
|
||||
* be calculated from this.
|
||||
*/
|
||||
public UTXOsMessage(NetworkParameters params, List<TransactionOutput> outputs, long[] heights, Sha256Hash chainHead, long height) {
|
||||
super(params);
|
||||
hits = new byte[(int) Math.ceil(outputs.size() / 8.0)];
|
||||
for (int i = 0; i < outputs.size(); i++) {
|
||||
if (outputs.get(i) != null)
|
||||
Utils.setBitLE(hits, i);
|
||||
}
|
||||
this.outputs = new ArrayList<TransactionOutput>(outputs.size());
|
||||
for (TransactionOutput output : outputs) {
|
||||
if (output != null) this.outputs.add(output);
|
||||
}
|
||||
this.chainHead = chainHead;
|
||||
this.height = height;
|
||||
this.heights = Arrays.copyOf(heights, heights.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
Utils.uint32ToByteStreamLE(height, stream);
|
||||
stream.write(chainHead.getBytes());
|
||||
stream.write(new VarInt(hits.length).encode());
|
||||
stream.write(hits);
|
||||
stream.write(new VarInt(outputs.size()).encode());
|
||||
for (TransactionOutput output : outputs) {
|
||||
// TODO: Allow these to be specified, if one day we care about sending this message ourselves
|
||||
// (currently it's just used for unit testing).
|
||||
Utils.uint32ToByteStreamLE(0L, stream); // Version
|
||||
Utils.uint32ToByteStreamLE(0L, stream); // Height
|
||||
output.bitcoinSerializeToStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
// Format is:
|
||||
// uint32 chainHeight
|
||||
// uint256 chainHeadHash
|
||||
// vector<unsigned char> hitsBitmap;
|
||||
// vector<CCoin> outs;
|
||||
//
|
||||
// A CCoin is { int nVersion, int nHeight, CTxOut output }
|
||||
// The bitmap indicates which of the requested TXOs were found in the UTXO set.
|
||||
height = readUint32();
|
||||
chainHead = readHash();
|
||||
int numBytes = (int) readVarInt();
|
||||
if (numBytes < 0 || numBytes > InventoryMessage.MAX_INVENTORY_ITEMS / 8)
|
||||
throw new ProtocolException("hitsBitmap out of range: " + numBytes);
|
||||
hits = readBytes(numBytes);
|
||||
int numOuts = (int) readVarInt();
|
||||
if (numOuts < 0 || numOuts > InventoryMessage.MAX_INVENTORY_ITEMS)
|
||||
throw new ProtocolException("numOuts out of range: " + numOuts);
|
||||
outputs = new ArrayList<TransactionOutput>(numOuts);
|
||||
heights = new long[numOuts];
|
||||
for (int i = 0; i < numOuts; i++) {
|
||||
long version = readUint32();
|
||||
long height = readUint32();
|
||||
if (version > 1)
|
||||
throw new ProtocolException("Unknown tx version in getutxo output: " + version);
|
||||
TransactionOutput output = new TransactionOutput(params, null, payload, cursor);
|
||||
outputs.add(output);
|
||||
heights[i] = height;
|
||||
cursor += output.length;
|
||||
}
|
||||
length = cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// Not used.
|
||||
}
|
||||
|
||||
public byte[] getHitMap() {
|
||||
return Arrays.copyOf(hits, hits.length);
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getOutputs() {
|
||||
return new ArrayList<TransactionOutput>(outputs);
|
||||
}
|
||||
|
||||
public long[] getHeights() { return Arrays.copyOf(heights, heights.length); }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UTXOsMessage{" +
|
||||
"height=" + height +
|
||||
", chainHead=" + chainHead +
|
||||
", hitMap=" + Arrays.toString(hits) +
|
||||
", outputs=" + outputs +
|
||||
", heights=" + Arrays.toString(heights) +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
UTXOsMessage message = (UTXOsMessage) o;
|
||||
|
||||
if (height != message.height) return false;
|
||||
if (!chainHead.equals(message.chainHead)) return false;
|
||||
if (!Arrays.equals(heights, message.heights)) return false;
|
||||
if (!Arrays.equals(hits, message.hits)) return false;
|
||||
if (!outputs.equals(message.outputs)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (height ^ (height >>> 32));
|
||||
result = 31 * result + chainHead.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(hits);
|
||||
result = 31 * result + outputs.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(heights);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
public class UnknownMessage extends EmptyMessage {
|
||||
private static final long serialVersionUID = 3614705938207918775L;
|
||||
private String name;
|
||||
|
||||
public UnknownMessage(NetworkParameters params, String name, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes, 0);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Unknown message [" + name + "]: " + (payload == null ? "" : Utils.HEX.encode(payload));
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Steve Coughlan.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* An unsynchronized implementation of ByteArrayOutputStream that will return the backing byte array if its length == size().
|
||||
* This avoids unneeded array copy where the BOS is simply being used to extract a byte array of known length from a
|
||||
* 'serialized to stream' method.
|
||||
* <p/>
|
||||
* Unless the final length can be accurately predicted the only performance this will yield is due to unsynchronized
|
||||
* methods.
|
||||
*
|
||||
* @author git
|
||||
*/
|
||||
public class UnsafeByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
public UnsafeByteArrayOutputStream() {
|
||||
super(32);
|
||||
}
|
||||
|
||||
public UnsafeByteArrayOutputStream(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this byte array output stream.
|
||||
*
|
||||
* @param b the byte to be written.
|
||||
*/
|
||||
@Override
|
||||
public void write(int b) {
|
||||
int newcount = count + 1;
|
||||
if (newcount > buf.length) {
|
||||
buf = Utils.copyOf(buf, Math.max(buf.length << 1, newcount));
|
||||
}
|
||||
buf[count] = (byte) b;
|
||||
count = newcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <code>len</code> bytes from the specified byte array
|
||||
* starting at offset <code>off</code> to this byte array output stream.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
*/
|
||||
@Override
|
||||
public void write(byte b[], int off, int len) {
|
||||
if ((off < 0) || (off > b.length) || (len < 0) ||
|
||||
((off + len) > b.length) || ((off + len) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return;
|
||||
}
|
||||
int newcount = count + len;
|
||||
if (newcount > buf.length) {
|
||||
buf = Utils.copyOf(buf, Math.max(buf.length << 1, newcount));
|
||||
}
|
||||
System.arraycopy(b, off, buf, count, len);
|
||||
count = newcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte array output stream to
|
||||
* the specified output stream argument, as if by calling the output
|
||||
* stream's write method using <code>out.write(buf, 0, count)</code>.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
out.write(buf, 0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the <code>count</code> field of this byte array output
|
||||
* stream to zero, so that all currently accumulated output in the
|
||||
* output stream is discarded. The output stream can be used again,
|
||||
* reusing the already allocated buffer space.
|
||||
*
|
||||
* @see java.io.ByteArrayInputStream#count
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a newly allocated byte array. Its size is the current
|
||||
* size of this output stream and the valid contents of the buffer
|
||||
* have been copied into it.
|
||||
*
|
||||
* @return the current contents of this output stream, as a byte array.
|
||||
* @see java.io.ByteArrayOutputStream#size()
|
||||
*/
|
||||
@Override
|
||||
public byte toByteArray()[] {
|
||||
return count == buf.length ? buf : Utils.copyOf(buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current size of the buffer.
|
||||
*
|
||||
* @return the value of the <code>count</code> field, which is the number
|
||||
* of valid bytes in this output stream.
|
||||
* @see java.io.ByteArrayOutputStream#count
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
@ -1,657 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
|
||||
|
||||
/**
|
||||
* A collection of various utility methods that are helpful for working with the Bitcoin protocol.
|
||||
* To enable debug logging from the library, run with -Dbitcoinj.logging=true on your command line.
|
||||
*/
|
||||
public class Utils {
|
||||
private static final MessageDigest digest;
|
||||
static {
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e); // Can't happen.
|
||||
}
|
||||
}
|
||||
|
||||
/** The string that prefixes all text messages signed using Bitcoin keys. */
|
||||
public static final String BITCOIN_SIGNED_MESSAGE_HEADER = "Bitcoin Signed Message:\n";
|
||||
public static final byte[] BITCOIN_SIGNED_MESSAGE_HEADER_BYTES = BITCOIN_SIGNED_MESSAGE_HEADER.getBytes(Charsets.UTF_8);
|
||||
|
||||
private static BlockingQueue<Boolean> mockSleepQueue;
|
||||
|
||||
/**
|
||||
* The regular {@link java.math.BigInteger#toByteArray()} method isn't quite what we often need: it appends a
|
||||
* leading zero to indicate that the number is positive and may need padding.
|
||||
*
|
||||
* @param b the integer to format into a byte array
|
||||
* @param numBytes the desired size of the resulting byte array
|
||||
* @return numBytes byte long array.
|
||||
*/
|
||||
public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) {
|
||||
if (b == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = new byte[numBytes];
|
||||
byte[] biBytes = b.toByteArray();
|
||||
int start = (biBytes.length == numBytes + 1) ? 1 : 0;
|
||||
int length = Math.min(biBytes.length, numBytes);
|
||||
System.arraycopy(biBytes, start, bytes, numBytes - length, length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static void uint32ToByteArrayBE(long val, byte[] out, int offset) {
|
||||
out[offset + 0] = (byte) (0xFF & (val >> 24));
|
||||
out[offset + 1] = (byte) (0xFF & (val >> 16));
|
||||
out[offset + 2] = (byte) (0xFF & (val >> 8));
|
||||
out[offset + 3] = (byte) (0xFF & (val >> 0));
|
||||
}
|
||||
|
||||
public static void uint32ToByteArrayLE(long val, byte[] out, int offset) {
|
||||
out[offset + 0] = (byte) (0xFF & (val >> 0));
|
||||
out[offset + 1] = (byte) (0xFF & (val >> 8));
|
||||
out[offset + 2] = (byte) (0xFF & (val >> 16));
|
||||
out[offset + 3] = (byte) (0xFF & (val >> 24));
|
||||
}
|
||||
|
||||
public static void uint64ToByteArrayLE(long val, byte[] out, int offset) {
|
||||
out[offset + 0] = (byte) (0xFF & (val >> 0));
|
||||
out[offset + 1] = (byte) (0xFF & (val >> 8));
|
||||
out[offset + 2] = (byte) (0xFF & (val >> 16));
|
||||
out[offset + 3] = (byte) (0xFF & (val >> 24));
|
||||
out[offset + 4] = (byte) (0xFF & (val >> 32));
|
||||
out[offset + 5] = (byte) (0xFF & (val >> 40));
|
||||
out[offset + 6] = (byte) (0xFF & (val >> 48));
|
||||
out[offset + 7] = (byte) (0xFF & (val >> 56));
|
||||
}
|
||||
|
||||
public static void uint32ToByteStreamLE(long val, OutputStream stream) throws IOException {
|
||||
stream.write((int) (0xFF & (val >> 0)));
|
||||
stream.write((int) (0xFF & (val >> 8)));
|
||||
stream.write((int) (0xFF & (val >> 16)));
|
||||
stream.write((int) (0xFF & (val >> 24)));
|
||||
}
|
||||
|
||||
public static void int64ToByteStreamLE(long val, OutputStream stream) throws IOException {
|
||||
stream.write((int) (0xFF & (val >> 0)));
|
||||
stream.write((int) (0xFF & (val >> 8)));
|
||||
stream.write((int) (0xFF & (val >> 16)));
|
||||
stream.write((int) (0xFF & (val >> 24)));
|
||||
stream.write((int) (0xFF & (val >> 32)));
|
||||
stream.write((int) (0xFF & (val >> 40)));
|
||||
stream.write((int) (0xFF & (val >> 48)));
|
||||
stream.write((int) (0xFF & (val >> 56)));
|
||||
}
|
||||
|
||||
public static void uint64ToByteStreamLE(BigInteger val, OutputStream stream) throws IOException {
|
||||
byte[] bytes = val.toByteArray();
|
||||
if (bytes.length > 8) {
|
||||
throw new RuntimeException("Input too large to encode into a uint64");
|
||||
}
|
||||
bytes = reverseBytes(bytes);
|
||||
stream.write(bytes);
|
||||
if (bytes.length < 8) {
|
||||
for (int i = 0; i < 8 - bytes.length; i++)
|
||||
stream.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link Utils#doubleDigest(byte[], int, int)}.
|
||||
*/
|
||||
public static byte[] doubleDigest(byte[] input) {
|
||||
return doubleDigest(input, 0, input.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the SHA-256 hash of the given byte range, and then hashes the resulting hash again. This is
|
||||
* standard procedure in Bitcoin. The resulting hash is in big endian form.
|
||||
*/
|
||||
public static byte[] doubleDigest(byte[] input, int offset, int length) {
|
||||
synchronized (digest) {
|
||||
digest.reset();
|
||||
digest.update(input, offset, length);
|
||||
byte[] first = digest.digest();
|
||||
return digest.digest(first);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] scryptDigest(byte[] input) {
|
||||
try {
|
||||
return SCrypt.scrypt(input, input, 1024, 1, 1, 32);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] singleDigest(byte[] input, int offset, int length) {
|
||||
synchronized (digest) {
|
||||
digest.reset();
|
||||
digest.update(input, offset, length);
|
||||
return digest.digest();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates SHA256(SHA256(byte range 1 + byte range 2)).
|
||||
*/
|
||||
public static byte[] doubleDigestTwoBuffers(byte[] input1, int offset1, int length1,
|
||||
byte[] input2, int offset2, int length2) {
|
||||
synchronized (digest) {
|
||||
digest.reset();
|
||||
digest.update(input1, offset1, length1);
|
||||
digest.update(input2, offset2, length2);
|
||||
byte[] first = digest.digest();
|
||||
return digest.digest(first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around lack of unsigned types in Java.
|
||||
*/
|
||||
public static boolean isLessThanUnsigned(long n1, long n2) {
|
||||
return UnsignedLongs.compare(n1, n2) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work around lack of unsigned types in Java.
|
||||
*/
|
||||
public static boolean isLessThanOrEqualToUnsigned(long n1, long n2) {
|
||||
return UnsignedLongs.compare(n1, n2) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex encoding used throughout the framework. Use with HEX.encode(byte[]) or HEX.decode(CharSequence).
|
||||
*/
|
||||
public static final BaseEncoding HEX = BaseEncoding.base16().lowerCase();
|
||||
|
||||
/**
|
||||
* Returns a copy of the given byte array in reverse order.
|
||||
*/
|
||||
public static byte[] reverseBytes(byte[] bytes) {
|
||||
// We could use the XOR trick here but it's easier to understand if we don't. If we find this is really a
|
||||
// performance issue the matter can be revisited.
|
||||
byte[] buf = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; i++)
|
||||
buf[i] = bytes[bytes.length - 1 - i];
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the given byte array with the bytes of each double-word (4 bytes) reversed.
|
||||
*
|
||||
* @param bytes length must be divisible by 4.
|
||||
* @param trimLength trim output to this length. If positive, must be divisible by 4.
|
||||
*/
|
||||
public static byte[] reverseDwordBytes(byte[] bytes, int trimLength) {
|
||||
checkArgument(bytes.length % 4 == 0);
|
||||
checkArgument(trimLength < 0 || trimLength % 4 == 0);
|
||||
|
||||
byte[] rev = new byte[trimLength >= 0 && bytes.length > trimLength ? trimLength : bytes.length];
|
||||
|
||||
for (int i = 0; i < rev.length; i += 4) {
|
||||
System.arraycopy(bytes, i, rev, i , 4);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
rev[i + j] = bytes[i + 3 - j];
|
||||
}
|
||||
}
|
||||
return rev;
|
||||
}
|
||||
|
||||
public static long readUint32(byte[] bytes, int offset) {
|
||||
return ((bytes[offset++] & 0xFFL) << 0) |
|
||||
((bytes[offset++] & 0xFFL) << 8) |
|
||||
((bytes[offset++] & 0xFFL) << 16) |
|
||||
((bytes[offset] & 0xFFL) << 24);
|
||||
}
|
||||
|
||||
public static long readInt64(byte[] bytes, int offset) {
|
||||
return ((bytes[offset++] & 0xFFL) << 0) |
|
||||
((bytes[offset++] & 0xFFL) << 8) |
|
||||
((bytes[offset++] & 0xFFL) << 16) |
|
||||
((bytes[offset++] & 0xFFL) << 24) |
|
||||
((bytes[offset++] & 0xFFL) << 32) |
|
||||
((bytes[offset++] & 0xFFL) << 40) |
|
||||
((bytes[offset++] & 0xFFL) << 48) |
|
||||
((bytes[offset] & 0xFFL) << 56);
|
||||
}
|
||||
|
||||
public static long readUint32BE(byte[] bytes, int offset) {
|
||||
return ((bytes[offset + 0] & 0xFFL) << 24) |
|
||||
((bytes[offset + 1] & 0xFFL) << 16) |
|
||||
((bytes[offset + 2] & 0xFFL) << 8) |
|
||||
((bytes[offset + 3] & 0xFFL) << 0);
|
||||
}
|
||||
|
||||
public static int readUint16BE(byte[] bytes, int offset) {
|
||||
return ((bytes[offset] & 0xff) << 8) | bytes[offset + 1] & 0xff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates RIPEMD160(SHA256(input)). This is used in Address calculations.
|
||||
*/
|
||||
public static byte[] sha256hash160(byte[] input) {
|
||||
try {
|
||||
byte[] sha256 = MessageDigest.getInstance("SHA-256").digest(input);
|
||||
RIPEMD160Digest digest = new RIPEMD160Digest();
|
||||
digest.update(sha256, 0, sha256.length);
|
||||
byte[] out = new byte[20];
|
||||
digest.doFinal(out, 0);
|
||||
return out;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of
|
||||
* a 4 byte big endian length field, followed by the stated number of bytes representing
|
||||
* the number in big endian format (with a sign bit).
|
||||
* @param hasLength can be set to false if the given array is missing the 4 byte length field
|
||||
*/
|
||||
public static BigInteger decodeMPI(byte[] mpi, boolean hasLength) {
|
||||
byte[] buf;
|
||||
if (hasLength) {
|
||||
int length = (int) readUint32BE(mpi, 0);
|
||||
buf = new byte[length];
|
||||
System.arraycopy(mpi, 4, buf, 0, length);
|
||||
} else
|
||||
buf = mpi;
|
||||
if (buf.length == 0)
|
||||
return BigInteger.ZERO;
|
||||
boolean isNegative = (buf[0] & 0x80) == 0x80;
|
||||
if (isNegative)
|
||||
buf[0] &= 0x7f;
|
||||
BigInteger result = new BigInteger(buf);
|
||||
return isNegative ? result.negate() : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of
|
||||
* a 4 byte big endian length field, followed by the stated number of bytes representing
|
||||
* the number in big endian format (with a sign bit).
|
||||
* @param includeLength indicates whether the 4 byte length field should be included
|
||||
*/
|
||||
public static byte[] encodeMPI(BigInteger value, boolean includeLength) {
|
||||
if (value.equals(BigInteger.ZERO)) {
|
||||
if (!includeLength)
|
||||
return new byte[] {};
|
||||
else
|
||||
return new byte[] {0x00, 0x00, 0x00, 0x00};
|
||||
}
|
||||
boolean isNegative = value.signum() < 0;
|
||||
if (isNegative)
|
||||
value = value.negate();
|
||||
byte[] array = value.toByteArray();
|
||||
int length = array.length;
|
||||
if ((array[0] & 0x80) == 0x80)
|
||||
length++;
|
||||
if (includeLength) {
|
||||
byte[] result = new byte[length + 4];
|
||||
System.arraycopy(array, 0, result, length - array.length + 3, array.length);
|
||||
uint32ToByteArrayBE(length, result, 0);
|
||||
if (isNegative)
|
||||
result[4] |= 0x80;
|
||||
return result;
|
||||
} else {
|
||||
byte[] result;
|
||||
if (length != array.length) {
|
||||
result = new byte[length];
|
||||
System.arraycopy(array, 0, result, 1, array.length);
|
||||
}else
|
||||
result = array;
|
||||
if (isNegative)
|
||||
result[0] |= 0x80;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The "compact" format is a representation of a whole number N using an unsigned 32 bit number similar to a
|
||||
* floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can
|
||||
* be thought of as "number of bytes of N". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents
|
||||
* the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).</p>
|
||||
*
|
||||
* <p>Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the
|
||||
* first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact
|
||||
* 0x05c0de00 would be -0x40de000000.</p>
|
||||
*
|
||||
* <p>Bitcoin only uses this "compact" format for encoding difficulty targets, which are unsigned 256bit quantities.
|
||||
* Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.</p>
|
||||
*/
|
||||
public static BigInteger decodeCompactBits(long compact) {
|
||||
int size = ((int) (compact >> 24)) & 0xFF;
|
||||
byte[] bytes = new byte[4 + size];
|
||||
bytes[3] = (byte) size;
|
||||
if (size >= 1) bytes[4] = (byte) ((compact >> 16) & 0xFF);
|
||||
if (size >= 2) bytes[5] = (byte) ((compact >> 8) & 0xFF);
|
||||
if (size >= 3) bytes[6] = (byte) ((compact >> 0) & 0xFF);
|
||||
return decodeMPI(bytes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Utils#decodeCompactBits(long)
|
||||
*/
|
||||
public static long encodeCompactBits(BigInteger value) {
|
||||
long result;
|
||||
int size = value.toByteArray().length;
|
||||
if (size <= 3)
|
||||
result = value.longValue() << 8 * (3 - size);
|
||||
else
|
||||
result = value.shiftRight(8 * (size - 3)).longValue();
|
||||
// The 0x00800000 bit denotes the sign.
|
||||
// Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
|
||||
if ((result & 0x00800000L) != 0) {
|
||||
result >>= 8;
|
||||
size++;
|
||||
}
|
||||
result |= size << 24;
|
||||
result |= value.signum() == -1 ? 0x00800000 : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-null, overrides the return value of now().
|
||||
*/
|
||||
public static volatile Date mockTime;
|
||||
|
||||
/**
|
||||
* Advances (or rewinds) the mock clock by the given number of seconds.
|
||||
*/
|
||||
public static Date rollMockClock(int seconds) {
|
||||
return rollMockClockMillis(seconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances (or rewinds) the mock clock by the given number of milliseconds.
|
||||
*/
|
||||
public static Date rollMockClockMillis(long millis) {
|
||||
if (mockTime == null)
|
||||
throw new IllegalStateException("You need to use setMockClock() first.");
|
||||
mockTime = new Date(mockTime.getTime() + millis);
|
||||
return mockTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock clock to the current time.
|
||||
*/
|
||||
public static void setMockClock() {
|
||||
mockTime = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mock clock to the given time (in seconds).
|
||||
*/
|
||||
public static void setMockClock(long mockClockSeconds) {
|
||||
mockTime = new Date(mockClockSeconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time, or a mocked out equivalent.
|
||||
*/
|
||||
public static Date now() {
|
||||
if (mockTime != null)
|
||||
return mockTime;
|
||||
else
|
||||
return new Date();
|
||||
}
|
||||
|
||||
// TODO: Replace usages of this where the result is / 1000 with currentTimeSeconds.
|
||||
/** Returns the current time in milliseconds since the epoch, or a mocked out equivalent. */
|
||||
public static long currentTimeMillis() {
|
||||
if (mockTime != null)
|
||||
return mockTime.getTime();
|
||||
else
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static long currentTimeSeconds() {
|
||||
return currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
|
||||
/**
|
||||
* Formats a given date+time value to an ISO 8601 string.
|
||||
* @param dateTime value to format, as a Date
|
||||
*/
|
||||
public static String dateTimeFormat(Date dateTime) {
|
||||
DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
iso8601.setTimeZone(UTC);
|
||||
return iso8601.format(dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a given date+time value to an ISO 8601 string.
|
||||
* @param dateTime value to format, unix time (ms)
|
||||
*/
|
||||
public static String dateTimeFormat(long dateTime) {
|
||||
DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
iso8601.setTimeZone(UTC);
|
||||
return iso8601.format(dateTime);
|
||||
}
|
||||
|
||||
public static byte[] copyOf(byte[] in, int length) {
|
||||
byte[] out = new byte[length];
|
||||
System.arraycopy(in, 0, out, 0, Math.min(length, in.length));
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of bytes and appends b to the end of it
|
||||
*/
|
||||
public static byte[] appendByte(byte[] bytes, byte b) {
|
||||
byte[] result = Arrays.copyOf(bytes, bytes.length + 1);
|
||||
result[result.length - 1] = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse the given string as arbitrary-length hex or base58 and then return the results, or null if
|
||||
* neither parse was successful.
|
||||
*/
|
||||
public static byte[] parseAsHexOrBase58(String data) {
|
||||
try {
|
||||
return HEX.decode(data);
|
||||
} catch (Exception e) {
|
||||
// Didn't decode as hex, try base58.
|
||||
try {
|
||||
return Base58.decodeChecked(data);
|
||||
} catch (AddressFormatException e1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWindows() {
|
||||
return System.getProperty("os.name").toLowerCase().contains("win");
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given a textual message, returns a byte buffer formatted as follows:</p>
|
||||
*
|
||||
* <tt><p>[24] "Bitcoin Signed Message:\n" [message.length as a varint] message</p></tt>
|
||||
*/
|
||||
public static byte[] formatMessageForSigning(String message) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bos.write(BITCOIN_SIGNED_MESSAGE_HEADER_BYTES.length);
|
||||
bos.write(BITCOIN_SIGNED_MESSAGE_HEADER_BYTES);
|
||||
byte[] messageBytes = message.getBytes(Charsets.UTF_8);
|
||||
VarInt size = new VarInt(messageBytes.length);
|
||||
bos.write(size.encode());
|
||||
bos.write(messageBytes);
|
||||
return bos.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
// 00000001, 00000010, 00000100, 00001000, ...
|
||||
private static final int bitMask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||
|
||||
/** Checks if the given bit is set in data, using little endian (not the same as Java native big endian) */
|
||||
public static boolean checkBitLE(byte[] data, int index) {
|
||||
return (data[index >>> 3] & bitMask[7 & index]) != 0;
|
||||
}
|
||||
|
||||
/** Sets the given bit in data to one, using little endian (not the same as Java native big endian) */
|
||||
public static void setBitLE(byte[] data, int index) {
|
||||
data[index >>> 3] |= bitMask[7 & index];
|
||||
}
|
||||
|
||||
/** Sleep for a span of time, or mock sleep if enabled */
|
||||
public static void sleep(long millis) {
|
||||
if (mockSleepQueue == null) {
|
||||
sleepUninterruptibly(millis, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
try {
|
||||
boolean isMultiPass = mockSleepQueue.take();
|
||||
rollMockClockMillis(millis);
|
||||
if (isMultiPass)
|
||||
mockSleepQueue.offer(true);
|
||||
} catch (InterruptedException e) {
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Enable or disable mock sleep. If enabled, set mock time to current time. */
|
||||
public static void setMockSleep(boolean isEnable) {
|
||||
if (isEnable) {
|
||||
mockSleepQueue = new ArrayBlockingQueue<Boolean>(1);
|
||||
mockTime = new Date(System.currentTimeMillis());
|
||||
} else {
|
||||
mockSleepQueue = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Let sleeping thread pass the synchronization point. */
|
||||
public static void passMockSleep() {
|
||||
mockSleepQueue.offer(false);
|
||||
}
|
||||
|
||||
/** Let the sleeping thread pass the synchronization point any number of times. */
|
||||
public static void finishMockSleep() {
|
||||
if (mockSleepQueue != null) {
|
||||
mockSleepQueue.offer(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAndroidRuntime() {
|
||||
final String runtime = System.getProperty("java.runtime.name");
|
||||
return runtime != null && runtime.equals("Android Runtime");
|
||||
}
|
||||
|
||||
private static class Pair implements Comparable<Pair> {
|
||||
int item, count;
|
||||
public Pair(int item, int count) { this.count = count; this.item = item; }
|
||||
@Override public int compareTo(Pair o) { return -Ints.compare(count, o.count); }
|
||||
}
|
||||
|
||||
public static int maxOfMostFreq(int... items) {
|
||||
// Java 6 sucks.
|
||||
ArrayList<Integer> list = new ArrayList<Integer>(items.length);
|
||||
for (int item : items) list.add(item);
|
||||
return maxOfMostFreq(list);
|
||||
}
|
||||
|
||||
public static int maxOfMostFreq(List<Integer> items) {
|
||||
if (items.isEmpty())
|
||||
return 0;
|
||||
// This would be much easier in a functional language (or in Java 8).
|
||||
items = Ordering.natural().reverse().sortedCopy(items);
|
||||
LinkedList<Pair> pairs = Lists.newLinkedList();
|
||||
pairs.add(new Pair(items.get(0), 0));
|
||||
for (int item : items) {
|
||||
Pair pair = pairs.getLast();
|
||||
if (pair.item != item)
|
||||
pairs.add((pair = new Pair(item, 0)));
|
||||
pair.count++;
|
||||
}
|
||||
// pairs now contains a uniqified list of the sorted inputs, with counts for how often that item appeared.
|
||||
// Now sort by how frequently they occur, and pick the max of the most frequent.
|
||||
Collections.sort(pairs);
|
||||
int maxCount = pairs.getFirst().count;
|
||||
int maxItem = pairs.getFirst().item;
|
||||
for (Pair pair : pairs) {
|
||||
if (pair.count != maxCount)
|
||||
break;
|
||||
maxItem = Math.max(maxItem, pair.item);
|
||||
}
|
||||
return maxItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and joins together with LF char (\n) all the lines from given file. It's assumed that file is in UTF-8.
|
||||
*/
|
||||
public static String getResourceAsString(URL url) throws IOException {
|
||||
List<String> lines = Resources.readLines(url, Charsets.UTF_8);
|
||||
return Joiner.on('\n').join(lines);
|
||||
}
|
||||
|
||||
// Can't use Closeable here because it's Java 7 only and Android devices only got that with KitKat.
|
||||
public static InputStream closeUnchecked(InputStream stream) {
|
||||
try {
|
||||
stream.close();
|
||||
return stream;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static OutputStream closeUnchecked(OutputStream stream) {
|
||||
try {
|
||||
stream.close();
|
||||
return stream;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.isLessThanUnsigned;
|
||||
import static com.dogecoin.dogecoinj.core.Utils.isLessThanOrEqualToUnsigned;
|
||||
|
||||
/**
|
||||
* A variable-length encoded integer using Satoshis encoding.
|
||||
*/
|
||||
public class VarInt {
|
||||
public final long value;
|
||||
private final int originallyEncodedSize;
|
||||
|
||||
public VarInt(long value) {
|
||||
this.value = value;
|
||||
originallyEncodedSize = getSizeInBytes();
|
||||
}
|
||||
|
||||
// Bitcoin has its own varint format, known in the C++ source as "compact size".
|
||||
public VarInt(byte[] buf, int offset) {
|
||||
int first = 0xFF & buf[offset];
|
||||
if (first < 253) {
|
||||
// 8 bits.
|
||||
this.value = first;
|
||||
originallyEncodedSize = 1;
|
||||
} else if (first == 253) {
|
||||
// 16 bits.
|
||||
this.value = (0xFF & buf[offset + 1]) | ((0xFF & buf[offset + 2]) << 8);
|
||||
originallyEncodedSize = 3;
|
||||
} else if (first == 254) {
|
||||
// 32 bits.
|
||||
this.value = Utils.readUint32(buf, offset + 1);
|
||||
originallyEncodedSize = 5;
|
||||
} else {
|
||||
// 64 bits.
|
||||
this.value = Utils.readUint32(buf, offset + 1) | (Utils.readUint32(buf, offset + 5) << 32);
|
||||
originallyEncodedSize = 9;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes used to encode this originally if deserialized from a byte array.
|
||||
* Otherwise returns the minimum encoded size
|
||||
*/
|
||||
public int getOriginalSizeInBytes() {
|
||||
return originallyEncodedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum encoded size of the value stored in this VarInt
|
||||
*/
|
||||
public int getSizeInBytes() {
|
||||
return sizeOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum encoded size of the given value.
|
||||
*/
|
||||
public static int sizeOf(int value) {
|
||||
if (value < 253)
|
||||
return 1;
|
||||
else if (value < 65536)
|
||||
return 3; // 1 marker + 2 data bytes
|
||||
return 5; // 1 marker + 4 data bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum encoded size of the given value.
|
||||
*/
|
||||
public static int sizeOf(long value) {
|
||||
if (isLessThanUnsigned(value, 253))
|
||||
return 1;
|
||||
else if (isLessThanOrEqualToUnsigned(value, 0xFFFFL))
|
||||
return 3; // 1 marker + 2 data bytes
|
||||
else if (isLessThanOrEqualToUnsigned(value, 0xFFFFFFFFL))
|
||||
return 5; // 1 marker + 4 data bytes
|
||||
else
|
||||
return 9; // 1 marker + 8 data bytes
|
||||
}
|
||||
|
||||
public byte[] encode() {
|
||||
if (isLessThanUnsigned(value, 253)) {
|
||||
return new byte[]{(byte) value};
|
||||
} else if (isLessThanOrEqualToUnsigned(value, 0xFFFFL)) {
|
||||
return new byte[]{(byte) 253, (byte) (value), (byte) (value >> 8)};
|
||||
} else if (isLessThanOrEqualToUnsigned(value, 0xFFFFFFFFL)) {
|
||||
byte[] bytes = new byte[5];
|
||||
bytes[0] = (byte) 254;
|
||||
Utils.uint32ToByteArrayLE(value, bytes, 1);
|
||||
return bytes;
|
||||
} else {
|
||||
byte[] bytes = new byte[9];
|
||||
bytes[0] = (byte) 255;
|
||||
Utils.uint32ToByteArrayLE(value, bytes, 1);
|
||||
Utils.uint32ToByteArrayLE(value >>> 32, bytes, 5);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class VerificationException extends RuntimeException {
|
||||
public VerificationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public VerificationException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public VerificationException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
|
||||
public static class EmptyInputsOrOutputs extends VerificationException {
|
||||
public EmptyInputsOrOutputs() {
|
||||
super("Transaction had no inputs or no outputs.");
|
||||
}
|
||||
}
|
||||
|
||||
public static class LargerThanMaxBlockSize extends VerificationException {
|
||||
public LargerThanMaxBlockSize() {
|
||||
super("Transaction larger than MAX_BLOCK_SIZE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class DuplicatedOutPoint extends VerificationException {
|
||||
public DuplicatedOutPoint() {
|
||||
super("Duplicated outpoint");
|
||||
}
|
||||
}
|
||||
|
||||
public static class NegativeValueOutput extends VerificationException {
|
||||
public NegativeValueOutput() {
|
||||
super("Transaction output negative");
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExcessiveValue extends VerificationException {
|
||||
public ExcessiveValue() {
|
||||
super("Total transaction output value greater than possible");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class CoinbaseScriptSizeOutOfRange extends VerificationException {
|
||||
public CoinbaseScriptSizeOutOfRange() {
|
||||
super("Coinbase script size out of range");
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnexpectedCoinbaseInput extends VerificationException {
|
||||
public UnexpectedCoinbaseInput() {
|
||||
super("Coinbase input as input in non-coinbase transaction");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Noa Resare.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* The verack message, sent by a client accepting the version message they
|
||||
* received from their peer.
|
||||
*/
|
||||
public class VersionAck extends EmptyMessage {
|
||||
public VersionAck() {
|
||||
}
|
||||
|
||||
// this is needed by the BitcoinSerializer
|
||||
public VersionAck(NetworkParameters params, byte[] payload) {
|
||||
}
|
||||
}
|
@ -1,318 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* A VersionMessage holds information exchanged during connection setup with another peer. Most of the fields are not
|
||||
* particularly interesting. The subVer field, since BIP 14, acts as a User-Agent string would. You can and should
|
||||
* append to or change the subVer for your own software so other implementations can identify it, and you can look at
|
||||
* the subVer field received from other nodes to see what they are running. <p>
|
||||
*
|
||||
* After creating yourself a VersionMessage, you can pass it to {@link PeerGroup#setVersionMessage(VersionMessage)}
|
||||
* to ensure it will be used for each new connection.
|
||||
*/
|
||||
public class VersionMessage extends Message {
|
||||
private static final long serialVersionUID = 7313594258967483180L;
|
||||
|
||||
/** A services flag that denotes whether the peer has a copy of the block chain or not. */
|
||||
public static final int NODE_NETWORK = 1;
|
||||
/** A flag that denotes whether the peer supports the getutxos message or not. */
|
||||
public static final int NODE_GETUTXOS = 2;
|
||||
|
||||
/**
|
||||
* The version number of the protocol spoken.
|
||||
*/
|
||||
public int clientVersion;
|
||||
/**
|
||||
* Flags defining what optional services are supported.
|
||||
*/
|
||||
public long localServices;
|
||||
/**
|
||||
* What the other side believes the current time to be, in seconds.
|
||||
*/
|
||||
public long time;
|
||||
/**
|
||||
* What the other side believes the address of this program is. Not used.
|
||||
*/
|
||||
public PeerAddress myAddr;
|
||||
/**
|
||||
* What the other side believes their own address is. Not used.
|
||||
*/
|
||||
public PeerAddress theirAddr;
|
||||
/**
|
||||
* User-Agent as defined in <a href="https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki">BIP 14</a>.
|
||||
* The official client sets it to something like "/Satoshi:0.9.1/".
|
||||
*/
|
||||
public String subVer;
|
||||
/**
|
||||
* How many blocks are in the chain, according to the other side.
|
||||
*/
|
||||
public long bestHeight;
|
||||
/**
|
||||
* Whether or not to relay tx invs before a filter is received.
|
||||
* See <a href="https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#extensions-to-existing-messages">BIP 37</a>.
|
||||
*/
|
||||
public boolean relayTxesBeforeFilter;
|
||||
|
||||
/** The version of this library release, as a string. */
|
||||
public static final String BITCOINJ_VERSION = "0.13-SNAPSHOT";
|
||||
/** The value that is prepended to the subVer field of this application. */
|
||||
public static final String LIBRARY_SUBVER = "/dogecoinj:" + BITCOINJ_VERSION + "/";
|
||||
|
||||
public VersionMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0);
|
||||
}
|
||||
|
||||
// It doesn't really make sense to ever lazily parse a version message or to retain the backing bytes.
|
||||
// If you're receiving this on the wire you need to check the protocol version and it will never need to be sent
|
||||
// back down the wire.
|
||||
|
||||
public VersionMessage(NetworkParameters params, int newBestHeight) {
|
||||
super(params);
|
||||
clientVersion = NetworkParameters.PROTOCOL_VERSION;
|
||||
localServices = 0;
|
||||
time = System.currentTimeMillis() / 1000;
|
||||
// Note that the official client doesn't do anything with these, and finding out your own external IP address
|
||||
// is kind of tricky anyway, so we just put nonsense here for now.
|
||||
try {
|
||||
// We hard-code the IPv4 localhost address here rather than use InetAddress.getLocalHost() because some
|
||||
// mobile phones have broken localhost DNS entries, also, this is faster.
|
||||
final byte[] localhost = { 127, 0, 0, 1 };
|
||||
myAddr = new PeerAddress(InetAddress.getByAddress(localhost), params.getPort(), 0);
|
||||
theirAddr = new PeerAddress(InetAddress.getByAddress(localhost), params.getPort(), 0);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Cannot happen (illegal IP length).
|
||||
}
|
||||
subVer = LIBRARY_SUBVER;
|
||||
bestHeight = newBestHeight;
|
||||
relayTxesBeforeFilter = true;
|
||||
|
||||
length = 85;
|
||||
if (protocolVersion > 31402)
|
||||
length += 8;
|
||||
length += VarInt.sizeOf(subVer.length()) + subVer.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// NOP. VersionMessage is never lazy parsed.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse() throws ProtocolException {
|
||||
if (parsed)
|
||||
return;
|
||||
parsed = true;
|
||||
|
||||
clientVersion = (int) readUint32();
|
||||
localServices = readUint64().longValue();
|
||||
time = readUint64().longValue();
|
||||
myAddr = new PeerAddress(params, payload, cursor, 0);
|
||||
cursor += myAddr.getMessageSize();
|
||||
theirAddr = new PeerAddress(params, payload, cursor, 0);
|
||||
cursor += theirAddr.getMessageSize();
|
||||
// uint64 localHostNonce (random data)
|
||||
// We don't care about the localhost nonce. It's used to detect connecting back to yourself in cases where
|
||||
// there are NATs and proxies in the way. However we don't listen for inbound connections so it's irrelevant.
|
||||
readUint64();
|
||||
try {
|
||||
// Initialize default values for flags which may not be sent by old nodes
|
||||
subVer = "";
|
||||
bestHeight = 0;
|
||||
relayTxesBeforeFilter = true;
|
||||
if (!hasMoreBytes())
|
||||
return;
|
||||
// string subVer (currently "")
|
||||
subVer = readStr();
|
||||
if (!hasMoreBytes())
|
||||
return;
|
||||
// int bestHeight (size of known block chain).
|
||||
bestHeight = readUint32();
|
||||
if (!hasMoreBytes())
|
||||
return;
|
||||
relayTxesBeforeFilter = readBytes(1)[0] != 0;
|
||||
} finally {
|
||||
length = cursor - offset;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bitcoinSerializeToStream(OutputStream buf) throws IOException {
|
||||
Utils.uint32ToByteStreamLE(clientVersion, buf);
|
||||
Utils.uint32ToByteStreamLE(localServices, buf);
|
||||
Utils.uint32ToByteStreamLE(localServices >> 32, buf);
|
||||
Utils.uint32ToByteStreamLE(time, buf);
|
||||
Utils.uint32ToByteStreamLE(time >> 32, buf);
|
||||
try {
|
||||
// My address.
|
||||
myAddr.bitcoinSerialize(buf);
|
||||
// Their address.
|
||||
theirAddr.bitcoinSerialize(buf);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e); // Can't happen.
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Can't happen.
|
||||
}
|
||||
// Next up is the "local host nonce", this is to detect the case of connecting
|
||||
// back to yourself. We don't care about this as we won't be accepting inbound
|
||||
// connections.
|
||||
Utils.uint32ToByteStreamLE(0, buf);
|
||||
Utils.uint32ToByteStreamLE(0, buf);
|
||||
// Now comes subVer.
|
||||
byte[] subVerBytes = subVer.getBytes("UTF-8");
|
||||
buf.write(new VarInt(subVerBytes.length).encode());
|
||||
buf.write(subVerBytes);
|
||||
// Size of known block chain.
|
||||
Utils.uint32ToByteStreamLE(bestHeight, buf);
|
||||
buf.write(relayTxesBeforeFilter ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the version message indicates the sender has a full copy of the block chain,
|
||||
* or if it's running in client mode (only has the headers).
|
||||
*/
|
||||
public boolean hasBlockChain() {
|
||||
return (localServices & NODE_NETWORK) == NODE_NETWORK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VersionMessage other = (VersionMessage) o;
|
||||
return other.bestHeight == bestHeight &&
|
||||
other.clientVersion == clientVersion &&
|
||||
other.localServices == localServices &&
|
||||
other.time == time &&
|
||||
other.subVer.equals(subVer) &&
|
||||
other.myAddr.equals(myAddr) &&
|
||||
other.theirAddr.equals(theirAddr) &&
|
||||
other.relayTxesBeforeFilter == relayTxesBeforeFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) bestHeight ^ clientVersion ^ (int) localServices ^ (int) time ^ subVer.hashCode() ^ myAddr.hashCode()
|
||||
^ theirAddr.hashCode() * (relayTxesBeforeFilter ? 1 : 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* VersionMessage does not handle cached byte array so should not have a cached checksum.
|
||||
*/
|
||||
@Override
|
||||
byte[] getChecksum() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* VersionMessage does not handle cached byte array so should not have a cached checksum.
|
||||
*/
|
||||
@Override
|
||||
void setChecksum(byte[] checksum) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n");
|
||||
sb.append("client version: ").append(clientVersion).append("\n");
|
||||
sb.append("local services: ").append(localServices).append("\n");
|
||||
sb.append("time: ").append(time).append("\n");
|
||||
sb.append("my addr: ").append(myAddr).append("\n");
|
||||
sb.append("their addr: ").append(theirAddr).append("\n");
|
||||
sb.append("sub version: ").append(subVer).append("\n");
|
||||
sb.append("best height: ").append(bestHeight).append("\n");
|
||||
sb.append("delay tx relay: ").append(!relayTxesBeforeFilter).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public VersionMessage duplicate() {
|
||||
VersionMessage v = new VersionMessage(params, (int) bestHeight);
|
||||
v.clientVersion = clientVersion;
|
||||
v.localServices = localServices;
|
||||
v.time = time;
|
||||
v.myAddr = myAddr;
|
||||
v.theirAddr = theirAddr;
|
||||
v.subVer = subVer;
|
||||
v.relayTxesBeforeFilter = relayTxesBeforeFilter;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given user-agent information to the subVer field. The subVer is composed of a series of
|
||||
* name:version pairs separated by slashes in the form of a path. For example a typical subVer field for BitCoinJ
|
||||
* users might look like "/BitCoinJ:0.4-SNAPSHOT/MultiBit:1.2/" where libraries come further to the left.<p>
|
||||
*
|
||||
* There can be as many components as you feel a need for, and the version string can be anything, but it is
|
||||
* recommended to use A.B.C where A = major, B = minor and C = revision for software releases, and dates for
|
||||
* auto-generated source repository snapshots. A valid subVer begins and ends with a slash, therefore name
|
||||
* and version are not allowed to contain such characters. <p>
|
||||
*
|
||||
* Anything put in the "comments" field will appear in brackets and may be used for platform info, or anything
|
||||
* else. For example, calling <tt>appendToSubVer("MultiBit", "1.0", "Windows")</tt> will result in a subVer being
|
||||
* set of "/BitCoinJ:1.0/MultiBit:1.0(Windows)/". Therefore the / ( and ) characters are reserved in all these
|
||||
* components. If you don't want to add a comment (recommended), pass null.<p>
|
||||
*
|
||||
* See <a href="https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki">BIP 14</a> for more information.
|
||||
*
|
||||
* @param comments Optional (can be null) platform or other node specific information.
|
||||
* @throws IllegalArgumentException if name, version or comments contains invalid characters.
|
||||
*/
|
||||
public void appendToSubVer(String name, String version, @Nullable String comments) {
|
||||
checkSubVerComponent(name);
|
||||
checkSubVerComponent(version);
|
||||
if (comments != null) {
|
||||
checkSubVerComponent(comments);
|
||||
subVer = subVer.concat(String.format("%s:%s(%s)/", name, version, comments));
|
||||
} else {
|
||||
subVer = subVer.concat(String.format("%s:%s/", name, version));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkSubVerComponent(String component) {
|
||||
if (component.contains("/") || component.contains("(") || component.contains(")"))
|
||||
throw new IllegalArgumentException("name contains invalid characters");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the clientVersion field is >= Pong.MIN_PROTOCOL_VERSION. If it is then ping() is usable.
|
||||
*/
|
||||
public boolean isPingPongSupported() {
|
||||
return clientVersion >= Pong.MIN_PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the clientVersion field is >= FilteredBlock.MIN_PROTOCOL_VERSION. If it is then Bloom filtering
|
||||
* is available and the memory pool of the remote peer will be queried when the downloadData property is true.
|
||||
*/
|
||||
public boolean isBloomFilteringSupported() {
|
||||
return clientVersion >= FilteredBlock.MIN_PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
/** Returns true if the protocol version and service bits both indicate support for the getutxos message. */
|
||||
public boolean isGetUTXOsSupported() {
|
||||
return clientVersion >= GetUTXOsMessage.MIN_PROTOCOL_VERSION &&
|
||||
(localServices & NODE_GETUTXOS) == NODE_GETUTXOS;
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/**
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.UnsignedBytes;
|
||||
|
||||
/**
|
||||
* <p>In Bitcoin the following format is often used to represent some type of key:</p>
|
||||
* <p/>
|
||||
* <pre>[one version byte] [data bytes] [4 checksum bytes]</pre>
|
||||
* <p/>
|
||||
* <p>and the result is then Base58 encoded. This format is used for addresses, and private keys exported using the
|
||||
* dumpprivkey command.</p>
|
||||
*/
|
||||
public class VersionedChecksummedBytes implements Serializable, Cloneable, Comparable<VersionedChecksummedBytes> {
|
||||
protected final int version;
|
||||
protected byte[] bytes;
|
||||
|
||||
protected VersionedChecksummedBytes(String encoded) throws AddressFormatException {
|
||||
byte[] versionAndDataBytes = Base58.decodeChecked(encoded);
|
||||
byte versionByte = versionAndDataBytes[0];
|
||||
version = versionByte & 0xFF;
|
||||
bytes = new byte[versionAndDataBytes.length - 1];
|
||||
System.arraycopy(versionAndDataBytes, 1, bytes, 0, versionAndDataBytes.length - 1);
|
||||
}
|
||||
|
||||
protected VersionedChecksummedBytes(int version, byte[] bytes) {
|
||||
checkArgument(version >= 0 && version < 256);
|
||||
this.version = version;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base-58 encoded String representation of this
|
||||
* object, including version and checksum bytes.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// A stringified buffer is:
|
||||
// 1 byte version + data bytes + 4 bytes check code (a truncated hash)
|
||||
byte[] addressBytes = new byte[1 + bytes.length + 4];
|
||||
addressBytes[0] = (byte) version;
|
||||
System.arraycopy(bytes, 0, addressBytes, 1, bytes.length);
|
||||
byte[] checksum = Utils.doubleDigest(addressBytes, 0, bytes.length + 1);
|
||||
System.arraycopy(checksum, 0, addressBytes, bytes.length + 1, 4);
|
||||
return Base58.encode(addressBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(version, Arrays.hashCode(bytes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VersionedChecksummedBytes other = (VersionedChecksummedBytes) o;
|
||||
return this.version == other.version
|
||||
&& Arrays.equals(this.bytes, other.bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation narrows the return type to <code>VersionedChecksummedBytes</code>
|
||||
* and allows subclasses to throw <code>CloneNotSupportedException</code> even though it
|
||||
* is never thrown by this implementation.
|
||||
*/
|
||||
@Override
|
||||
public VersionedChecksummedBytes clone() throws CloneNotSupportedException {
|
||||
return (VersionedChecksummedBytes) super.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation uses an optimized Google Guava method to compare <code>bytes</code>.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(VersionedChecksummedBytes o) {
|
||||
int versionCompare = Integer.valueOf(this.version).compareTo(Integer.valueOf(o.version)); // JDK 6 way
|
||||
if (versionCompare == 0) {
|
||||
// Would there be a performance benefit to caching the comparator?
|
||||
return UnsignedBytes.lexicographicalComparator().compare(this.bytes, o.bytes);
|
||||
} else {
|
||||
return versionCompare;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "version" or "header" byte: the first byte of the data. This is used to disambiguate what the
|
||||
* contents apply to, for example, which network the key or address is valid on.
|
||||
*
|
||||
* @return A positive number between 0 and 255.
|
||||
*/
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,126 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.script.Script;
|
||||
import com.dogecoin.dogecoinj.wallet.KeyChainEventListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>Implementors are called when the contents of the wallet changes, for instance due to receiving/sending money
|
||||
* or a block chain re-organize. It may be convenient to derive from {@link AbstractWalletEventListener} instead.</p>
|
||||
*/
|
||||
public interface WalletEventListener extends KeyChainEventListener {
|
||||
/**
|
||||
* This is called when a transaction is seen that sends coins <b>to</b> this wallet, either because it
|
||||
* was broadcast across the network or because a block was received. If a transaction is seen when it was broadcast,
|
||||
* onCoinsReceived won't be called again when a block containing it is received. If you want to know when such a
|
||||
* transaction receives its first confirmation, register a {@link TransactionConfidence} event listener using
|
||||
* the object retrieved via {@link com.dogecoin.dogecoinj.core.Transaction#getConfidence()}. It's safe to modify the
|
||||
* wallet in this callback, for example, by spending the transaction just received.
|
||||
*
|
||||
* @param wallet The wallet object that received the coins
|
||||
* @param tx The transaction which sent us the coins.
|
||||
* @param prevBalance Balance before the coins were received.
|
||||
* @param newBalance Current balance of the wallet. This is the 'estimated' balance.
|
||||
*/
|
||||
void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance);
|
||||
|
||||
/**
|
||||
* This is called when a transaction is seen that sends coins <b>from</b> this wallet, either
|
||||
* because it was broadcast across the network or because a block was received. This may at first glance seem
|
||||
* useless, because in the common case you already know about such transactions because you created them with
|
||||
* the Wallets createSend/sendCoins methods. However when you have a wallet containing only keys, and you wish
|
||||
* to replay the block chain to fill it with transactions, it's useful to find out when a transaction is discovered
|
||||
* that sends coins from the wallet.<p>
|
||||
*
|
||||
* It's safe to modify the wallet from inside this callback, but if you're replaying the block chain you should
|
||||
* be careful to avoid such modifications. Otherwise your changes may be overridden by new data from the chain.
|
||||
*
|
||||
* @param wallet The wallet object that this callback relates to (that sent the coins).
|
||||
* @param tx The transaction that sent the coins to someone else.
|
||||
* @param prevBalance The wallets balance before this transaction was seen.
|
||||
* @param newBalance The wallets balance after this transaction was seen. This is the 'estimated' balance.
|
||||
*/
|
||||
void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance);
|
||||
|
||||
// TODO: Finish onReorganize to be more useful.
|
||||
/**
|
||||
* <p>This is called when a block is received that triggers a block chain re-organization.</p>
|
||||
*
|
||||
* <p>A re-organize means that the consensus (chain) of the network has diverged and now changed from what we
|
||||
* believed it was previously. Usually this won't matter because the new consensus will include all our old
|
||||
* transactions assuming we are playing by the rules. However it's theoretically possible for our balance to
|
||||
* change in arbitrary ways, most likely, we could lose some money we thought we had.</p>
|
||||
*
|
||||
* <p>It is safe to use methods of wallet whilst inside this callback.</p>
|
||||
*/
|
||||
void onReorganize(Wallet wallet);
|
||||
|
||||
/**
|
||||
* <p>Called when a transaction changes its confidence level. You can also attach event listeners to
|
||||
* the individual transactions, if you don't care about all of them. Usually you would save the wallet to disk after
|
||||
* receiving this callback unless you already set up autosaving.</p>
|
||||
*
|
||||
* <p>You should pay attention to this callback in case a transaction becomes <i>dead</i>, that is, a transaction
|
||||
* you believed to be active (send or receive) becomes overridden by the network. This can happen if</p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>You are sharing keys between wallets and accidentally create/broadcast a double spend.</li>
|
||||
* <li>Somebody is attacking the network and reversing transactions, ie, the user is a victim of fraud.</li>
|
||||
* <li>A bug: for example you create a transaction, broadcast it but fail to commit it. The {@link Wallet}
|
||||
* will then re-use the same outputs when creating the next spend.</li>
|
||||
* </ol><p>
|
||||
*
|
||||
* <p>To find if the transaction is dead, you can use <tt>tx.getConfidence().getConfidenceType() ==
|
||||
* TransactionConfidence.ConfidenceType.DEAD</tt>. If it is, you should notify the user
|
||||
* in some way so they know the thing they bought may not arrive/the thing they sold should not be dispatched.</p>
|
||||
*
|
||||
* <p>Note that this callback will be invoked for every transaction in the wallet, for every new block that is
|
||||
* received (because the depth has changed). <b>If you want to update a UI view from the contents of the wallet
|
||||
* it is more efficient to use onWalletChanged instead.</b></p>
|
||||
*/
|
||||
void onTransactionConfidenceChanged(Wallet wallet, Transaction tx);
|
||||
|
||||
/**
|
||||
* <p>Designed for GUI applications to refresh their transaction lists. This callback is invoked in the following
|
||||
* situations:</p>
|
||||
*
|
||||
* <ol>
|
||||
* <li>A new block is received (and thus building transactions got more confidence)</li>
|
||||
* <li>A pending transaction is received</li>
|
||||
* <li>A pending transaction changes confidence due to some non-new-block related event, such as being
|
||||
* announced by more peers or by a double-spend conflict being observed.</li>
|
||||
* <li>A re-organize occurs. Call occurs only if the re-org modified any of our transactions.</li>
|
||||
* <li>A new spend is committed to the wallet.</li>
|
||||
* <li>The wallet is reset and all transactions removed.<li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>When this is called you can refresh the UI contents from the wallet contents. It's more efficient to use
|
||||
* this rather than onTransactionConfidenceChanged() + onReorganize() because you only get one callback per block
|
||||
* rather than one per transaction per block. Note that this is <b>not</b> called when a key is added. </p>
|
||||
*/
|
||||
void onWalletChanged(Wallet wallet);
|
||||
|
||||
/**
|
||||
* Called whenever a new watched script is added to the wallet.
|
||||
*
|
||||
* @param isAddingScripts will be true if added scripts, false if removed scripts.
|
||||
*/
|
||||
void onScriptsChanged(Wallet wallet, List<Script> scripts, boolean isAddingScripts);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
/**
|
||||
* <p>An object implementing this interface can be added to a {@link Wallet} and provide arbitrary byte arrays that will
|
||||
* be serialized alongside the wallet. Extensions can be mandatory, in which case applications that don't know how to
|
||||
* read the given data will refuse to load the wallet at all. Extensions identify themselves with a string ID that
|
||||
* should use a Java-style reverse DNS identifier to avoid being mixed up with other kinds of extension. To use an
|
||||
* extension, add an object that implements this interface to the wallet using {@link Wallet#addExtension(WalletExtension)}
|
||||
* before you load it (to read existing data) and ensure it's present when the wallet is save (to write the data).</p>
|
||||
*
|
||||
* <p>Note that extensions are singletons - you cannot add two objects that provide the same ID to the same wallet.</p>
|
||||
*/
|
||||
public interface WalletExtension {
|
||||
/** Returns a Java package/class style name used to disambiguate this extension from others. */
|
||||
public String getWalletExtensionID();
|
||||
|
||||
/**
|
||||
* If this returns true, the mandatory flag is set when the wallet is serialized and attempts to load it without
|
||||
* the extension being in the wallet will throw an exception. This method should not change its result during
|
||||
* the objects lifetime.
|
||||
*/
|
||||
public boolean isWalletExtensionMandatory();
|
||||
|
||||
/** Returns bytes that will be saved in the wallet. */
|
||||
public byte[] serializeWalletExtension();
|
||||
/** Loads the contents of this object from the wallet. */
|
||||
public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception;
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This exception is thrown by the Address class when you try and decode an address with a version code that isn't
|
||||
* used by that network. You shouldn't allow the user to proceed in this case as they are trying to send money across
|
||||
* different chains, an operation that is guaranteed to destroy the money.
|
||||
*/
|
||||
public class WrongNetworkException extends AddressFormatException {
|
||||
/** The version code that was provided in the address. */
|
||||
public int verCode;
|
||||
/** The list of acceptable versions that were expected given the addresses network parameters. */
|
||||
public int[] acceptableVersions;
|
||||
|
||||
public WrongNetworkException(int verCode, int[] acceptableVersions) {
|
||||
super("Version code of address did not match acceptable versions for network: " + verCode + " not in " +
|
||||
Arrays.toString(acceptableVersions));
|
||||
this.verCode = verCode;
|
||||
this.acceptableVersions = acceptableVersions;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
/**
|
||||
* The core package contains classes for network messages like {@link com.dogecoin.dogecoinj.core.Block} and
|
||||
* {@link com.dogecoin.dogecoinj.core.Transaction}, peer connectivity via {@link com.dogecoin.dogecoinj.core.PeerGroup},
|
||||
* block chain management and the {@link com.dogecoin.dogecoinj.core.Wallet} class.
|
||||
* If what you're doing can be described as basic bitcoin tasks, the code is probably found here.
|
||||
* To learn more please consult the documentation on the website.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.core;
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.Normalizer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* Implementation of <a href="https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki">BIP 38</a>
|
||||
* passphrase-protected private keys. Currently, only decryption is supported.
|
||||
*/
|
||||
public class BIP38PrivateKey extends VersionedChecksummedBytes {
|
||||
|
||||
public final NetworkParameters params;
|
||||
public final boolean ecMultiply;
|
||||
public final boolean compressed;
|
||||
public final boolean hasLotAndSequence;
|
||||
public final byte[] addressHash;
|
||||
public final byte[] content;
|
||||
|
||||
public static final class BadPassphraseException extends Exception {
|
||||
}
|
||||
|
||||
public BIP38PrivateKey(NetworkParameters params, String encoded) throws AddressFormatException {
|
||||
super(encoded);
|
||||
this.params = params;
|
||||
if (version != 0x01)
|
||||
throw new AddressFormatException("Mismatched version number: " + version);
|
||||
if (bytes.length != 38)
|
||||
throw new AddressFormatException("Wrong number of bytes, excluding version byte: " + bytes.length);
|
||||
hasLotAndSequence = (bytes[1] & 0x04) != 0; // bit 2
|
||||
compressed = (bytes[1] & 0x20) != 0; // bit 5
|
||||
if ((bytes[1] & 0x01) != 0) // bit 0
|
||||
throw new AddressFormatException("Bit 0x01 reserved for future use.");
|
||||
if ((bytes[1] & 0x02) != 0) // bit 1
|
||||
throw new AddressFormatException("Bit 0x02 reserved for future use.");
|
||||
if ((bytes[1] & 0x08) != 0) // bit 3
|
||||
throw new AddressFormatException("Bit 0x08 reserved for future use.");
|
||||
if ((bytes[1] & 0x10) != 0) // bit 4
|
||||
throw new AddressFormatException("Bit 0x10 reserved for future use.");
|
||||
final int byte0 = bytes[0] & 0xff;
|
||||
if (byte0 == 0x42) {
|
||||
// Non-EC-multiplied key
|
||||
if ((bytes[1] & 0xc0) != 0xc0) // bits 6+7
|
||||
throw new AddressFormatException("Bits 0x40 and 0x80 must be set for non-EC-multiplied keys.");
|
||||
ecMultiply = false;
|
||||
if (hasLotAndSequence)
|
||||
throw new AddressFormatException("Non-EC-multiplied keys cannot have lot/sequence.");
|
||||
} else if (byte0 == 0x43) {
|
||||
// EC-multiplied key
|
||||
if ((bytes[1] & 0xc0) != 0x00) // bits 6+7
|
||||
throw new AddressFormatException("Bits 0x40 and 0x80 must be cleared for EC-multiplied keys.");
|
||||
ecMultiply = true;
|
||||
} else {
|
||||
throw new AddressFormatException("Second byte must by 0x42 or 0x43.");
|
||||
}
|
||||
addressHash = Arrays.copyOfRange(bytes, 2, 6);
|
||||
content = Arrays.copyOfRange(bytes, 6, 38);
|
||||
}
|
||||
|
||||
public ECKey decrypt(String passphrase) throws BadPassphraseException {
|
||||
String normalizedPassphrase = Normalizer.normalize(passphrase, Normalizer.Form.NFC);
|
||||
ECKey key = ecMultiply ? decryptEC(normalizedPassphrase) : decryptNoEC(normalizedPassphrase);
|
||||
Sha256Hash hash = Sha256Hash.createDouble(key.toAddress(params).toString().getBytes(Charsets.US_ASCII));
|
||||
byte[] actualAddressHash = Arrays.copyOfRange(hash.getBytes(), 0, 4);
|
||||
if (!Arrays.equals(actualAddressHash, addressHash))
|
||||
throw new BadPassphraseException();
|
||||
return key;
|
||||
}
|
||||
|
||||
private ECKey decryptNoEC(String normalizedPassphrase) {
|
||||
try {
|
||||
byte[] derived = SCrypt.scrypt(normalizedPassphrase.getBytes(Charsets.UTF_8), addressHash, 16384, 8, 8, 64);
|
||||
byte[] key = Arrays.copyOfRange(derived, 32, 64);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key, "AES");
|
||||
|
||||
DRMWorkaround.maybeDisableExportControls();
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec);
|
||||
byte[] decrypted = cipher.doFinal(content, 0, 32);
|
||||
for (int i = 0; i < 32; i++)
|
||||
decrypted[i] ^= derived[i];
|
||||
return ECKey.fromPrivate(decrypted, compressed);
|
||||
} catch (GeneralSecurityException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private ECKey decryptEC(String normalizedPassphrase) {
|
||||
try {
|
||||
byte[] ownerEntropy = Arrays.copyOfRange(content, 0, 8);
|
||||
byte[] ownerSalt = hasLotAndSequence ? Arrays.copyOfRange(ownerEntropy, 0, 4) : ownerEntropy;
|
||||
|
||||
byte[] passFactorBytes = SCrypt.scrypt(normalizedPassphrase.getBytes(Charsets.UTF_8), ownerSalt, 16384, 8, 8, 32);
|
||||
if (hasLotAndSequence) {
|
||||
byte[] hashBytes = Bytes.concat(passFactorBytes, ownerEntropy);
|
||||
checkState(hashBytes.length == 40);
|
||||
passFactorBytes = Sha256Hash.createDouble(hashBytes).getBytes();
|
||||
}
|
||||
BigInteger passFactor = new BigInteger(1, passFactorBytes);
|
||||
ECKey k = ECKey.fromPrivate(passFactor, true);
|
||||
|
||||
byte[] salt = Bytes.concat(addressHash, ownerEntropy);
|
||||
checkState(salt.length == 12);
|
||||
byte[] derived = SCrypt.scrypt(k.getPubKey(), salt, 1024, 1, 1, 64);
|
||||
byte[] aeskey = Arrays.copyOfRange(derived, 32, 64);
|
||||
|
||||
SecretKeySpec keyspec = new SecretKeySpec(aeskey, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec);
|
||||
|
||||
byte[] encrypted2 = Arrays.copyOfRange(content, 16, 32);
|
||||
byte[] decrypted2 = cipher.doFinal(encrypted2);
|
||||
checkState(decrypted2.length == 16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
decrypted2[i] ^= derived[i + 16];
|
||||
|
||||
byte[] encrypted1 = Bytes.concat(Arrays.copyOfRange(content, 8, 16), Arrays.copyOfRange(decrypted2, 0, 8));
|
||||
byte[] decrypted1 = cipher.doFinal(encrypted1);
|
||||
checkState(decrypted1.length == 16);
|
||||
for (int i = 0; i < 16; i++)
|
||||
decrypted1[i] ^= derived[i];
|
||||
|
||||
byte[] seed = Bytes.concat(decrypted1, Arrays.copyOfRange(decrypted2, 8, 16));
|
||||
checkState(seed.length == 24);
|
||||
BigInteger seedFactor = new BigInteger(1, Sha256Hash.createDouble(seed).getBytes());
|
||||
checkState(passFactor.signum() >= 0);
|
||||
checkState(seedFactor.signum() >= 0);
|
||||
BigInteger priv = passFactor.multiply(seedFactor).mod(ECKey.CURVE.getN());
|
||||
|
||||
return ECKey.fromPrivate(priv, compressed);
|
||||
} catch (GeneralSecurityException x) {
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BIP38PrivateKey other = (BIP38PrivateKey) o;
|
||||
|
||||
return super.equals(other)
|
||||
&& Objects.equal(this.params, other.params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(super.hashCode(), params);
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
/**
|
||||
* <p>This is just a wrapper for the i (child number) as per BIP 32 with a boolean getter for the most significant bit
|
||||
* and a getter for the actual 0-based child number. A {@link List} of these forms a <i>path</i> through a
|
||||
* {@link DeterministicHierarchy}. This class is immutable.
|
||||
*/
|
||||
public class ChildNumber {
|
||||
/**
|
||||
* The bit that's set in the child number to indicate whether this key is "hardened". Given a hardened key, it is
|
||||
* not possible to derive a child public key if you know only the hardened public key. With a non-hardened key this
|
||||
* is possible, so you can derive trees of public keys given only a public parent, but the downside is that it's
|
||||
* possible to leak private keys if you disclose a parent public key and a child private key (elliptic curve maths
|
||||
* allows you to work upwards).
|
||||
*/
|
||||
public static final int HARDENED_BIT = 0x80000000;
|
||||
|
||||
public static final ChildNumber ZERO = new ChildNumber(0);
|
||||
public static final ChildNumber ONE = new ChildNumber(1);
|
||||
public static final ChildNumber ZERO_HARDENED = new ChildNumber(0, true);
|
||||
|
||||
/** Integer i as per BIP 32 spec, including the MSB denoting derivation type (0 = public, 1 = private) **/
|
||||
private final int i;
|
||||
|
||||
public ChildNumber(int childNumber, boolean isHardened) {
|
||||
if (hasHardenedBit(childNumber))
|
||||
throw new IllegalArgumentException("Most significant bit is reserved and shouldn't be set: " + childNumber);
|
||||
i = isHardened ? (childNumber | HARDENED_BIT) : childNumber;
|
||||
}
|
||||
|
||||
public ChildNumber(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
/** Returns the uint32 encoded form of the path element, including the most significant bit. */
|
||||
public int getI() {
|
||||
return i;
|
||||
}
|
||||
|
||||
/** Returns the uint32 encoded form of the path element, including the most significant bit. */
|
||||
public int i() { return i; }
|
||||
|
||||
public boolean isHardened() {
|
||||
return hasHardenedBit(i);
|
||||
}
|
||||
|
||||
private static boolean hasHardenedBit(int a) {
|
||||
return (a & HARDENED_BIT) != 0;
|
||||
}
|
||||
|
||||
/** Returns the child number without the hardening bit set (i.e. index in that part of the tree). */
|
||||
public int num() {
|
||||
return i & (~HARDENED_BIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d%s", num(), isHardened() ? "H" : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ChildNumber other = (ChildNumber) o;
|
||||
return i == other.i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return i;
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class DRMWorkaround {
|
||||
private static Logger log = LoggerFactory.getLogger(DRMWorkaround.class);
|
||||
|
||||
private static boolean done = false;
|
||||
|
||||
public static void maybeDisableExportControls() {
|
||||
// This sorry story is documented in https://bugs.openjdk.java.net/browse/JDK-7024850
|
||||
// Oracle received permission to ship AES-256 by default in 2011, but didn't get around to it for Java 8
|
||||
// even though that shipped in 2014! That's dumb. So we disable the ridiculous US government mandated DRM
|
||||
// for AES-256 here, as Tor/BIP38 requires it.
|
||||
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
if (Utils.isAndroidRuntime())
|
||||
return;
|
||||
try {
|
||||
Field gate = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
|
||||
gate.setAccessible(true);
|
||||
gate.setBoolean(null, false);
|
||||
final Field allPerm = Class.forName("javax.crypto.CryptoAllPermission").getDeclaredField("INSTANCE");
|
||||
allPerm.setAccessible(true);
|
||||
Object accessAllAreasCard = allPerm.get(null);
|
||||
final Constructor<?> constructor = Class.forName("javax.crypto.CryptoPermissions").getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
Object coll = constructor.newInstance();
|
||||
Method addPerm = Class.forName("javax.crypto.CryptoPermissions").getDeclaredMethod("add", java.security.Permission.class);
|
||||
addPerm.setAccessible(true);
|
||||
addPerm.invoke(coll, accessAllAreasCard);
|
||||
Field defaultPolicy = Class.forName("javax.crypto.JceSecurity").getDeclaredField("defaultPolicy");
|
||||
defaultPolicy.setAccessible(true);
|
||||
defaultPolicy.set(null, coll);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to deactivate AES-256 barrier logic, Tor mode/BIP38 decryption may crash if this JVM requires it: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
// TODO: This whole API feels a bit object heavy. Do we really need ChildNumber and so many maps, etc?
|
||||
// TODO: Should we be representing this using an actual tree arrangement in memory instead of a bunch of hashmaps?
|
||||
|
||||
/**
|
||||
* <p>A DeterministicHierarchy calculates and keeps a whole tree (hierarchy) of keys originating from a single
|
||||
* root key. This implements part of the BIP 32 specification. A deterministic key tree is useful because
|
||||
* Bitcoin's privacy system require new keys to be created for each transaction, but managing all these
|
||||
* keys quickly becomes unwieldy. In particular it becomes hard to back up and distribute them. By having
|
||||
* a way to derive random-looking but deterministic keys we can make wallet backup simpler and gain the
|
||||
* ability to hand out {@link DeterministicKey}s to other people who can then create new addresses
|
||||
* on the fly, without having to contact us.</p>
|
||||
*
|
||||
* <p>The hierarchy is started from a single root key, and a location in the tree is given by a path which
|
||||
* is a list of {@link ChildNumber}s.</p>
|
||||
*/
|
||||
public class DeterministicHierarchy implements Serializable {
|
||||
private final Map<ImmutableList<ChildNumber>, DeterministicKey> keys = Maps.newHashMap();
|
||||
private final ImmutableList<ChildNumber> rootPath;
|
||||
// Keep track of how many child keys each node has. This is kind of weak.
|
||||
private final Map<ImmutableList<ChildNumber>, ChildNumber> lastChildNumbers = Maps.newHashMap();
|
||||
|
||||
public static final int BIP32_STANDARDISATION_TIME_SECS = 1369267200;
|
||||
|
||||
/**
|
||||
* Constructs a new hierarchy rooted at the given key. Note that this does not have to be the top of the tree.
|
||||
* You can construct a DeterministicHierarchy for a subtree of a larger tree that you may not own.
|
||||
*/
|
||||
public DeterministicHierarchy(DeterministicKey rootKey) {
|
||||
putKey(rootKey);
|
||||
rootPath = rootKey.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a key into the heirarchy. Used during deserialization: you normally don't need this. Keys must be
|
||||
* inserted in order.
|
||||
*/
|
||||
public void putKey(DeterministicKey key) {
|
||||
ImmutableList<ChildNumber> path = key.getPath();
|
||||
// Update our tracking of what the next child in each branch of the tree should be. Just assume that keys are
|
||||
// inserted in order here.
|
||||
final DeterministicKey parent = key.getParent();
|
||||
if (parent != null)
|
||||
lastChildNumbers.put(parent.getPath(), key.getChildNumber());
|
||||
keys.put(path, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key for the given path, optionally creating it.
|
||||
*
|
||||
* @param path the path to the key
|
||||
* @param relativePath whether the path is relative to the root path
|
||||
* @param create whether the key corresponding to path should be created (with any necessary ancestors) if it doesn't exist already
|
||||
* @return next newly created key using the child derivation function
|
||||
* @throws IllegalArgumentException if create is false and the path was not found.
|
||||
*/
|
||||
public DeterministicKey get(List<ChildNumber> path, boolean relativePath, boolean create) {
|
||||
ImmutableList<ChildNumber> absolutePath = relativePath
|
||||
? ImmutableList.<ChildNumber>builder().addAll(rootPath).addAll(path).build()
|
||||
: ImmutableList.copyOf(path);
|
||||
if (!keys.containsKey(absolutePath)) {
|
||||
if (!create)
|
||||
throw new IllegalArgumentException(String.format("No key found for %s path %s.",
|
||||
relativePath ? "relative" : "absolute", HDUtils.formatPath(path)));
|
||||
checkArgument(absolutePath.size() > 0, "Can't derive the master key: nothing to derive from.");
|
||||
DeterministicKey parent = get(absolutePath.subList(0, absolutePath.size() - 1), false, true);
|
||||
putKey(HDKeyDerivation.deriveChildKey(parent, absolutePath.get(absolutePath.size() - 1)));
|
||||
}
|
||||
return keys.get(absolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the tree by calculating the next key that hangs off the given parent path. For example, if you pass a
|
||||
* path of 1/2 here and there are already keys 1/2/1 and 1/2/2 then it will derive 1/2/3.
|
||||
*
|
||||
* @param parentPath the path to the parent
|
||||
* @param relative whether the path is relative to the root path
|
||||
* @param createParent whether the parent corresponding to path should be created (with any necessary ancestors) if it doesn't exist already
|
||||
* @param privateDerivation whether to use private or public derivation
|
||||
* @return next newly created key using the child derivation funtcion
|
||||
* @throws IllegalArgumentException if the parent doesn't exist and createParent is false.
|
||||
*/
|
||||
public DeterministicKey deriveNextChild(ImmutableList<ChildNumber> parentPath, boolean relative, boolean createParent, boolean privateDerivation) {
|
||||
DeterministicKey parent = get(parentPath, relative, createParent);
|
||||
int nAttempts = 0;
|
||||
while (nAttempts++ < HDKeyDerivation.MAX_CHILD_DERIVATION_ATTEMPTS) {
|
||||
try {
|
||||
ChildNumber createChildNumber = getNextChildNumberToDerive(parent.getPath(), privateDerivation);
|
||||
return deriveChild(parent, createChildNumber);
|
||||
} catch (HDDerivationException ignore) { }
|
||||
}
|
||||
throw new HDDerivationException("Maximum number of child derivation attempts reached, this is probably an indication of a bug.");
|
||||
}
|
||||
|
||||
private ChildNumber getNextChildNumberToDerive(ImmutableList<ChildNumber> path, boolean privateDerivation) {
|
||||
ChildNumber lastChildNumber = lastChildNumbers.get(path);
|
||||
ChildNumber nextChildNumber = new ChildNumber(lastChildNumber != null ? lastChildNumber.num() + 1 : 0, privateDerivation);
|
||||
lastChildNumbers.put(path, nextChildNumber);
|
||||
return nextChildNumber;
|
||||
}
|
||||
|
||||
public int getNumChildren(ImmutableList<ChildNumber> path) {
|
||||
final ChildNumber cn = lastChildNumbers.get(path);
|
||||
if (cn == null)
|
||||
return 0;
|
||||
else
|
||||
return cn.num() + 1; // children start with zero based childnumbers
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the tree by calculating the requested child for the given path. For example, to get the key at position
|
||||
* 1/2/3 you would pass 1/2 as the parent path and 3 as the child number.
|
||||
*
|
||||
* @param parentPath the path to the parent
|
||||
* @param relative whether the path is relative to the root path
|
||||
* @param createParent whether the parent corresponding to path should be created (with any necessary ancestors) if it doesn't exist already
|
||||
* @return the requested key.
|
||||
* @throws IllegalArgumentException if the parent doesn't exist and createParent is false.
|
||||
*/
|
||||
public DeterministicKey deriveChild(List<ChildNumber> parentPath, boolean relative, boolean createParent, ChildNumber createChildNumber) {
|
||||
return deriveChild(get(parentPath, relative, createParent), createChildNumber);
|
||||
}
|
||||
|
||||
private DeterministicKey deriveChild(DeterministicKey parent, ChildNumber createChildNumber) {
|
||||
DeterministicKey childKey = HDKeyDerivation.deriveChildKey(parent, createChildNumber);
|
||||
putKey(childKey);
|
||||
return childKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root key that the {@link DeterministicHierarchy} was created with.
|
||||
*/
|
||||
public DeterministicKey getRootKey() {
|
||||
return get(rootPath, false, false);
|
||||
}
|
||||
}
|
@ -1,510 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
* Copyright 2014 Andreas Schildbach
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.*;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Objects.ToStringHelper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.dogecoin.dogecoinj.core.Utils.HEX;
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
/**
|
||||
* A deterministic key is a node in a {@link DeterministicHierarchy}. As per
|
||||
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki">the BIP 32 specification</a> it is a pair
|
||||
* (key, chaincode). If you know its path in the tree and its chain code you can derive more keys from this. To obtain
|
||||
* one of these, you can call {@link HDKeyDerivation#createMasterPrivateKey(byte[])}.
|
||||
*/
|
||||
public class DeterministicKey extends ECKey {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final DeterministicKey parent;
|
||||
private final ImmutableList<ChildNumber> childNumberPath;
|
||||
|
||||
/** 32 bytes */
|
||||
private final byte[] chainCode;
|
||||
|
||||
/** Constructs a key from its components. This is not normally something you should use. */
|
||||
public DeterministicKey(ImmutableList<ChildNumber> childNumberPath,
|
||||
byte[] chainCode,
|
||||
LazyECPoint publicAsPoint,
|
||||
@Nullable BigInteger priv,
|
||||
@Nullable DeterministicKey parent) {
|
||||
super(priv, compressPoint(checkNotNull(publicAsPoint)));
|
||||
checkArgument(chainCode.length == 32);
|
||||
this.parent = parent;
|
||||
this.childNumberPath = checkNotNull(childNumberPath);
|
||||
this.chainCode = Arrays.copyOf(chainCode, chainCode.length);
|
||||
}
|
||||
|
||||
public DeterministicKey(ImmutableList<ChildNumber> childNumberPath,
|
||||
byte[] chainCode,
|
||||
ECPoint publicAsPoint,
|
||||
@Nullable BigInteger priv,
|
||||
@Nullable DeterministicKey parent) {
|
||||
this(childNumberPath, chainCode, new LazyECPoint(publicAsPoint), priv, parent);
|
||||
}
|
||||
|
||||
/** Constructs a key from its components. This is not normally something you should use. */
|
||||
public DeterministicKey(ImmutableList<ChildNumber> childNumberPath,
|
||||
byte[] chainCode,
|
||||
BigInteger priv,
|
||||
@Nullable DeterministicKey parent) {
|
||||
super(priv, compressPoint(ECKey.CURVE.getG().multiply(priv)));
|
||||
checkArgument(chainCode.length == 32);
|
||||
this.parent = parent;
|
||||
this.childNumberPath = checkNotNull(childNumberPath);
|
||||
this.chainCode = Arrays.copyOf(chainCode, chainCode.length);
|
||||
}
|
||||
|
||||
/** Constructs a key from its components. This is not normally something you should use. */
|
||||
public DeterministicKey(ImmutableList<ChildNumber> childNumberPath,
|
||||
byte[] chainCode,
|
||||
KeyCrypter crypter,
|
||||
LazyECPoint pub,
|
||||
EncryptedData priv,
|
||||
@Nullable DeterministicKey parent) {
|
||||
this(childNumberPath, chainCode, pub, null, parent);
|
||||
this.encryptedPrivateKey = checkNotNull(priv);
|
||||
this.keyCrypter = checkNotNull(crypter);
|
||||
}
|
||||
|
||||
/** Clones the key */
|
||||
public DeterministicKey(DeterministicKey keyToClone, DeterministicKey newParent) {
|
||||
super(keyToClone.priv, keyToClone.pub.get());
|
||||
this.parent = newParent;
|
||||
this.childNumberPath = keyToClone.childNumberPath;
|
||||
this.chainCode = keyToClone.chainCode;
|
||||
this.encryptedPrivateKey = keyToClone.encryptedPrivateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path through some {@link DeterministicHierarchy} which reaches this keys position in the tree.
|
||||
* A path can be written as 1/2/1 which means the first child of the root, the second child of that node, then
|
||||
* the first child of that node.
|
||||
*/
|
||||
public ImmutableList<ChildNumber> getPath() {
|
||||
return childNumberPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of this key as a human readable string starting with M to indicate the master key.
|
||||
*/
|
||||
public String getPathAsString() {
|
||||
return HDUtils.formatPath(getPath());
|
||||
}
|
||||
|
||||
private int getDepth() {
|
||||
return childNumberPath.size();
|
||||
}
|
||||
|
||||
/** Returns the last element of the path returned by {@link DeterministicKey#getPath()} */
|
||||
public ChildNumber getChildNumber() {
|
||||
return getDepth() == 0 ? ChildNumber.ZERO : childNumberPath.get(childNumberPath.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chain code associated with this key. See the specification to learn more about chain codes.
|
||||
*/
|
||||
public byte[] getChainCode() {
|
||||
return chainCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns RIPE-MD160(SHA256(pub key bytes)).
|
||||
*/
|
||||
public byte[] getIdentifier() {
|
||||
return Utils.sha256hash160(getPubKey());
|
||||
}
|
||||
|
||||
/** Returns the first 32 bits of the result of {@link #getIdentifier()}. */
|
||||
public byte[] getFingerprint() {
|
||||
// TODO: why is this different than armory's fingerprint? BIP 32: "The first 32 bits of the identifier are called the fingerprint."
|
||||
return Arrays.copyOfRange(getIdentifier(), 0, 4);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DeterministicKey getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns private key bytes, padded with zeros to 33 bytes.
|
||||
* @throws java.lang.IllegalStateException if the private key bytes are missing.
|
||||
*/
|
||||
public byte[] getPrivKeyBytes33() {
|
||||
byte[] bytes33 = new byte[33];
|
||||
byte[] priv = getPrivKeyBytes();
|
||||
System.arraycopy(priv, 0, bytes33, 33 - priv.length, priv.length);
|
||||
return bytes33;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the same key with the private part removed. May return the same instance.
|
||||
*/
|
||||
public DeterministicKey getPubOnly() {
|
||||
if (isPubKeyOnly()) return this;
|
||||
return new DeterministicKey(getPath(), getChainCode(), pub, null, parent);
|
||||
}
|
||||
|
||||
|
||||
static byte[] addChecksum(byte[] input) {
|
||||
int inputLength = input.length;
|
||||
byte[] checksummed = new byte[inputLength + 4];
|
||||
System.arraycopy(input, 0, checksummed, 0, inputLength);
|
||||
byte[] checksum = Utils.doubleDigest(input);
|
||||
System.arraycopy(checksum, 0, checksummed, inputLength, 4);
|
||||
return checksummed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKey encrypt(KeyCrypter keyCrypter, KeyParameter aesKey) throws KeyCrypterException {
|
||||
throw new UnsupportedOperationException("Must supply a new parent for encryption");
|
||||
}
|
||||
|
||||
public DeterministicKey encrypt(KeyCrypter keyCrypter, KeyParameter aesKey, @Nullable DeterministicKey newParent) throws KeyCrypterException {
|
||||
// Same as the parent code, except we construct a DeterministicKey instead of an ECKey.
|
||||
checkNotNull(keyCrypter);
|
||||
if (newParent != null)
|
||||
checkArgument(newParent.isEncrypted());
|
||||
final byte[] privKeyBytes = getPrivKeyBytes();
|
||||
checkState(privKeyBytes != null, "Private key is not available");
|
||||
EncryptedData encryptedPrivateKey = keyCrypter.encrypt(privKeyBytes, aesKey);
|
||||
DeterministicKey key = new DeterministicKey(childNumberPath, chainCode, keyCrypter, pub, encryptedPrivateKey, newParent);
|
||||
if (newParent == null)
|
||||
key.setCreationTimeSeconds(getCreationTimeSeconds());
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* A deterministic key is considered to be encrypted if it has access to encrypted private key bytes, OR if its
|
||||
* parent does. The reason is because the parent would be encrypted under the same key and this key knows how to
|
||||
* rederive its own private key bytes from the parent, if needed.
|
||||
*/
|
||||
@Override
|
||||
public boolean isEncrypted() {
|
||||
return priv == null && (super.isEncrypted() || (parent != null && parent.isEncrypted()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this keys {@link com.dogecoin.dogecoinj.crypto.KeyCrypter} <b>or</b> the keycrypter of its parent key.
|
||||
*/
|
||||
@Override @Nullable
|
||||
public KeyCrypter getKeyCrypter() {
|
||||
if (keyCrypter != null)
|
||||
return keyCrypter;
|
||||
else if (parent != null)
|
||||
return parent.getKeyCrypter();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECDSASignature sign(Sha256Hash input, @Nullable KeyParameter aesKey) throws KeyCrypterException {
|
||||
if (isEncrypted()) {
|
||||
// If the key is encrypted, ECKey.sign will decrypt it first before rerunning sign. Decryption walks the
|
||||
// key heirarchy to find the private key (see below), so, we can just run the inherited method.
|
||||
return super.sign(input, aesKey);
|
||||
} else {
|
||||
// If it's not encrypted, derive the private via the parents.
|
||||
final BigInteger privateKey = findOrDerivePrivateKey();
|
||||
if (privateKey == null) {
|
||||
// This key is a part of a public-key only heirarchy and cannot be used for signing
|
||||
throw new MissingPrivateKeyException();
|
||||
}
|
||||
return super.doSign(input, privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKey decrypt(KeyCrypter keyCrypter, KeyParameter aesKey) throws KeyCrypterException {
|
||||
checkNotNull(keyCrypter);
|
||||
// Check that the keyCrypter matches the one used to encrypt the keys, if set.
|
||||
if (this.keyCrypter != null && !this.keyCrypter.equals(keyCrypter))
|
||||
throw new KeyCrypterException("The keyCrypter being used to decrypt the key is different to the one that was used to encrypt it");
|
||||
BigInteger privKey = findOrDeriveEncryptedPrivateKey(keyCrypter, aesKey);
|
||||
DeterministicKey key = new DeterministicKey(childNumberPath, chainCode, privKey, parent);
|
||||
if (!Arrays.equals(key.getPubKey(), getPubKey()))
|
||||
throw new KeyCrypterException("Provided AES key is wrong");
|
||||
if (parent == null)
|
||||
key.setCreationTimeSeconds(getCreationTimeSeconds());
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeterministicKey decrypt(KeyParameter aesKey) throws KeyCrypterException {
|
||||
return (DeterministicKey) super.decrypt(aesKey);
|
||||
}
|
||||
|
||||
// For when a key is encrypted, either decrypt our encrypted private key bytes, or work up the tree asking parents
|
||||
// to decrypt and re-derive.
|
||||
private BigInteger findOrDeriveEncryptedPrivateKey(KeyCrypter keyCrypter, KeyParameter aesKey) {
|
||||
if (encryptedPrivateKey != null)
|
||||
return new BigInteger(1, keyCrypter.decrypt(encryptedPrivateKey, aesKey));
|
||||
// Otherwise we don't have it, but maybe we can figure it out from our parents. Walk up the tree looking for
|
||||
// the first key that has some encrypted private key data.
|
||||
DeterministicKey cursor = parent;
|
||||
while (cursor != null) {
|
||||
if (cursor.encryptedPrivateKey != null) break;
|
||||
cursor = cursor.parent;
|
||||
}
|
||||
if (cursor == null)
|
||||
throw new KeyCrypterException("Neither this key nor its parents have an encrypted private key");
|
||||
byte[] parentalPrivateKeyBytes = keyCrypter.decrypt(cursor.encryptedPrivateKey, aesKey);
|
||||
return derivePrivateKeyDownwards(cursor, parentalPrivateKeyBytes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BigInteger findOrDerivePrivateKey() {
|
||||
DeterministicKey cursor = this;
|
||||
while (cursor != null) {
|
||||
if (cursor.priv != null) break;
|
||||
cursor = cursor.parent;
|
||||
}
|
||||
if (cursor == null)
|
||||
return null;
|
||||
return derivePrivateKeyDownwards(cursor, cursor.priv.toByteArray());
|
||||
}
|
||||
|
||||
private BigInteger derivePrivateKeyDownwards(DeterministicKey cursor, byte[] parentalPrivateKeyBytes) {
|
||||
DeterministicKey downCursor = new DeterministicKey(cursor.childNumberPath, cursor.chainCode,
|
||||
cursor.pub, new BigInteger(1, parentalPrivateKeyBytes), cursor.parent);
|
||||
// Now we have to rederive the keys along the path back to ourselves. That path can be found by just truncating
|
||||
// our path with the length of the parents path.
|
||||
ImmutableList<ChildNumber> path = childNumberPath.subList(cursor.getDepth(), childNumberPath.size());
|
||||
for (ChildNumber num : path) {
|
||||
downCursor = HDKeyDerivation.deriveChildKey(downCursor, num);
|
||||
}
|
||||
// downCursor is now the same key as us, but with private key bytes.
|
||||
checkState(downCursor.pub.equals(pub));
|
||||
return checkNotNull(downCursor.priv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a child at the given index using hardened derivation. Note: <code>index</code> is
|
||||
* not the "i" value. If you want the softened derivation, then use instead
|
||||
* <code>HDKeyDerivation.deriveChildKey(this, new ChildNumber(child, false))</code>.
|
||||
*/
|
||||
public DeterministicKey derive(int child) {
|
||||
return HDKeyDerivation.deriveChildKey(this, new ChildNumber(child, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the private key of this deterministic key. Even if this object isn't storing the private key,
|
||||
* it can be re-derived by walking up to the parents if necessary and this is what will happen.
|
||||
* @throws java.lang.IllegalStateException if the parents are encrypted or a watching chain.
|
||||
*/
|
||||
@Override
|
||||
public BigInteger getPrivKey() {
|
||||
final BigInteger key = findOrDerivePrivateKey();
|
||||
checkState(key != null, "Private key bytes not available");
|
||||
return key;
|
||||
}
|
||||
|
||||
public byte[] serializePublic(NetworkParameters params) {
|
||||
return serialize(params, true);
|
||||
}
|
||||
|
||||
public byte[] serializePrivate(NetworkParameters params) {
|
||||
return serialize(params, false);
|
||||
}
|
||||
|
||||
private byte[] serialize(NetworkParameters params, boolean pub) {
|
||||
ByteBuffer ser = ByteBuffer.allocate(78);
|
||||
ser.putInt(pub ? params.getBip32HeaderPub() : params.getBip32HeaderPriv());
|
||||
ser.put((byte) getDepth());
|
||||
if (parent == null) {
|
||||
ser.putInt(0);
|
||||
} else {
|
||||
ser.put(parent.getFingerprint());
|
||||
}
|
||||
ser.putInt(getChildNumber().i());
|
||||
ser.put(getChainCode());
|
||||
ser.put(pub ? getPubKey() : getPrivKeyBytes33());
|
||||
checkState(ser.position() == 78);
|
||||
return ser.array();
|
||||
}
|
||||
|
||||
public String serializePubB58(NetworkParameters params) {
|
||||
return toBase58(serialize(params, true));
|
||||
}
|
||||
|
||||
public String serializePrivB58(NetworkParameters params) {
|
||||
return toBase58(serialize(params, false));
|
||||
}
|
||||
|
||||
static String toBase58(byte[] ser) {
|
||||
return Base58.encode(addChecksum(ser));
|
||||
}
|
||||
|
||||
/** Deserialize a base-58-encoded HD Key with no parent */
|
||||
public static DeterministicKey deserializeB58(String base58, NetworkParameters params) {
|
||||
return deserializeB58(null, base58, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a base-58-encoded HD Key.
|
||||
* @param parent The parent node in the given key's deterministic hierarchy.
|
||||
*/
|
||||
public static DeterministicKey deserializeB58(@Nullable DeterministicKey parent, String base58, NetworkParameters params) {
|
||||
try {
|
||||
return deserialize(params, Base58.decodeChecked(base58), parent);
|
||||
} catch (AddressFormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize an HD Key with no parent
|
||||
*/
|
||||
public static DeterministicKey deserialize(NetworkParameters params, byte[] serializedKey) {
|
||||
return deserialize(params, serializedKey, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize an HD Key.
|
||||
* @param parent The parent node in the given key's deterministic hierarchy.
|
||||
*/
|
||||
public static DeterministicKey deserialize(NetworkParameters params, byte[] serializedKey, @Nullable DeterministicKey parent) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
|
||||
int header = buffer.getInt();
|
||||
if (header != params.getBip32HeaderPriv() && header != params.getBip32HeaderPub())
|
||||
throw new IllegalArgumentException("Unknown header bytes: " + toBase58(serializedKey).substring(0, 4));
|
||||
boolean pub = header == params.getBip32HeaderPub();
|
||||
byte depth = buffer.get();
|
||||
byte[] parentFingerprint = new byte[4];
|
||||
buffer.get(parentFingerprint);
|
||||
final int i = buffer.getInt();
|
||||
final ChildNumber childNumber = new ChildNumber(i);
|
||||
ImmutableList<ChildNumber> path;
|
||||
if (parent != null) {
|
||||
if (Arrays.equals(parentFingerprint, HDUtils.longTo4ByteArray(0)))
|
||||
throw new IllegalArgumentException("Parent was provided but this key doesn't have one");
|
||||
if (!Arrays.equals(parent.getFingerprint(), parentFingerprint))
|
||||
throw new IllegalArgumentException("Parent fingerprints don't match");
|
||||
path = HDUtils.append(parent.getPath(), childNumber);
|
||||
if (path.size() != depth)
|
||||
throw new IllegalArgumentException("Depth does not match");
|
||||
} else {
|
||||
if (depth >= 1)
|
||||
// We have been given a key that is not a root key, yet we lack any object representing the parent.
|
||||
// This can happen when deserializing an account key for a watching wallet. In this case, we assume that
|
||||
// the client wants to conceal the key's position in the hierarchy. The parent is deemed to be the
|
||||
// root of the hierarchy.
|
||||
path = ImmutableList.of(childNumber);
|
||||
else path = ImmutableList.of();
|
||||
}
|
||||
byte[] chainCode = new byte[32];
|
||||
buffer.get(chainCode);
|
||||
byte[] data = new byte[33];
|
||||
buffer.get(data);
|
||||
checkArgument(!buffer.hasRemaining(), "Found unexpected data in key");
|
||||
if (pub) {
|
||||
ECPoint point = ECKey.CURVE.getCurve().decodePoint(data);
|
||||
return new DeterministicKey(path, chainCode, new LazyECPoint(point), null, parent);
|
||||
} else {
|
||||
return new DeterministicKey(path, chainCode, new BigInteger(1, data), parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The creation time of a deterministic key is equal to that of its parent, unless this key is the root of a tree
|
||||
* in which case the time is stored alongside the key as per normal, see {@link com.dogecoin.dogecoinj.core.ECKey#getCreationTimeSeconds()}.
|
||||
*/
|
||||
@Override
|
||||
public long getCreationTimeSeconds() {
|
||||
if (parent != null)
|
||||
return parent.getCreationTimeSeconds();
|
||||
else
|
||||
return super.getCreationTimeSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* The creation time of a deterministic key is equal to that of its parent, unless this key is the root of a tree.
|
||||
* Thus, setting the creation time on a leaf is forbidden.
|
||||
*/
|
||||
@Override
|
||||
public void setCreationTimeSeconds(long newCreationTimeSeconds) {
|
||||
if (parent != null)
|
||||
throw new IllegalStateException("Creation time can only be set on root keys.");
|
||||
else
|
||||
super.setCreationTimeSeconds(newCreationTimeSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies equality of all fields but NOT the parent pointer (thus the same key derived in two separate heirarchy
|
||||
* objects will equal each other.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DeterministicKey other = (DeterministicKey) o;
|
||||
|
||||
return super.equals(other)
|
||||
&& Arrays.equals(this.chainCode, other.chainCode)
|
||||
&& Objects.equal(this.childNumberPath, other.childNumberPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + childNumberPath.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(chainCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final ToStringHelper helper = Objects.toStringHelper(this).omitNullValues();
|
||||
helper.add("pub", Utils.HEX.encode(pub.getEncoded()));
|
||||
helper.add("chainCode", HEX.encode(chainCode));
|
||||
helper.add("path", getPathAsString());
|
||||
if (creationTimeSeconds > 0)
|
||||
helper.add("creationTimeSeconds", creationTimeSeconds);
|
||||
helper.add("isEncrypted", isEncrypted());
|
||||
helper.add("isPubKeyOnly", isPubKeyOnly());
|
||||
return helper.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void formatKeyWithAddress(boolean includePrivateKeys, StringBuilder builder, NetworkParameters params) {
|
||||
final Address address = toAddress(params);
|
||||
builder.append(" addr:");
|
||||
builder.append(address.toString());
|
||||
builder.append(" hash160:");
|
||||
builder.append(Utils.HEX.encode(getPubKeyHash()));
|
||||
builder.append(" (");
|
||||
builder.append(getPathAsString());
|
||||
builder.append(")");
|
||||
builder.append("\n");
|
||||
if (includePrivateKeys) {
|
||||
builder.append(" ");
|
||||
builder.append(toStringWithPrivate(params));
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.wallet.Protos;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides a uniform way to access something that can be optionally encrypted with a
|
||||
* {@link com.dogecoin.dogecoinj.crypto.KeyCrypter}, yielding an {@link com.dogecoin.dogecoinj.crypto.EncryptedData}, and
|
||||
* which can have a creation time associated with it.
|
||||
*/
|
||||
public interface EncryptableItem {
|
||||
/** Returns whether the item is encrypted or not. If it is, then {@link #getSecretBytes()} will return null. */
|
||||
public boolean isEncrypted();
|
||||
|
||||
/** Returns the raw bytes of the item, if not encrypted, or null if encrypted or the secret is missing. */
|
||||
@Nullable public byte[] getSecretBytes();
|
||||
|
||||
/** Returns the initialization vector and encrypted secret bytes, or null if not encrypted. */
|
||||
@Nullable public EncryptedData getEncryptedData();
|
||||
|
||||
/** Returns an enum constant describing what algorithm was used to encrypt the key or UNENCRYPTED. */
|
||||
public Protos.Wallet.EncryptionType getEncryptionType();
|
||||
|
||||
/** Returns the time in seconds since the UNIX epoch at which this encryptable item was first created/derived. */
|
||||
public long getCreationTimeSeconds();
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Jim Burton.
|
||||
*
|
||||
* Licensed under the MIT license (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>An instance of EncryptedData is a holder for an initialization vector and encrypted bytes. It is typically
|
||||
* used to hold encrypted private key bytes.</p>
|
||||
*
|
||||
* <p>The initialisation vector is random data that is used to initialise the AES block cipher when the
|
||||
* private key bytes were encrypted. You need these for decryption.</p>
|
||||
*/
|
||||
public final class EncryptedData {
|
||||
public final byte[] initialisationVector;
|
||||
public final byte[] encryptedBytes;
|
||||
|
||||
public EncryptedData(byte[] initialisationVector, byte[] encryptedBytes) {
|
||||
this.initialisationVector = Arrays.copyOf(initialisationVector, initialisationVector.length);
|
||||
this.encryptedBytes = Arrays.copyOf(encryptedBytes, encryptedBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
EncryptedData that = (EncryptedData) o;
|
||||
return Arrays.equals(encryptedBytes, that.encryptedBytes) && Arrays.equals(initialisationVector, that.initialisationVector);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(initialisationVector);
|
||||
result = 31 * result + Arrays.hashCode(encryptedBytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EncryptedData [initialisationVector=" + Arrays.toString(initialisationVector) + ", encryptedPrivateKey=" + Arrays.toString(encryptedBytes) + "]";
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Jim Burton.
|
||||
*
|
||||
* Licensed under the MIT license (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>An EncryptedPrivateKey contains the information produced after encrypting the private key bytes of an ECKey.</p>
|
||||
*
|
||||
* <p>It contains two member variables - initialisationVector and encryptedPrivateBytes. The initialisationVector is
|
||||
* a randomly chosen list of bytes that were used to initialise the AES block cipher when the private key bytes were encrypted.
|
||||
* You need these for decryption. The encryptedPrivateBytes are the result of AES encrypting the private keys using
|
||||
* an AES key that is derived from a user entered password. You need the password to recreate the AES key in order
|
||||
* to decrypt these bytes.</p>
|
||||
*/
|
||||
public class EncryptedPrivateKey {
|
||||
|
||||
private byte[] initialisationVector = null;
|
||||
private byte[] encryptedPrivateBytes = null;
|
||||
|
||||
/**
|
||||
* Cloning constructor.
|
||||
* @param encryptedPrivateKey EncryptedPrivateKey to clone.
|
||||
*/
|
||||
public EncryptedPrivateKey(EncryptedPrivateKey encryptedPrivateKey) {
|
||||
setInitialisationVector(encryptedPrivateKey.getInitialisationVector());
|
||||
setEncryptedPrivateBytes(encryptedPrivateKey.getEncryptedBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initialisationVector
|
||||
* @param encryptedPrivateKeys
|
||||
*/
|
||||
public EncryptedPrivateKey(byte[] initialisationVector, byte[] encryptedPrivateKeys) {
|
||||
setInitialisationVector(initialisationVector);
|
||||
setEncryptedPrivateBytes(encryptedPrivateKeys);
|
||||
}
|
||||
|
||||
public byte[] getInitialisationVector() {
|
||||
return initialisationVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the initialisationVector, cloning the bytes.
|
||||
*
|
||||
* @param initialisationVector
|
||||
*/
|
||||
public void setInitialisationVector(byte[] initialisationVector) {
|
||||
if (initialisationVector == null) {
|
||||
this.initialisationVector = null;
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] cloneIV = new byte[initialisationVector.length];
|
||||
System.arraycopy(initialisationVector, 0, cloneIV, 0, initialisationVector.length);
|
||||
|
||||
this.initialisationVector = cloneIV;
|
||||
}
|
||||
|
||||
public byte[] getEncryptedBytes() {
|
||||
return encryptedPrivateBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encrypted private key bytes, cloning them.
|
||||
*
|
||||
* @param encryptedPrivateBytes
|
||||
*/
|
||||
public void setEncryptedPrivateBytes(byte[] encryptedPrivateBytes) {
|
||||
if (encryptedPrivateBytes == null) {
|
||||
this.encryptedPrivateBytes = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.encryptedPrivateBytes = Arrays.copyOf(encryptedPrivateBytes, encryptedPrivateBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncryptedPrivateKey clone() {
|
||||
return new EncryptedPrivateKey(getInitialisationVector(), getEncryptedBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return com.google.common.base.Objects.hashCode(encryptedPrivateBytes, initialisationVector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
EncryptedPrivateKey other = (EncryptedPrivateKey) o;
|
||||
return Objects.equal(initialisationVector, other.initialisationVector) &&
|
||||
Objects.equal(encryptedPrivateBytes, other.encryptedPrivateBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EncryptedPrivateKey [initialisationVector=" + Arrays.toString(initialisationVector) + ", encryptedPrivateKey=" + Arrays.toString(encryptedPrivateBytes) + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the EncryptedPrivateKey contents from memory (overwriting all data including PRIVATE KEYS).
|
||||
* WARNING - this method irreversibly deletes the private key information.
|
||||
*/
|
||||
public void clear() {
|
||||
if (encryptedPrivateBytes != null) {
|
||||
Arrays.fill(encryptedPrivateBytes, (byte)0);
|
||||
}
|
||||
if (initialisationVector != null) {
|
||||
Arrays.fill(initialisationVector, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
public class HDDerivationException extends RuntimeException {
|
||||
public HDDerivationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.ECKey;
|
||||
import com.dogecoin.dogecoinj.core.Utils;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.spongycastle.crypto.macs.HMac;
|
||||
import org.spongycastle.math.ec.ECPoint;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* Implementation of the <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki">BIP 32</a>
|
||||
* deterministic wallet child key generation algorithm.
|
||||
*/
|
||||
public final class HDKeyDerivation {
|
||||
static {
|
||||
// Init proper random number generator, as some old Android installations have bugs that make it unsecure.
|
||||
if (Utils.isAndroidRuntime())
|
||||
new LinuxSecureRandom();
|
||||
|
||||
RAND_INT = new BigInteger(256, new SecureRandom());
|
||||
}
|
||||
|
||||
// Some arbitrary random number. Doesn't matter what it is.
|
||||
private static final BigInteger RAND_INT;
|
||||
|
||||
private HDKeyDerivation() { }
|
||||
|
||||
/**
|
||||
* Child derivation may fail (although with extremely low probability); in such case it is re-attempted.
|
||||
* This is the maximum number of re-attempts (to avoid an infinite loop in case of bugs etc.).
|
||||
*/
|
||||
public static final int MAX_CHILD_DERIVATION_ATTEMPTS = 100;
|
||||
|
||||
public static final HMac MASTER_HMAC_SHA512 = HDUtils.createHmacSha512Digest("Bitcoin seed".getBytes());
|
||||
|
||||
/**
|
||||
* Generates a new deterministic key from the given seed, which can be any arbitrary byte array. However resist
|
||||
* the temptation to use a string as the seed - any key derived from a password is likely to be weak and easily
|
||||
* broken by attackers (this is not theoretical, people have had money stolen that way). This method checks
|
||||
* that the given seed is at least 64 bits long.
|
||||
*
|
||||
* @throws HDDerivationException if generated master key is invalid (private key 0 or >= n).
|
||||
* @throws IllegalArgumentException if the seed is less than 8 bytes and could be brute forced.
|
||||
*/
|
||||
public static DeterministicKey createMasterPrivateKey(byte[] seed) throws HDDerivationException {
|
||||
checkArgument(seed.length > 8, "Seed is too short and could be brute forced");
|
||||
// Calculate I = HMAC-SHA512(key="Bitcoin seed", msg=S)
|
||||
byte[] i = HDUtils.hmacSha512(MASTER_HMAC_SHA512, seed);
|
||||
// Split I into two 32-byte sequences, Il and Ir.
|
||||
// Use Il as master secret key, and Ir as master chain code.
|
||||
checkState(i.length == 64, i.length);
|
||||
byte[] il = Arrays.copyOfRange(i, 0, 32);
|
||||
byte[] ir = Arrays.copyOfRange(i, 32, 64);
|
||||
Arrays.fill(i, (byte)0);
|
||||
DeterministicKey masterPrivKey = createMasterPrivKeyFromBytes(il, ir);
|
||||
Arrays.fill(il, (byte)0);
|
||||
Arrays.fill(ir, (byte)0);
|
||||
// Child deterministic keys will chain up to their parents to find the keys.
|
||||
masterPrivKey.setCreationTimeSeconds(Utils.currentTimeSeconds());
|
||||
return masterPrivKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HDDerivationException if privKeyBytes is invalid (0 or >= n).
|
||||
*/
|
||||
public static DeterministicKey createMasterPrivKeyFromBytes(byte[] privKeyBytes, byte[] chainCode) throws HDDerivationException {
|
||||
BigInteger priv = new BigInteger(1, privKeyBytes);
|
||||
assertNonZero(priv, "Generated master key is invalid.");
|
||||
assertLessThanN(priv, "Generated master key is invalid.");
|
||||
return new DeterministicKey(ImmutableList.<ChildNumber>of(), chainCode, priv, null);
|
||||
}
|
||||
|
||||
public static DeterministicKey createMasterPubKeyFromBytes(byte[] pubKeyBytes, byte[] chainCode) {
|
||||
return new DeterministicKey(ImmutableList.<ChildNumber>of(), chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), pubKeyBytes), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a key given the "extended" child number, ie. the 0x80000000 bit of the value that you
|
||||
* pass for <code>childNumber</code> will determine whether to use hardened derivation or not.
|
||||
* Consider whether your code would benefit from the clarity of the equivalent, but explicit, form
|
||||
* of this method that takes a <code>ChildNumber</code> rather than an <code>int</code>, for example:
|
||||
* <code>deriveChildKey(parent, new ChildNumber(childNumber, true))</code>
|
||||
* where the value of the hardened bit of <code>childNumber</code> is zero.
|
||||
*/
|
||||
public static DeterministicKey deriveChildKey(DeterministicKey parent, int childNumber) {
|
||||
return deriveChildKey(parent, new ChildNumber(childNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a key of the "extended" child number, ie. with the 0x80000000 bit specifying whether to use
|
||||
* hardened derivation or not. If derivation fails, tries a next child.
|
||||
*/
|
||||
public static DeterministicKey deriveThisOrNextChildKey(DeterministicKey parent, int childNumber) {
|
||||
int nAttempts = 0;
|
||||
ChildNumber child = new ChildNumber(childNumber);
|
||||
boolean isHardened = child.isHardened();
|
||||
while (nAttempts < MAX_CHILD_DERIVATION_ATTEMPTS) {
|
||||
try {
|
||||
child = new ChildNumber(child.num() + nAttempts, isHardened);
|
||||
return deriveChildKey(parent, child);
|
||||
} catch (HDDerivationException ignore) { }
|
||||
nAttempts++;
|
||||
}
|
||||
throw new HDDerivationException("Maximum number of child derivation attempts reached, this is probably an indication of a bug.");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HDDerivationException if private derivation is attempted for a public-only parent key, or
|
||||
* if the resulting derived key is invalid (eg. private key == 0).
|
||||
*/
|
||||
public static DeterministicKey deriveChildKey(DeterministicKey parent, ChildNumber childNumber) throws HDDerivationException {
|
||||
if (parent.isPubKeyOnly()) {
|
||||
RawKeyBytes rawKey = deriveChildKeyBytesFromPublic(parent, childNumber, PublicDeriveMode.NORMAL);
|
||||
return new DeterministicKey(
|
||||
HDUtils.append(parent.getPath(), childNumber),
|
||||
rawKey.chainCode,
|
||||
new LazyECPoint(ECKey.CURVE.getCurve(), rawKey.keyBytes),
|
||||
null,
|
||||
parent);
|
||||
} else {
|
||||
RawKeyBytes rawKey = deriveChildKeyBytesFromPrivate(parent, childNumber);
|
||||
return new DeterministicKey(
|
||||
HDUtils.append(parent.getPath(), childNumber),
|
||||
rawKey.chainCode,
|
||||
new BigInteger(1, rawKey.keyBytes),
|
||||
parent);
|
||||
}
|
||||
}
|
||||
|
||||
public static RawKeyBytes deriveChildKeyBytesFromPrivate(DeterministicKey parent,
|
||||
ChildNumber childNumber) throws HDDerivationException {
|
||||
checkArgument(parent.hasPrivKey(), "Parent key must have private key bytes for this method.");
|
||||
byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
|
||||
assert parentPublicKey.length == 33 : parentPublicKey.length;
|
||||
ByteBuffer data = ByteBuffer.allocate(37);
|
||||
if (childNumber.isHardened()) {
|
||||
data.put(parent.getPrivKeyBytes33());
|
||||
} else {
|
||||
data.put(parentPublicKey);
|
||||
}
|
||||
data.putInt(childNumber.i());
|
||||
byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
|
||||
assert i.length == 64 : i.length;
|
||||
byte[] il = Arrays.copyOfRange(i, 0, 32);
|
||||
byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
|
||||
BigInteger ilInt = new BigInteger(1, il);
|
||||
assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
|
||||
final BigInteger priv = parent.getPrivKey();
|
||||
BigInteger ki = priv.add(ilInt).mod(ECKey.CURVE.getN());
|
||||
assertNonZero(ki, "Illegal derived key: derived private key equals 0.");
|
||||
return new RawKeyBytes(ki.toByteArray(), chainCode);
|
||||
}
|
||||
|
||||
public enum PublicDeriveMode {
|
||||
NORMAL,
|
||||
WITH_INVERSION
|
||||
}
|
||||
|
||||
public static RawKeyBytes deriveChildKeyBytesFromPublic(DeterministicKey parent, ChildNumber childNumber, PublicDeriveMode mode) throws HDDerivationException {
|
||||
checkArgument(!childNumber.isHardened(), "Can't use private derivation with public keys only.");
|
||||
byte[] parentPublicKey = ECKey.compressPoint(parent.getPubKeyPoint()).getEncoded();
|
||||
assert parentPublicKey.length == 33 : parentPublicKey.length;
|
||||
ByteBuffer data = ByteBuffer.allocate(37);
|
||||
data.put(parentPublicKey);
|
||||
data.putInt(childNumber.i());
|
||||
byte[] i = HDUtils.hmacSha512(parent.getChainCode(), data.array());
|
||||
assert i.length == 64 : i.length;
|
||||
byte[] il = Arrays.copyOfRange(i, 0, 32);
|
||||
byte[] chainCode = Arrays.copyOfRange(i, 32, 64);
|
||||
BigInteger ilInt = new BigInteger(1, il);
|
||||
assertLessThanN(ilInt, "Illegal derived key: I_L >= n");
|
||||
|
||||
final ECPoint G = ECKey.CURVE.getG();
|
||||
final BigInteger N = ECKey.CURVE.getN();
|
||||
ECPoint Ki;
|
||||
switch (mode) {
|
||||
case NORMAL:
|
||||
Ki = G.multiply(ilInt).add(parent.getPubKeyPoint());
|
||||
break;
|
||||
case WITH_INVERSION:
|
||||
// This trick comes from Gregory Maxwell. Check the homomorphic properties of our curve hold. The
|
||||
// below calculations should be redundant and give the same result as NORMAL but if the precalculated
|
||||
// tables have taken a bit flip will yield a different answer. This mode is used when vending a key
|
||||
// to perform a last-ditch sanity check trying to catch bad RAM.
|
||||
Ki = G.multiply(ilInt.add(RAND_INT));
|
||||
BigInteger additiveInverse = RAND_INT.negate().mod(N);
|
||||
Ki = Ki.add(G.multiply(additiveInverse));
|
||||
Ki = Ki.add(parent.getPubKeyPoint());
|
||||
break;
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
|
||||
assertNonInfinity(Ki, "Illegal derived key: derived public key equals infinity.");
|
||||
return new RawKeyBytes(Ki.getEncoded(true), chainCode);
|
||||
}
|
||||
|
||||
private static void assertNonZero(BigInteger integer, String errorMessage) {
|
||||
if (integer.equals(BigInteger.ZERO))
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
private static void assertNonInfinity(ECPoint point, String errorMessage) {
|
||||
if (point.equals(ECKey.CURVE.getCurve().getInfinity()))
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
private static void assertLessThanN(BigInteger integer, String errorMessage) {
|
||||
if (integer.compareTo(ECKey.CURVE.getN()) > 0)
|
||||
throw new HDDerivationException(errorMessage);
|
||||
}
|
||||
|
||||
public static class RawKeyBytes {
|
||||
public final byte[] keyBytes, chainCode;
|
||||
|
||||
public RawKeyBytes(byte[] keyBytes, byte[] chainCode) {
|
||||
this.keyBytes = keyBytes;
|
||||
this.chainCode = chainCode;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Matija Mazi.
|
||||
* Copyright 2014 Giannis Dzegoutanis.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.dogecoin.dogecoinj.crypto;
|
||||
|
||||
import com.dogecoin.dogecoinj.core.ECKey;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.spongycastle.crypto.digests.SHA512Digest;
|
||||
import org.spongycastle.crypto.macs.HMac;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Static utilities used in BIP 32 Hierarchical Deterministic Wallets (HDW).
|
||||
*/
|
||||
public final class HDUtils {
|
||||
private static final Joiner PATH_JOINER = Joiner.on("/");
|
||||
|
||||
static HMac createHmacSha512Digest(byte[] key) {
|
||||
SHA512Digest digest = new SHA512Digest();
|
||||
HMac hMac = new HMac(digest);
|
||||
hMac.init(new KeyParameter(key));
|
||||
return hMac;
|
||||
}
|
||||
|
||||
static byte[] hmacSha512(HMac hmacSha512, byte[] input) {
|
||||
hmacSha512.reset();
|
||||
hmacSha512.update(input, 0, input.length);
|
||||
byte[] out = new byte[64];
|
||||
hmacSha512.doFinal(out, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
public static byte[] hmacSha512(byte[] key, byte[] data) {
|
||||
return hmacSha512(createHmacSha512Digest(key), data);
|
||||
}
|
||||
|
||||
static byte[] toCompressed(byte[] uncompressedPoint) {
|
||||
return ECKey.CURVE.getCurve().decodePoint(uncompressedPoint).getEncoded(true);
|
||||
}
|
||||
|
||||
static byte[] longTo4ByteArray(long n) {
|
||||
byte[] bytes = Arrays.copyOfRange(ByteBuffer.allocate(8).putLong(n).array(), 4, 8);
|
||||
assert bytes.length == 4 : bytes.length;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static ImmutableList<ChildNumber> append(List<ChildNumber> path, ChildNumber childNumber) {
|
||||
return ImmutableList.<ChildNumber>builder().addAll(path).add(childNumber).build();
|
||||
}
|
||||
|
||||
public static String formatPath(List<ChildNumber> path) {
|
||||
return PATH_JOINER.join(Iterables.concat(Collections.singleton("M"), path));
|
||||
}
|
||||
|
||||
/**
|
||||
* The path is a human-friendly representation of the deterministic path. For example:
|
||||
*
|
||||
* "44H / 0H / 0H / 1 / 1"
|
||||
*
|
||||
* Where a letter "H" means hardened key. Spaces are ignored.
|
||||
*/
|
||||
public static List<ChildNumber> parsePath(@Nonnull String path) {
|
||||
String[] parsedNodes = path.replace("M", "").split("/");
|
||||
List<ChildNumber> nodes = new ArrayList<ChildNumber>();
|
||||
|
||||
for (String n : parsedNodes) {
|
||||
n = n.replaceAll(" ", "");
|
||||
if (n.length() == 0) continue;
|
||||
boolean isHard = n.endsWith("H");
|
||||
if (isHard) n = n.substring(0, n.length() - 1);
|
||||
int nodeNumber = Integer.parseInt(n);
|
||||
nodes.add(new ChildNumber(nodeNumber, isHard));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user