mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 14:52:16 +00:00
Removed namecoin stuff
This commit is contained in:
parent
889ff26ebf
commit
9159a629a2
117
namecoin/pom.xml
117
namecoin/pom.xml
@ -1,117 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>
|
||||
<groupId>org.libdohj</groupId>
|
||||
<artifactId>libdohj-namecoin</artifactId>
|
||||
<version>0.14-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<!-- Dummy block to make Maven Central happy: authors list is in AUTHORS -->
|
||||
<developers>
|
||||
<developer>
|
||||
<name>The libdohj team.</name>
|
||||
<email>info@dogecoin.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>update-protobuf</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>updateProtobuf</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile-protoc</id>
|
||||
<phase>generate-sources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<path id="proto.path">
|
||||
<fileset dir="src">
|
||||
<include name="**/*.proto"/>
|
||||
</fileset>
|
||||
</path>
|
||||
<pathconvert pathsep=" " property="proto.files" refid="proto.path"/>
|
||||
<exec executable="protoc" failonerror="true">
|
||||
<arg value="--java_out=${project.basedir}/src/main/java"/>
|
||||
<arg value="-I${project.basedir}/src"/>
|
||||
<arg line="${proto.files}"/>
|
||||
</exec>
|
||||
</tasks>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lambdaworks</groupId>
|
||||
<artifactId>scrypt</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bitcoinj</groupId>
|
||||
<artifactId>bitcoinj-core</artifactId>
|
||||
<version>0.14.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.libdohj</groupId>
|
||||
<artifactId>libdohj-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fusesource.leveldbjni</groupId>
|
||||
<artifactId>leveldbjni-all</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.6</maven.compiler.source>
|
||||
<maven.compiler.target>1.6</maven.compiler.target>
|
||||
</properties>
|
||||
<name>libdohj</name>
|
||||
</project>
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
// TODO: Document this.
|
||||
|
||||
// identity is used for things like Tor stream isolation
|
||||
public interface NameLookupByBlockHash {
|
||||
|
||||
public Transaction getNameTransaction(String name, Sha256Hash blockHash, String identity) throws Exception;
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.Block;
|
||||
import org.bitcoinj.core.PeerGroup;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public class NameLookupByBlockHashOneFullBlock implements NameLookupByBlockHash {
|
||||
|
||||
protected PeerGroup peerGroup;
|
||||
|
||||
public NameLookupByBlockHashOneFullBlock (PeerGroup peerGroup) {
|
||||
this.peerGroup = peerGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction getNameTransaction(String name, Sha256Hash blockHash, String identity) throws Exception {
|
||||
|
||||
Block nameFullBlock = peerGroup.getDownloadPeer().getBlock(blockHash).get();
|
||||
|
||||
// The full block hasn't been verified in any way!
|
||||
// So let's do that now.
|
||||
|
||||
if (! nameFullBlock.getHash().equals(blockHash)) {
|
||||
throw new Exception("Block hash mismatch!");
|
||||
}
|
||||
|
||||
// Now we know that the received block actually does have a header that matches the hash that we requested.
|
||||
// However, that doesn't mean that the block's contents are valid.
|
||||
|
||||
final EnumSet<Block.VerifyFlag> flags = EnumSet.noneOf(Block.VerifyFlag.class);
|
||||
nameFullBlock.verify(-1, flags);
|
||||
|
||||
// Now we know that the block is internally valid (including the merkle root).
|
||||
// We haven't verified signature validity, but our threat model is SPV.
|
||||
|
||||
for (Transaction tx : nameFullBlock.getTransactions()) {
|
||||
if (NameTransactionUtils.getNameAnyUpdateOutput(tx, name) != null) {
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
// The name wasn't found.
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public interface NameLookupByBlockHeight {
|
||||
|
||||
public Transaction getNameTransaction(String name, int height, String identity) throws Exception;
|
||||
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.StoredBlock;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.store.BlockStore;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
// TODO: breakout the 36000 expiration time into NetworkParameters.
|
||||
|
||||
// TODO: breakout the hash cache into its own class
|
||||
|
||||
// TODO: update blockHashCache with new blocks as they come into the chain
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public class NameLookupByBlockHeightHashCache implements NameLookupByBlockHeight {
|
||||
|
||||
protected BlockChain chain;
|
||||
protected BlockStore store;
|
||||
|
||||
protected NameLookupByBlockHash hashLookup;
|
||||
|
||||
protected ConcurrentHashMap<Integer, Sha256Hash> blockHashCache;
|
||||
|
||||
public NameLookupByBlockHeightHashCache (BlockChain chain, NameLookupByBlockHash hashLookup) throws Exception {
|
||||
this.chain = chain;
|
||||
this.store = chain.getBlockStore();
|
||||
|
||||
this.hashLookup = hashLookup;
|
||||
|
||||
initBlockHashCache();
|
||||
}
|
||||
|
||||
protected void initBlockHashCache() throws BlockStoreException {
|
||||
blockHashCache = new ConcurrentHashMap<Integer, Sha256Hash>(72000);
|
||||
|
||||
StoredBlock blockPointer = chain.getChainHead();
|
||||
|
||||
int headHeight = blockPointer.getHeight();
|
||||
int reorgSafety = 120;
|
||||
int newestHeight = headHeight - reorgSafety;
|
||||
int oldestHeight = headHeight - 36000 - reorgSafety; // 36000 = name expiration
|
||||
|
||||
while (blockPointer.getHeight() >= oldestHeight) {
|
||||
|
||||
if (blockPointer.getHeight() <= newestHeight) {
|
||||
blockHashCache.put(new Integer(blockPointer.getHeight()), blockPointer.getHeader().getHash());
|
||||
}
|
||||
|
||||
blockPointer = blockPointer.getPrev(store);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction getNameTransaction(String name, int height, String identity) throws Exception {
|
||||
|
||||
Sha256Hash blockHash = getBlockHash(height);
|
||||
|
||||
Transaction tx = hashLookup.getNameTransaction(name, blockHash, identity);
|
||||
|
||||
tx.getConfidence().setAppearedAtChainHeight(height); // TODO: test this line
|
||||
tx.getConfidence().setDepthInBlocks(chain.getChainHead().getHeight() - height + 1);
|
||||
|
||||
return tx;
|
||||
|
||||
}
|
||||
|
||||
public Sha256Hash getBlockHash(int height) throws BlockStoreException {
|
||||
Sha256Hash maybeResult = blockHashCache.get(new Integer(height));
|
||||
|
||||
if (maybeResult != null) {
|
||||
return maybeResult;
|
||||
}
|
||||
|
||||
// If we got this far, the block height is uncached.
|
||||
// This could be because the block is immature,
|
||||
// or it could be because the cache is only initialized on initial startup.
|
||||
|
||||
StoredBlock blockPointer = chain.getChainHead();
|
||||
|
||||
while (blockPointer.getHeight() != height) {
|
||||
blockPointer = blockPointer.getPrev(store);
|
||||
}
|
||||
|
||||
return blockPointer.getHeader().getHash();
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public interface NameLookupLatest {
|
||||
|
||||
public Transaction getNameTransaction(String name, String identity) throws Exception;
|
||||
|
||||
}
|
@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2017 Jeremy Rand.
|
||||
* Based on LevelDBBlockStore.java, copyright 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 org.libdohj.names;
|
||||
|
||||
import org.libdohj.script.NameScript;
|
||||
|
||||
import org.bitcoinj.core.AbstractBlockChain;
|
||||
import org.bitcoinj.core.Block;
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.PeerGroup;
|
||||
import org.bitcoinj.core.ScriptException;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.StoredBlock;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
import org.bitcoinj.core.VerificationException;
|
||||
import org.bitcoinj.core.listeners.NewBestBlockListener;
|
||||
import org.bitcoinj.core.listeners.ReorganizeListener;
|
||||
import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.store.BlockStore;
|
||||
import org.bitcoinj.store.BlockStoreException;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
|
||||
import org.fusesource.leveldbjni.*;
|
||||
import org.iq80.leveldb.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
// TODO: dead blocks' name transactions are stored in memory indefinitely. We should probably fix that, although doing so will slow down processing of reorgs.
|
||||
|
||||
public class NameLookupLatestLevelDBTransactionCache implements NameLookupLatest, NewBestBlockListener, ReorganizeListener, TransactionReceivedInBlockListener {
|
||||
|
||||
|
||||
protected static final byte[] CHAIN_HEAD_KEY = "Head".getBytes();
|
||||
protected static final byte[] HEIGHT_KEY = "Height".getBytes();
|
||||
|
||||
protected BlockChain chain;
|
||||
protected BlockStore store;
|
||||
protected PeerGroup peerGroup;
|
||||
|
||||
protected Context context;
|
||||
protected NetworkParameters params;
|
||||
protected File path;
|
||||
|
||||
protected DB db;
|
||||
|
||||
protected SetMultimap<Sha256Hash, Transaction> pendingBlockTransactions = Multimaps.synchronizedSetMultimap(HashMultimap.<Sha256Hash, Transaction>create());
|
||||
|
||||
protected Logger log = LoggerFactory.getLogger(NameLookupLatestLevelDBTransactionCache.class);
|
||||
|
||||
public NameLookupLatestLevelDBTransactionCache (Context context, File directory, BlockChain chain, BlockStore store, PeerGroup peerGroup) throws IOException {
|
||||
this(context, directory, JniDBFactory.factory, chain, store, peerGroup);
|
||||
}
|
||||
|
||||
public NameLookupLatestLevelDBTransactionCache (Context context, File directory, DBFactory dbFactory, BlockChain chain, BlockStore store, PeerGroup peerGroup) throws IOException {
|
||||
this.chain = chain;
|
||||
this.store = store;
|
||||
this.peerGroup = peerGroup;
|
||||
|
||||
this.context = context;
|
||||
this.params = context.getParams();
|
||||
|
||||
this.path = directory;
|
||||
Options options = new Options();
|
||||
options.createIfMissing();
|
||||
|
||||
try {
|
||||
tryOpen(directory, dbFactory, options);
|
||||
} catch (IOException e) {
|
||||
dbFactory.repair(directory, options);
|
||||
tryOpen(directory, dbFactory, options);
|
||||
}
|
||||
|
||||
chain.addNewBestBlockListener(Threading.SAME_THREAD, this);
|
||||
chain.addReorganizeListener(Threading.SAME_THREAD, this);
|
||||
chain.addTransactionReceivedListener(Threading.SAME_THREAD, this);
|
||||
}
|
||||
|
||||
protected void tryOpen(File directory, DBFactory dbFactory, Options options) throws IOException {
|
||||
db = dbFactory.open(directory, options);
|
||||
initStoreIfNeeded();
|
||||
}
|
||||
|
||||
protected synchronized void initStoreIfNeeded() {
|
||||
if (db.get(CHAIN_HEAD_KEY) != null)
|
||||
return; // Already initialised.
|
||||
|
||||
setChainHead(0);
|
||||
}
|
||||
|
||||
protected StoredBlock getSafeBlock(StoredBlock block) throws BlockStoreException {
|
||||
|
||||
StoredBlock result = block;
|
||||
|
||||
int safetyCount;
|
||||
for (safetyCount = 0; safetyCount < 12; safetyCount++) {
|
||||
result = result.getPrev(store);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected synchronized void putBlockChain(StoredBlock block) throws Exception {
|
||||
|
||||
// TODO: use BIP 113 timestamps
|
||||
if ( (new Date().getTime() / 1000 ) - block.getHeader().getTimeSeconds() > 366 * 24 * 60 * 60) {
|
||||
log.debug("NameDB halting walkbalk due to timestamp expiration, height " + block.getHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.getHeight() > getChainHead() + 1) {
|
||||
putBlockChain(block.getPrev(store));
|
||||
}
|
||||
|
||||
putBlock(block);
|
||||
}
|
||||
|
||||
// TODO: try a different peer if downloading a block fails, otherwise we're likely to stall the syncup
|
||||
protected synchronized void putBlock(StoredBlock block) throws Exception {
|
||||
|
||||
Sha256Hash blockHash = block.getHeader().getHash();
|
||||
|
||||
// We might not have the block's transactions already; if we don't, we have to download the block again.
|
||||
// This should be very rare; I'm not actually certain what circumstances would trigger it.
|
||||
// (I guess it would happen if block.transactions is null?)
|
||||
if (! pendingBlockTransactions.containsKey(block.getHeader().getHash())) {
|
||||
log.warn("Transactions missing from block " + blockHash + "; re-downloading block...");
|
||||
|
||||
Block nameFullBlock = peerGroup.getDownloadPeer().getBlock(blockHash).get();
|
||||
|
||||
// The full block hasn't been verified in any way!
|
||||
// So let's do that now.
|
||||
|
||||
if (! nameFullBlock.getHash().equals(blockHash)) {
|
||||
throw new VerificationException("Block hash mismatch!");
|
||||
}
|
||||
|
||||
// Now we know that the received block actually does match the hash that we requested.
|
||||
// However, that doesn't mean that the block's contents are valid.
|
||||
|
||||
final EnumSet<Block.VerifyFlag> flags = EnumSet.noneOf(Block.VerifyFlag.class);
|
||||
nameFullBlock.verify(-1, flags);
|
||||
|
||||
// Now we know that the block is internally valid (including the merkle root).
|
||||
// We haven't verified signature validity, but our threat model is SPV.
|
||||
|
||||
for (Transaction tx : nameFullBlock.getTransactions()) {
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
try {
|
||||
Script scriptPubKey = output.getScriptPubKey();
|
||||
NameScript ns = new NameScript(scriptPubKey);
|
||||
if(ns.isNameOp() && ns.isAnyUpdate() ) {
|
||||
pendingBlockTransactions.put(block.getHeader().getHash(), tx);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
// Our threat model is lightweight SPV, which means we
|
||||
// don't attempt to reject a blockchain due to a single
|
||||
// invalid transaction. As such, if we see a
|
||||
// ScriptException, we just discard the transaction
|
||||
// (and log a warning) rather than rejecting the block.
|
||||
log.warn("Error checking TransactionOutput for name_anyupdate script!", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int height = block.getHeight();
|
||||
|
||||
// See thread safety warning:
|
||||
// https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Multimaps.html#synchronizedMultimap%28com.google.common.collect.Multimap%29
|
||||
synchronized (pendingBlockTransactions) {
|
||||
for (Transaction tx : pendingBlockTransactions.get(block.getHeader().getHash())) {
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
try {
|
||||
Script scriptPubKey = output.getScriptPubKey();
|
||||
NameScript ns = new NameScript(scriptPubKey);
|
||||
if(ns.isNameOp() && ns.isAnyUpdate() ) {
|
||||
putNameScript(scriptPubKey, ns, height);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pendingBlockTransactions.removeAll(block.getHeader().getHash());
|
||||
|
||||
setChainHead(block.getHeight());
|
||||
}
|
||||
|
||||
protected synchronized void putNameScript(Script scriptPubKey, NameScript ns, int height) throws UnsupportedEncodingException {
|
||||
|
||||
// TODO: check if name is relevant (e.g. namespace is id/, has zeronet field)
|
||||
|
||||
// key format:
|
||||
byte[] headerBytes = "NamScr".getBytes("ISO-8859-1");
|
||||
byte[] nameBytes = ns.getOpName().data;
|
||||
|
||||
// record format:
|
||||
// height goes here
|
||||
byte[] scriptBytes = scriptPubKey.getProgram();
|
||||
|
||||
ByteBuffer keyBuffer = ByteBuffer.allocate(headerBytes.length + nameBytes.length);
|
||||
ByteBuffer recordBuffer = ByteBuffer.allocate(4 + scriptBytes.length);
|
||||
|
||||
keyBuffer.put(headerBytes).put(nameBytes);
|
||||
recordBuffer.putInt(height).put(scriptBytes);
|
||||
|
||||
db.put(keyBuffer.array(), recordBuffer.array());
|
||||
}
|
||||
|
||||
// TODO: stop duplicating code from the other NameLookupLatest implementations
|
||||
protected void verifyHeightTrustworthy(int height) throws IllegalArgumentException, VerificationException {
|
||||
if (height < 1) {
|
||||
throw new IllegalArgumentException("Nonpositive block height; not trustworthy!");
|
||||
}
|
||||
|
||||
int headHeight = chain.getChainHead().getHeight();
|
||||
|
||||
int confirmations = headHeight - height + 1;
|
||||
|
||||
// TODO: optionally use transaction chains (with signature checks) to verify transactions without 12 confirmations
|
||||
// TODO: the above needs to be optional, because some applications (e.g. cert transparency) require confirmations
|
||||
if (confirmations < 12) {
|
||||
throw new VerificationException("Block does not yet have 12 confirmations; not trustworthy!");
|
||||
}
|
||||
|
||||
// TODO: check for off-by-one errors on this line
|
||||
if (confirmations >= 36000) {
|
||||
throw new VerificationException("Block has expired; not trustworthy!");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make a new Exception class
|
||||
@Override
|
||||
public Transaction getNameTransaction(String name, String identity) throws Exception {
|
||||
|
||||
byte[] headerBytes = "NamScr".getBytes("ISO-8859-1");
|
||||
byte[] nameBytes = name.getBytes("ISO-8859-1");
|
||||
// name goes here
|
||||
|
||||
ByteBuffer keyBuffer = ByteBuffer.allocate(headerBytes.length + nameBytes.length);
|
||||
keyBuffer.put(headerBytes).put(nameBytes);
|
||||
|
||||
byte[] recordBytes = db.get(keyBuffer.array());
|
||||
if (recordBytes == null)
|
||||
return null;
|
||||
|
||||
ByteBuffer recordBuffer = ByteBuffer.wrap(recordBytes);
|
||||
|
||||
int height = recordBuffer.getInt();
|
||||
|
||||
verifyHeightTrustworthy(height);
|
||||
|
||||
byte[] scriptPubKeyBytes = Arrays.copyOfRange(recordBytes, 4, recordBytes.length);
|
||||
|
||||
Transaction tx = new Transaction(params);
|
||||
Script scriptPubKey = new Script(scriptPubKeyBytes);
|
||||
tx.addOutput(Coin.CENT, scriptPubKey);
|
||||
|
||||
tx.getConfidence().setAppearedAtChainHeight(height); // TODO: test this line
|
||||
tx.getConfidence().setDepthInBlocks(chain.getChainHead().getHeight() - height + 1);
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
protected synchronized int getChainHead() {
|
||||
return ByteBuffer.wrap(db.get(CHAIN_HEAD_KEY)).getInt();
|
||||
}
|
||||
|
||||
protected synchronized void setChainHead(int chainHead) {
|
||||
db.put(CHAIN_HEAD_KEY, ByteBuffer.allocate(4).putInt(chainHead).array());
|
||||
}
|
||||
|
||||
public synchronized void close() throws IOException {
|
||||
db.close();
|
||||
}
|
||||
|
||||
/** Erases the contents of the database (but NOT the underlying files themselves) and then reinitialises with the genesis block. */
|
||||
protected synchronized void reset() throws IOException {
|
||||
WriteBatch batch = db.createWriteBatch();
|
||||
try {
|
||||
DBIterator it = db.iterator();
|
||||
try {
|
||||
it.seekToFirst();
|
||||
while (it.hasNext())
|
||||
batch.delete(it.next().getKey());
|
||||
db.write(batch);
|
||||
} finally {
|
||||
it.close();
|
||||
}
|
||||
} finally {
|
||||
batch.close();
|
||||
}
|
||||
initStoreIfNeeded();
|
||||
}
|
||||
|
||||
protected synchronized void destroy() throws IOException {
|
||||
JniDBFactory.factory.destroy(path, new Options());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNewBestBlock (StoredBlock block) throws VerificationException {
|
||||
// TODO: use BIP 113 timestamps
|
||||
if ( (new Date().getTime() / 1000 ) - block.getHeader().getTimeSeconds() > 366 * 24 * 60 * 60) {
|
||||
log.debug("NameDB skipping block at height " + block.getHeight() + " due to timestamp " + block.getHeader().getTimeSeconds());
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("NameDB started processing new best block at height " + block.getHeight());
|
||||
|
||||
try {
|
||||
putBlockChain(getSafeBlock(block));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("NameDB Exception while processing new best block", e);
|
||||
throw new VerificationException(e);
|
||||
}
|
||||
|
||||
log.debug("NameDB finished processing new best block at height " + block.getHeight());
|
||||
}
|
||||
|
||||
// WARNING: in a reorg that is at least 12 blocks deep, any names updated in the old blocks that aren't updated in the new blocks will remain in their old state in the database.
|
||||
// That is incorrect behavior, but it usually isn't advantageous to an attacker.
|
||||
// In certain applications where proof of existence is used, this incorrect behavior could allow a true existence claim to be accepted,
|
||||
// even though the rest of the network will incorrectly reject it.
|
||||
// I don't see any other significant attacks here. Have I missed something?
|
||||
// If we're really worried about this, the "right" solution is to either store name history in the database,
|
||||
// or redownload all of the last 36 kiloblocks.
|
||||
@Override
|
||||
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException {
|
||||
// TODO: use BIP 113 timestamps
|
||||
if ( (new Date().getTime() / 1000 ) - newBlocks.get(0).getHeader().getTimeSeconds() > 366 * 24 * 60 * 60) {
|
||||
return;
|
||||
}
|
||||
|
||||
setChainHead(splitPoint.getHeight() - 12);
|
||||
|
||||
try {
|
||||
putBlockChain(getSafeBlock(newBlocks.get(0)));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Exception during NameDB reorganize", e);
|
||||
throw new VerificationException(e);
|
||||
}
|
||||
|
||||
log.warn("Finished NameDB reorganize, height " + newBlocks.get(0).getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException {
|
||||
// TODO: use BIP 113 timestamps
|
||||
if ( (new Date().getTime() / 1000 ) - block.getHeader().getTimeSeconds() > 366 * 24 * 60 * 60) {
|
||||
log.debug("NameDB skipping new transaction at height " + block.getHeight() + " due to timestamp " + block.getHeader().getTimeSeconds());
|
||||
return;
|
||||
}
|
||||
|
||||
for (TransactionOutput output : tx.getOutputs()) {
|
||||
try {
|
||||
Script scriptPubKey = output.getScriptPubKey();
|
||||
NameScript ns = new NameScript(scriptPubKey);
|
||||
// Always save the coinbase, because it lets us identify that we've received the contents of the block, even if it has no name_anyupdate operations.
|
||||
// TODO: maybe save a null reference instead of the actual coinbase tx, since this would cut down on memory usage very slightly.
|
||||
if(tx.isCoinBase() || ( ns.isNameOp() && ns.isAnyUpdate() ) ) {
|
||||
log.debug("NameDB temporarily storing name transaction until it gets more confirmations.");
|
||||
pendingBlockTransactions.put(block.getHeader().getHash(), tx);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
// Our threat model is lightweight SPV, which means we
|
||||
// don't attempt to reject a blockchain due to a single
|
||||
// invalid transaction. As such, if we see a
|
||||
// ScriptException, we just discard the transaction
|
||||
// (and log a warning) rather than rejecting the block.
|
||||
log.warn("Error checking TransactionOutput for name_anyupdate script!", e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add optional FilteredBlock support
|
||||
@Override
|
||||
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public class NameLookupLatestRestHeightApi implements NameLookupLatest {
|
||||
|
||||
protected BlockChain chain;
|
||||
protected NameLookupByBlockHeight heightLookup;
|
||||
protected String restUrlPrefix;
|
||||
protected String restUrlSuffix;
|
||||
|
||||
public NameLookupLatestRestHeightApi (String restUrlPrefix, String restUrlSuffix, BlockChain chain, NameLookupByBlockHeight heightLookup) {
|
||||
this.restUrlPrefix = restUrlPrefix;
|
||||
this.restUrlSuffix = restUrlSuffix;
|
||||
this.chain = chain;
|
||||
this.heightLookup = heightLookup;
|
||||
}
|
||||
|
||||
// TODO: make a new Exception class
|
||||
@Override
|
||||
public Transaction getNameTransaction(String name, String identity) throws Exception {
|
||||
|
||||
int height = getHeight(name);
|
||||
|
||||
return heightLookup.getNameTransaction(name, height, identity);
|
||||
|
||||
}
|
||||
|
||||
// TODO: break out the getHeight into its own class + interface
|
||||
// TODO: add identity isolation
|
||||
// TODO: use an older height if the newest height has insufficient confirmations, instead of throwing an Exception
|
||||
// NOTE: this might fail if special characters are in the name, since it's not URL-escaping them.
|
||||
public int getHeight(String name) throws Exception {
|
||||
ArrayList<NameData> untrustedNameHistory = getUntrustedNameHistory(name);
|
||||
|
||||
int height;
|
||||
|
||||
int index;
|
||||
|
||||
for (index = untrustedNameHistory.size() - 1; index >= 0; index--) {
|
||||
|
||||
height = untrustedNameHistory.get(index).height;
|
||||
try {
|
||||
verifyHeightTrustworthy(height);
|
||||
return height;
|
||||
}
|
||||
catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Height not trustworthy or name does not exist.");
|
||||
}
|
||||
|
||||
// TODO: add identity isolation
|
||||
protected ArrayList<NameData> getUntrustedNameHistory(String name) throws Exception {
|
||||
URL nameUrl = new URL(restUrlPrefix + name + restUrlSuffix);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
ArrayList<NameData> untrustedNameHistory = new ArrayList<NameData>(Arrays.asList(mapper.readValue(nameUrl, NameData[].class)));
|
||||
|
||||
return untrustedNameHistory;
|
||||
}
|
||||
|
||||
protected void verifyHeightTrustworthy(int height) throws Exception {
|
||||
if (height < 1) {
|
||||
throw new Exception("Nonpositive block height; not trustworthy!");
|
||||
}
|
||||
|
||||
int headHeight = chain.getChainHead().getHeight();
|
||||
|
||||
int confirmations = headHeight - height + 1;
|
||||
|
||||
// TODO: optionally use transaction chains (with signature checks) to verify transactions without 12 confirmations
|
||||
// TODO: the above needs to be optional, because some applications (e.g. cert transparency) require confirmations
|
||||
if (confirmations < 12) {
|
||||
throw new Exception("Block does not yet have 12 confirmations; not trustworthy!");
|
||||
}
|
||||
|
||||
// TODO: check for off-by-one errors on this line
|
||||
if (confirmations >= 36000) {
|
||||
throw new Exception("Block has expired; not trustworthy!");
|
||||
}
|
||||
}
|
||||
|
||||
static protected class NameData {
|
||||
|
||||
public String name;
|
||||
public String value;
|
||||
public String txid;
|
||||
public String address;
|
||||
public int expires_in;
|
||||
public int height;
|
||||
|
||||
@JsonCreator
|
||||
public NameData(@JsonProperty("name") String name,
|
||||
@JsonProperty("value") String value,
|
||||
@JsonProperty("txid") String txid,
|
||||
@JsonProperty("address") String address,
|
||||
@JsonProperty("expires_in") int expires_in,
|
||||
@JsonProperty("height") int height) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.txid = txid;
|
||||
this.address = address;
|
||||
this.expires_in = expires_in;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,191 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.Block;
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.MerkleBranch;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.store.BlockStore;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
// TODO: document this
|
||||
|
||||
public class NameLookupLatestRestMerkleApi implements NameLookupLatest {
|
||||
|
||||
protected NetworkParameters params;
|
||||
protected BlockChain chain;
|
||||
protected BlockStore store;
|
||||
protected NameLookupByBlockHeightHashCache heightLookup; // only needed for the hash cache
|
||||
protected String restUrlPrefix;
|
||||
protected String restUrlSuffix;
|
||||
|
||||
// TODO: break out the hash cache into its own class so that we don't need the NameLookup features.
|
||||
public NameLookupLatestRestMerkleApi (NetworkParameters params, String restUrlPrefix, String restUrlSuffix, BlockChain chain, BlockStore store, NameLookupByBlockHeightHashCache heightLookup) {
|
||||
this.params = params;
|
||||
this.restUrlPrefix = restUrlPrefix;
|
||||
this.restUrlSuffix = restUrlSuffix;
|
||||
this.chain = chain;
|
||||
this.store = store;
|
||||
this.heightLookup = heightLookup;
|
||||
}
|
||||
|
||||
// TODO: make a new Exception class
|
||||
@Override
|
||||
public Transaction getNameTransaction(String name, String identity) throws Exception {
|
||||
|
||||
NameData data = getLatestUntrustedNameData(name);
|
||||
|
||||
Sha256Hash blockHash = heightLookup.getBlockHash(data.height);
|
||||
|
||||
Block blockHeader = store.get(blockHash).getHeader();
|
||||
|
||||
// Convert merkle hashes from String to Sha256Hash
|
||||
ArrayList<Sha256Hash> merkleHashes = new ArrayList<Sha256Hash>(data.mrkl_branch.size());
|
||||
for (String merkleHashString : data.mrkl_branch) {
|
||||
merkleHashes.add(Sha256Hash.wrap(merkleHashString));
|
||||
}
|
||||
|
||||
long merkleBranchSideMask = data.tx_idx;
|
||||
|
||||
MerkleBranch branch = new MerkleBranch(params, null, merkleHashes, merkleBranchSideMask);
|
||||
|
||||
Transaction tx = new Transaction(params, Utils.HEX.decode(data.rawtx));
|
||||
|
||||
Sha256Hash txId = tx.getHash();
|
||||
|
||||
if(! blockHeader.getMerkleRoot().equals(branch.calculateMerkleRoot(txId))) {
|
||||
throw new Exception("Merkle proof failed to verify!");
|
||||
}
|
||||
|
||||
tx.getConfidence().setAppearedAtChainHeight(data.height); // TODO: test this line
|
||||
tx.getConfidence().setDepthInBlocks(chain.getChainHead().getHeight() - data.height + 1);
|
||||
|
||||
if (NameTransactionUtils.getNameAnyUpdateOutput(tx, name) == null) {
|
||||
throw new Exception("Not a name_anyupdate transaction or wrong name!");
|
||||
}
|
||||
|
||||
return tx;
|
||||
|
||||
}
|
||||
|
||||
// TODO: break out the getHeight into its own class + interface
|
||||
// TODO: add identity isolation
|
||||
// TODO: use an older height if the newest height has insufficient confirmations, instead of throwing an Exception
|
||||
// NOTE: this might fail if special characters are in the name, since it's not URL-escaping them.
|
||||
public NameData getLatestUntrustedNameData(String name) throws Exception {
|
||||
ArrayList<NameData> untrustedNameHistory = getUntrustedNameHistory(name);
|
||||
|
||||
int height;
|
||||
|
||||
int index;
|
||||
|
||||
for (index = untrustedNameHistory.size() - 1; index >= 0; index--) {
|
||||
|
||||
NameData candidate = untrustedNameHistory.get(index);
|
||||
try {
|
||||
verifyHeightTrustworthy(candidate.height);
|
||||
return candidate;
|
||||
}
|
||||
catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Height not trustworthy or name does not exist.");
|
||||
}
|
||||
|
||||
// TODO: add identity isolation
|
||||
protected ArrayList<NameData> getUntrustedNameHistory(String name) throws Exception {
|
||||
URL nameUrl = new URL(restUrlPrefix + name + restUrlSuffix);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
ArrayList<NameData> untrustedNameHistory = new ArrayList<NameData>(Arrays.asList(mapper.readValue(nameUrl, NameData[].class)));
|
||||
|
||||
return untrustedNameHistory;
|
||||
}
|
||||
|
||||
protected void verifyHeightTrustworthy(int height) throws Exception {
|
||||
if (height < 1) {
|
||||
throw new Exception("Nonpositive block height; not trustworthy!");
|
||||
}
|
||||
|
||||
int headHeight = chain.getChainHead().getHeight();
|
||||
|
||||
int confirmations = headHeight - height + 1;
|
||||
|
||||
// TODO: optionally use transaction chains (with signature checks) to verify transactions without 12 confirmations
|
||||
// TODO: the above needs to be optional, because some applications (e.g. cert transparency) require confirmations
|
||||
if (confirmations < 12) {
|
||||
throw new Exception("Block does not yet have 12 confirmations; not trustworthy!");
|
||||
}
|
||||
|
||||
// TODO: check for off-by-one errors on this line
|
||||
if (confirmations >= 36000) {
|
||||
throw new Exception("Block has expired; not trustworthy!");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: break this out into its own class; add the extra fields to bitcoinj-addons too
|
||||
static protected class NameData {
|
||||
|
||||
public String name;
|
||||
public String value;
|
||||
public String txid;
|
||||
public String address;
|
||||
public int expires_in;
|
||||
public int height;
|
||||
public long tx_idx;
|
||||
public ArrayList<String> mrkl_branch;
|
||||
public String rawtx;
|
||||
|
||||
@JsonCreator
|
||||
public NameData(@JsonProperty("name") String name,
|
||||
@JsonProperty("value") String value,
|
||||
@JsonProperty("txid") String txid,
|
||||
@JsonProperty("address") String address,
|
||||
@JsonProperty("expires_in") int expires_in,
|
||||
@JsonProperty("height") int height,
|
||||
@JsonProperty("tx_idx") long tx_idx,
|
||||
@JsonProperty("mrkl_branch") ArrayList<String> mrkl_branch,
|
||||
@JsonProperty("rawtx") String rawtx) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.txid = txid;
|
||||
this.address = address;
|
||||
this.expires_in = expires_in;
|
||||
this.height = height;
|
||||
this.tx_idx = tx_idx;
|
||||
this.mrkl_branch = mrkl_branch;
|
||||
this.rawtx = rawtx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Jeremy Rand.
|
||||
*
|
||||
* 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 org.libdohj.names;
|
||||
|
||||
import org.bitcoinj.core.BlockChain;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.store.BlockStore;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
// This lookup client only downloads a single transaction from the API rather than a history.
|
||||
// This means that it's usually faster, but the API has to be careful to choose the correct transaction.
|
||||
// As of writing (2016 Jun 26), webbtc does *not* always make the correct choice.
|
||||
// That means that using this lookup client will result in an incorrect "nonexistent" result
|
||||
// if the latest name_update for the targeted name has a depth between 1 and 11 (inclusive).
|
||||
// I'm engaging with Marius from webbtc and hope to have a solution soon.
|
||||
// -- Jeremy
|
||||
|
||||
public class NameLookupLatestRestMerkleApiSingleTx extends NameLookupLatestRestMerkleApi {
|
||||
|
||||
public NameLookupLatestRestMerkleApiSingleTx (NetworkParameters params, String restUrlPrefix, String restUrlSuffix, BlockChain chain, BlockStore store, NameLookupByBlockHeightHashCache heightLookup) {
|
||||
super(params, restUrlPrefix, restUrlSuffix, chain, store, heightLookup);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayList<NameData> getUntrustedNameHistory(String name) throws Exception {
|
||||
URL nameUrl = new URL(restUrlPrefix + name + restUrlSuffix);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
NameData[] untrustedNameSingleEntry = {mapper.readValue(nameUrl, NameData.class)};
|
||||
ArrayList<NameData> untrustedNameHistory = new ArrayList<NameData>(Arrays.asList(untrustedNameSingleEntry));
|
||||
|
||||
return untrustedNameHistory;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user