Skip to content

Commit

Permalink
Add Network unique field concept
Browse files Browse the repository at this point in the history
  • Loading branch information
esneider committed May 16, 2015
1 parent e787963 commit 3680aa5
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 56 deletions.
File renamed without changes.
127 changes: 71 additions & 56 deletions bitforge/network.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
from collections import namedtuple
import collections

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from errors import BitforgeError, KeyValueError, StringError

from bitforge import error


class Error(error.BitforgeError):
pass


class UnknownNetwork(Error, error.KeyValueError):
"""No network with {key} matching '{value}'"""


class InvalidNetwork(Error, error.KeyValueError):
"""The network {key} '{value}' is already taken"""


class InvalidField(Error, error.StringError):
"""The field {string} is not unique and thus, not searchable"""


BaseNetwork = collections.namedtuple('Network', [

# TODO: document these fields
# TODO: which should be unique? It should be enforced
BaseNetwork = namedtuple('Network', [
# Descriptors
'name',
'aliases',
'name', # Main network name
'aliases', # All the network aliases

# Cryptography parameters
'curve',
'curve', # Elliptic curve used for the crypto
'hash_function', # Signature hashing function

# Serialization magic numbers
'pubkeyhash',
'wif_prefix',
'wif_prefix', # Byte prefix used to identify the network in the WIF encoding
'scripthash',
'hd_public_key',
'hd_private_key',
'magic',

# Network parameters
'port',
'seeds',
Expand All @@ -27,79 +49,74 @@
class Network(BaseNetwork):
"""Parameters of a Bitcoin-compatible network."""

class Error(BitforgeError):
pass
UNIQUE_FIELDS = ['name', 'wif_prefix']

class UnknownNetwork(Error, KeyValueError):
"""No network with '{key}' matching '{value}'"""

class MultipleNetworks(Error, KeyValueError):
"""Multiple networks with '{key}' matching '{value}'"""

class ExistingNetworkName(Error, StringError):
"""Network name '{string}' already taken"""

_networks = {}
_networks = []
_networks_by_name = {}

@classmethod
def get(cls, name, raises=True):
"""Get a network by name or alias."""

network = cls._networks.get(name, None)
network = cls._networks_by_name.get(name, None)

if raises and network is None:
raise Network.UnknownNetwork('name', name)
raise UnknownNetwork('name', name)

return network

# TODO: discuss if this method should exist at all
@classmethod
def get_by_field(cls, field, value, raises=True):
"""Get a network uniquely determined by a field."""

matches = []
if field not in cls.UNIQUE_FIELDS:
if raises:
raise InvalidField(field)
return None

for network in cls._networks:
if getattr(network, field, None) == value:
matches.append(network)

if raises and len(matches) == 0:
raise Network.UnknownNetwork(field, value)
if getattr(network, field) == value:
return network

if raises and len(matches) > 1:
raise Network.MultipleNetworks(field, value)

if len(matches) == 1:
return matches[0]
if raises:
raise UnknownNetwork(field, value)

def __new__(cls, **kwargs):
network = super(Network, cls).__new__(cls, **kwargs)
network._register()

# Enforce all unique fields
for other in cls._networks:
for field in cls.UNIQUE_FIELDS:
if getattr(other, field) == getattr(network, field):
raise InvalidNetwork(field, getattr(network, field))

# Enforce uniqueness of the network aliases
for name in [network.name] + network.aliases:
if name in cls._networks_by_name:
raise InvalidNetwork('name', name)
cls._networks_by_name[name] = network

return network

def _register(self):
"""Register a network by its unique name and aliases."""
def __repr__(self):
return '<Network name: {}>'.format(self.name)

for name in [self.name] + self.aliases:
if name in self._networks:
raise Network.ExistingNetworkName(name)
self._networks[name] = self
def __str__(self):
return self.name


testnet = Network(
name = 'testnet',
aliases = [],

curve = ec.SECP256K1,

hash_function = hashes.SHA256,
pubkeyhash = 111,
wif_prefix = 239,
scripthash = 196,
hd_public_key = 0x043587cf,
hd_private_key = 0x04358394,
magic = 0x0b110907,

port = 18333,
hd_public_key = 0x043587cf,
hd_private_key = 0x04358394,
magic = 0x0b110907,
port = 18333,
seeds = [
'testnet-seed.bitcoin.petertodd.org',
'testnet-seed.bluematt.me',
Expand All @@ -112,17 +129,15 @@ def _register(self):
default = livenet = Network(
name = 'livenet',
aliases = ['mainnet', 'default'],

curve = ec.SECP256K1,

hash_function = hashes.SHA256,
pubkeyhash = 0x00,
wif_prefix = 0x80,
scripthash = 0x05,
hd_public_key = 0x0488b21e,
hd_private_key = 0x0488ade4,
magic = 0xf9beb4d9,

port = 8333,
hd_public_key = 0x0488b21e,
hd_private_key = 0x0488ade4,
magic = 0xf9beb4d9,
port = 8333,
seeds = [
'seed.bitcoin.sipa.be',
'dnsseed.bluematt.me',
Expand Down

0 comments on commit 3680aa5

Please sign in to comment.