Using zCash with BitcoinJ seems a reasonable advance for me since I need to monitor various currencies for an academic project. Currently, I run a full node with zCash along with BitcoinJ for Bitcoin and Litecoin using network params from the Dogecoin developers.
Out of the circumstance that zCash uses a lot of Bitcoin's Codebase, I think this might be compatible, but unfortunately, I'm unable to get it to work alone.
Here's my approach to ZcashMainNetParams:-
public class ZcashMainNetParams extends AbstractZcashMainNetParams { public static final int MAINNET_MAJORITY_WINDOW = 1000; public static final int MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = 950; public static final int MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 750; public ZcashMainNetParams() { super(); interval = INTERVAL; targetTimespan = TARGET_TIMESPAN; maxTarget = Utils.decodeCompactBits(0x1d00ffffL); dumpedPrivateKeyHeader = 128; addressHeader = 0; p2shHeader = 5; acceptableAddressCodes = new int[] { addressHeader, p2shHeader }; port = 8233; packetMagic = 0xf9beb4d9 /* netmagic same as BTC ??? */; bip32HeaderPub = 0x0488B21E; //The 4 byte header that serializes in base58 to "xpub". bip32HeaderPriv = 0x0488ADE4; //The 4 byte header that serializes in base58 to "xprv" majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE; majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED; majorityWindow = MAINNET_MAJORITY_WINDOW; genesisBlock = createGenesis(this); id = ID_MAINNET; subsidyDecreaseBlockCount = 210000; spendableCoinbaseDepth = 100; String genesisHash = genesisBlock.getHashAsString(); checkState(genesisHash.equals("00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08"), genesisHash); /* updated */ //
This contains (at a minimum) the blocks which are not BIP30 compliant. BIP30 changed how duplicate // transactions are handled. Duplicated transactions could occur in the case where a coinbase had the same // extraNonce and the same outputs but appeared at different heights and greatly complicated re-org handling. // Having these here simplifies block connection logic considerably. checkpoints.put(2500,
Sha256Hash.wrap("00000006dc968f600be11a86cbfbf7feb61c7577f45caced2e82b6d261d19744")) /* updated */; checkpoints.put(15000, Sha256Hash.wrap("00000000b6bc56656812a5b8dcad69d6ad4446dec23b5ec456c18641fb5381ba")) /* updated */; checkpoints.put(67500, Sha256Hash.wrap("000000006b366d2c1649a6ebb4787ac2b39c422f451880bc922e3a6fbd723616")) /* updated */; dnsSeeds = new String[] { "dnsseed.z.cash", // Zcash "dnsseed.str4d.xyz", // @str4d "dnsseed.znodes.org" // @bitcartel }; } private static AltcoinBlock createGenesis(NetworkParameters params) { AltcoinBlock genesisBlock = new AltcoinBlock(params, 4L); Transaction t = new Transaction(params); try { byte[] bytes = Hex.decode ("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"); t.addInput(new TransactionInput(params, t, bytes)); ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream(); Script.writeBytes(scriptPubKeyBytes, Hex.decode ("000a889f00854b8665cd555f4656f68179d31ccadc1b1f7fb0952726313b16941da348284d67add4686121d4e3d930160c1348d8191c25f12b267a6a9c131b5031cbf8af1f79c9d513076a216ec87ed045fa966e01214ed83ca02dc1797270a454720d3206ac7d931a0a680c5c5e099057592570ca9bdf6058343958b31901fce1a15a4f38fd347750912e14004c73dfe588b903b6c03166582eeaf30529b14072a7b3079e3a684601b9b3024054201f7440b0ee9eb1a7120ff43f713735494aa27b1f8bab60d7f398bca14f6abb2adbf29b04099121438a7974b078a11635b594e9170f1086140b4173822dd697894483e1c6b4e8b8dcd5cb12ca4903bc61e108871d4d915a9093c18ac9b02b6716ce1013ca2c1174e319c1a570215bc9ab5f7564765f7be20524dc3fdf8aa356fd94d445e05ab165ad8bb4a0db096c097618c81098f91443c719416d39837af6de85015dca0de89462b1d8386758b2cf8a99e00953b308032ae44c35e05eb71842922eb69797f68813b59caf266cb6c213569ae3280505421a7e3a0a37fdf8e2ea354fc5422816655394a9454bac542a9298f176e211020d63dee6852c40de02267e2fc9d5e1ff2ad9309506f02a1a71a0501b16d0d36f70cdfd8de78116c0c506ee0b8ddfdeb561acadf31746b5a9dd32c21930884397fb1682164cb565cc14e089d66635a32618f7eb05fe05082b8a3fae620571660a6b89886eac53dec109d7cbb6930ca698a168f301a950be152da1be2b9e07516995e20baceebecb5579d7cdbc16d09f3a50cb3c7dffe33f26686d4ff3f8946ee6475e98cf7b3cf9062b6966e838f865ff3de5fb064a37a21da7bb8dfd2501a29e184f207caaba364f36f2329a77515dcb710e29ffbf73e2bbd773fab1f9a6b005567affff605c132e4e4dd69f36bd201005458cfbd2c658701eb2a700251cefd886b1e674ae816d3f719bac64be649c172ba27a4fd55947d95d53ba4cbc73de97b8af5ed4840b659370c556e7376457f51e5ebb66018849923db82c1c9a819f173cccdb8f3324b239609a300018d0fb094adf5bd7cbb3834c69e6d0b3798065c525b20f040e965e1a161af78ff7561cd874f5f1b75aa0bc77f720589e1b810f831eac5073e6dd46d00a2793f70f7427f0f798f2f53a67e615e65d356e66fe40609a958a05edb4c175bcc383ea0530e67ddbe479a898943c6e3074c6fcc252d6014de3a3d292b03f0d88d312fe221be7be7e3c59d07fa0f2f4029e364f1f355c5d01fa53770d0cd76d82bf7e60f6903bc1beb772e6fde4a70be51d9c7e03c8d6d8dfb361a234ba47c470fe630820bbd920715621b9fbedb49fcee165ead0875e6c2b1af16f50b5d6140cc981122fcbcf7c5a4e3772b3661b628e08380abc545957e59f634705b1bbde2f0b4e055a5ec5676d859be77e20962b645e051a880fddb0180b4555789e1f9344a436a84dc5579e2553f1e5fb0a599c137be36cabbed0319831fea3fddf94ddc7971e4bcf02cdc93294a9aab3e3b13e3b058235b4f4ec06ba4ceaa49d675b4ba80716f3bc6976b1fbf9c8bf1f3e3a4dc1cd83ef9cf816667fb94f1e923ff63fef072e6a19321e4812f96cb0ffa864da50ad74deb76917a336f31dce03ed5f0303aad5e6a83634f9fcc371096f8288b8f02ddded5ff1bb9d49331e4a84dbe1543164438fde9ad71dab024779dcdde0b6602b5ae0a6265c14b94edd83b37403f4b78fcd2ed555b596402c28ee81d87a909c4e8722b30c71ecdd861b05f61f8b1231795c76adba2fdefa451b283a5d527955b9f3de1b9828e7b2e74123dd47062ddcc09b05e7fa13cb2212a6fdbc65d7e852cec463ec6fd929f5b8483cf3052113b13dac91b69f49d1b7d1aec01c4a68e41ce157")); scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG); t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray())); } catch (Exception e) { // Cannot happen. throw new RuntimeException(e); } genesisBlock.addTransaction(t); genesisBlock.setTime(1477641360L); genesisBlock.setDifficultyTarget(504365040L); genesisBlock.setNonce(0x0000000000000000000000000000000000000000000000000000000000001257L); return genesisBlock; } private static ZcashMainNetParams instance; public static synchronized ZcashMainNetParams get() { if (instance == null) { instance = new ZcashMainNetParams(); } return instance; } @Override public String getPaymentProtocolId() { return PAYMENT_PROTOCOL_ID_MAINNET; } }
The abstract class is also re-used from the dogecoin developers:
public abstract class AbstractZcashMainNetParams extends NetworkParameters { /** * Scheme part for Bitcoin URIs. */ public static final String BITCOIN_SCHEME = "zcash"; public static final int REWARD_HALVING_INTERVAL = 210000; private static final Logger log = LoggerFactory.getLogger(AbstractZcashMainNetParams.class); public AbstractZcashMainNetParams() { super(); } /** * Checks if we are at a reward halving point. * @param height The height of the previous stored block * @return If this is a reward halving point */ public final boolean isRewardHalvingPoint(final int height) { return ((height + 1) % REWARD_HALVING_INTERVAL) == 0; } /** * Checks if we are at a difficulty transition point. * @param height The height of the previous stored block * @return If this is a difficulty transition point */ public final boolean isDifficultyTransitionPoint(final int height) { return ((height + 1) % this.getInterval()) == 0; } @Override public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock, final BlockStore blockStore) throws VerificationException, BlockStoreException { final Block prev = storedPrev.getHeader(); // Is this supposed to be a difficulty transition point? if (!isDifficultyTransitionPoint(storedPrev.getHeight())) { // No ... so check the difficulty didn't actually change. if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget()) throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() + ": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " + Long.toHexString(prev.getDifficultyTarget())); return; } // We need to find a block far back in the chain. It's OK that this is expensive because it only occurs every // two weeks after the initial block chain download. final Stopwatch watch = Stopwatch.createStarted(); Sha256Hash hash = prev.getHash(); StoredBlock cursor = null; final int interval = this.getInterval(); for (int i = 0; i < interval; i++) { cursor = blockStore.get(hash); if (cursor == null) { // This should never happen. If it does, it means we are following an incorrect or busted chain. throw new VerificationException( "Difficulty transition point but we did not find a way back to the last transition point. Not found: " + hash); } hash = cursor.getHeader().getPrevBlockHash(); } checkState(cursor != null && isDifficultyTransitionPoint(cursor.getHeight() - 1), "Didn't arrive at a transition point."); watch.stop(); if (watch.elapsed(TimeUnit.MILLISECONDS) > 50) log.info("Difficulty transition traversal took {}", watch); Block blockIntervalAgo = cursor.getHeader(); int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds()); // Limit the adjustment step. final int targetTimespan = this.getTargetTimespan(); if (timespan < targetTimespan / 4) timespan = targetTimespan / 4; if (timespan > targetTimespan * 4) timespan = targetTimespan * 4; BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget()); newTarget = newTarget.multiply(BigInteger.valueOf(timespan)); newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan)); if (newTarget.compareTo(this.getMaxTarget()) > 0) { log.info("Difficulty hit proof of work limit: {}", newTarget.toString(16)); newTarget = this.getMaxTarget(); } int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3; long receivedTargetCompact = nextBlock.getDifficultyTarget(); // The calculated difficulty is to a higher precision than received, so reduce here. BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8); newTarget = newTarget.and(mask); long newTargetCompact = Utils.encodeCompactBits(newTarget); if (newTargetCompact != receivedTargetCompact) throw new VerificationException("Network provided difficulty bits do not match what was calculated: " + Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact)); } @Override public Coin getMaxMoney() { return MAX_MONEY; } @Override public Coin getMinNonDustOutput() { return Transaction.MIN_NONDUST_OUTPUT; } @Override public MonetaryFormat getMonetaryFormat() { return new MonetaryFormat(); } @Override public int getProtocolVersionNum(final ProtocolVersion version) { return version.getBitcoinProtocolVersion(); } @Override public BitcoinSerializer getSerializer(boolean parseRetain) { return new BitcoinSerializer(this, parseRetain); } @Override public String getUriScheme() { return BITCOIN_SCHEME; } @Override public boolean hasMaxMoney() { return true; } }
My Problem here is basically the generation of the genesis block. I'm unable to forge it with the right hash. I believe it has to do with the transaction I try to understand in the chainparams.cpp of zCash. However my genesis block has the hash of e88b11fd3581e170f86db9c574f65c0ada3216e126011ac968869f1b64ea4c4a instead of the required 00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08
Thanks for any kind of help, even including a reasonable conclusion that this is not possible.