Browse Source

Fix support for Namecoin and Dogecoin, add Zcash support

Closes #83
master
John L. Jegutanis 8 years ago
committed by Neil Booth
parent
commit
bc31df8ee0
  1. 70
      lib/coins.py
  2. 64
      lib/tx.py
  3. 6
      tests/test_addresses.py

70
lib/coins.py

@ -39,7 +39,7 @@ from hashlib import sha256
import lib.util as util import lib.util as util
from lib.hash import Base58, hash160, double_sha256, hash_to_str from lib.hash import Base58, hash160, double_sha256, hash_to_str
from lib.script import ScriptPubKey from lib.script import ScriptPubKey
from lib.tx import Deserializer, DeserializerSegWit from lib.tx import Deserializer, DeserializerSegWit, DeserializerAuxPow, DeserializerZcash
Block = namedtuple("Block", "header transactions") Block = namedtuple("Block", "header transactions")
@ -293,6 +293,23 @@ class Coin(object):
def deserializer(cls): def deserializer(cls):
return Deserializer return Deserializer
class CoinAuxPow(Coin):
# Set NAME and NET to avoid exception in Coin::lookup_coin_class
NAME = ""
NET = ""
STATIC_BLOCK_HEADERS = False
@classmethod
def header_hash(cls, header):
'''Given a header return hash'''
return double_sha256(header[:cls.BASIC_HEADER_SIZE])
@classmethod
def block_header(cls, block, height):
'''Return the AuxPow block header bytes'''
block = DeserializerAuxPow(block)
return block.read_header(height, cls.BASIC_HEADER_SIZE)
class Bitcoin(Coin): class Bitcoin(Coin):
NAME = "Bitcoin" NAME = "Bitcoin"
@ -451,7 +468,7 @@ class LitecoinTestnetSegWit(LitecoinTestnet):
# Source: namecoin.org # Source: namecoin.org
class Namecoin(Coin): class Namecoin(CoinAuxPow):
NAME = "Namecoin" NAME = "Namecoin"
SHORTNAME = "NMC" SHORTNAME = "NMC"
NET = "mainnet" NET = "mainnet"
@ -484,7 +501,7 @@ class NamecoinTestnet(Namecoin):
# For DOGE there is disagreement across sites like bip32.org and # For DOGE there is disagreement across sites like bip32.org and
# pycoin. Taken from bip32.org and bitmerchant on github # pycoin. Taken from bip32.org and bitmerchant on github
class Dogecoin(Coin): class Dogecoin(CoinAuxPow):
NAME = "Dogecoin" NAME = "Dogecoin"
SHORTNAME = "DOGE" SHORTNAME = "DOGE"
NET = "mainnet" NET = "mainnet"
@ -682,3 +699,50 @@ class FairCoin(Coin):
'timestamp': timestamp, 'timestamp': timestamp,
'creatorId': creatorId, 'creatorId': creatorId,
} }
class Zcash(Coin):
NAME = "Zcash"
SHORTNAME = "ZEC"
NET = "mainnet"
XPUB_VERBYTES = bytes.fromhex("0488b21e")
XPRV_VERBYTES = bytes.fromhex("0488ade4")
P2PKH_VERBYTE = bytes.fromhex("1CB8")
P2SH_VERBYTE = bytes.fromhex("1CBD")
WIF_BYTE = bytes.fromhex("80")
GENESIS_HASH = ('00040fe8ec8471911baa1db1266ea15d'
'd06b4a8a5c453883c000b031973dce08')
STATIC_BLOCK_HEADERS = False
BASIC_HEADER_SIZE = 140 # Excluding Equihash solution
TX_COUNT = 329196
TX_COUNT_HEIGHT = 68379
TX_PER_BLOCK = 5
IRC_PREFIX = "E_"
IRC_CHANNEL = "#electrum-zcash"
RPC_PORT = 8232
REORG_LIMIT = 800
@classmethod
def electrum_header(cls, header, height):
version, = struct.unpack('<I', header[:4])
timestamp, bits = struct.unpack('<II', header[100:108])
return {
'block_height': height,
'version': version,
'prev_block_hash': hash_to_str(header[4:36]),
'merkle_root': hash_to_str(header[36:68]),
'timestamp': timestamp,
'bits': bits,
'nonce': hash_to_str(header[108:140]),
}
@classmethod
def block_header(cls, block, height):
'''Return the block header bytes'''
block = DeserializerZcash(block)
return block.read_header(height, cls.BASIC_HEADER_SIZE)
@classmethod
def deserializer(cls):
return DeserializerZcash

64
lib/tx.py

@ -238,3 +238,67 @@ class DeserializerSegWit(Deserializer):
return TxSegWit(version, marker, flag, inputs, return TxSegWit(version, marker, flag, inputs,
outputs, witness, locktime), double_sha256(orig_ser) outputs, witness, locktime), double_sha256(orig_ser)
class DeserializerAuxPow(Deserializer):
VERSION_AUXPOW = (1 << 8)
def read_header(self, height, static_header_size):
'''Return the AuxPow block header bytes'''
start = self.cursor
version = self._read_le_uint32()
if version & self.VERSION_AUXPOW:
# We are going to calculate the block size then read it as bytes
self.cursor = start
self.cursor += static_header_size # Block normal header
self.read_tx() # AuxPow transaction
self.cursor += 32 # Parent block hash
merkle_size = self._read_varint()
self.cursor += 32 * merkle_size # Merkle branch
self.cursor += 4 # Index
merkle_size = self._read_varint()
self.cursor += 32 * merkle_size # Chain merkle branch
self.cursor += 4 # Chain index
self.cursor += 80 # Parent block header
header_end = self.cursor
else:
header_end = static_header_size
self.cursor = start
return self._read_nbytes(header_end)
class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")):
'''Class representing a JoinSplit transaction.'''
@cachedproperty
def is_coinbase(self):
return self.inputs[0].is_coinbase if len(self.inputs) > 0 else False
class DeserializerZcash(Deserializer):
def read_header(self, height, static_header_size):
'''Return the block header bytes'''
start = self.cursor
# We are going to calculate the block size then read it as bytes
self.cursor += static_header_size
solution_size = self._read_varint()
self.cursor += solution_size
header_end = self.cursor
self.cursor = start
return self._read_nbytes(header_end)
def read_tx(self):
start = self.cursor
base_tx = TxJoinSplit(
self._read_le_int32(), # version
self._read_inputs(), # inputs
self._read_outputs(), # outputs
self._read_le_uint32() # locktime
)
if base_tx.version >= 2:
joinsplit_size = self._read_varint()
if joinsplit_size > 0:
self.cursor += joinsplit_size * 1802 # JSDescription
self.cursor += 32 # joinSplitPubKey
self.cursor += 64 # joinSplitSig
return base_tx, double_sha256(self.binary[start:self.cursor])

6
tests/test_addresses.py

@ -26,7 +26,7 @@
import pytest import pytest
from lib.coins import Litecoin, Bitcoin from lib.coins import Litecoin, Bitcoin, Zcash
from lib.hash import Base58 from lib.hash import Base58
addresses = [ addresses = [
@ -38,6 +38,10 @@ addresses = [
"206168f5322583ff37f8e55665a4789ae8963532", "b8cb80b26e8932f5b12a7e"), "206168f5322583ff37f8e55665a4789ae8963532", "b8cb80b26e8932f5b12a7e"),
(Litecoin, "3GxRZWkJufR5XA8hnNJgQ2gkASSheoBcmW", (Litecoin, "3GxRZWkJufR5XA8hnNJgQ2gkASSheoBcmW",
"a773db925b09add367dcc253c1f9bbc1d11ec6fd", "062d8515e50cb92b8a3a73"), "a773db925b09add367dcc253c1f9bbc1d11ec6fd", "062d8515e50cb92b8a3a73"),
(Zcash, "t1LppKe1sfPNDMysGSGuTjxoAsBcvvSYv5j",
"206168f5322583ff37f8e55665a4789ae8963532", "b8cb80b26e8932f5b12a7e"),
(Zcash, "t3Zq2ZrASszCg7oBbio7oXqnfR6dnSWqo76",
"a773db925b09add367dcc253c1f9bbc1d11ec6fd", "062d8515e50cb92b8a3a73"),
] ]

Loading…
Cancel
Save