inital commit, already did quite a few things.

This commit is contained in:
pablo 2021-12-28 12:31:58 +01:00
commit a3619df8cf
6 changed files with 2321 additions and 0 deletions

6
README.md Normal file
View file

@ -0,0 +1,6 @@
# Chatochi notes
My notes on running bitcoin and lighting nodes, as well as in learning more
about learning more through Mastering Bitcoin and Mastering the Lightning
Network.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,100 @@
from typing import List, Dict, Tuple
def get_words_from_file(file_path) -> Tuple[dict, dict]:
with open(file_path, "r") as words_file:
lines_contents = words_file.read().splitlines()
words = {index: word for index, word in enumerate(lines_contents)}
indexes = {word: index for index, word in enumerate(lines_contents)}
return words, indexes
WORDS, INDEXES = get_words_from_file("bip39-english-words.txt")
class MnemonicWord:
def __init__(self, word: str):
word = word.lstrip().rstrip().lower()
if word not in WORDS.values():
raise ValueError(f"'{word}' is not a valid bip39 word.")
self.word = word
def as_int(self):
return INDEXES[self.word]
def as_binary(self):
return bin(INDEXES[self.word])
class MnemonicPhrase:
allowed_phrase_lengths = [12, 18, 24]
def __init__(self, words: List[MnemonicWord]):
if len(words) not in self.allowed_phrase_lengths:
raise ValueError(
f"The mnemonic phrase length should be one of {self.allowed_phrase_lengths}, but it was {len(words)}"
)
self.words = {index: word for index, word in enumerate(words)}
def __iter__(self):
for index, word in self.words.items():
yield word
def __getitem__(self, item):
return self.words[item]
def as_bin(self):
binary_representation = "0b" + "".join(
[word.as_binary()[2:] for word in self]
)
return binary_representation
def as_int(self):
return int(
self.as_bin(), base=2
)
class Seed:
def __init__(self, seed_as_int: int):
self._seed_as_int = seed_as_int
@property
def as_mnemonic(self) -> List[str]:
return self._seed_as_mnemonic(self._seed)
pass
@property
def as_integer(self) -> int:
return self._seed_as_int
@property
def as_binary(self) -> str:
return bin(self._seed_as_int)
@property
def as_hex(self) -> str:
return hex(self._seed_as_int)
@classmethod
def from_mnemonic(cls, mnemonic_phrase: MnemonicPhrase):
seed_as_int = mnemonic_phrase.as_int()
return cls(seed_as_int)
@classmethod
def from_binary(cls, binary_seed_representation: str):
return cls(int(binary_seed_representation, base=2))
@classmethod
def from_integer(cls, int_seed_representation: int):
return cls(int_seed_representation)
@classmethod
def from_hex(cls, hex_seed_representation: str):
return cls(int(hex_seed_representation, base=16))

View file

@ -0,0 +1,101 @@
import pytest
from seed_playground import WORDS, MnemonicWord, MnemonicPhrase
def is_a_bin_num_with_0b(possible_bin_num):
allowed_chars = {"0", "1", "b"}
if possible_bin_num[:2] == "0b" and set(possible_bin_num) <= allowed_chars:
return True
return False
@pytest.fixture()
def twelve_valid_words():
raw_words = [
"primary",
"find",
"roof",
"forget",
"subject",
"present",
"pipe",
"primary",
"flavor",
"grant",
"remain",
"present",
]
mnemonic_words = [MnemonicWord(a_word) for a_word in raw_words]
return mnemonic_words
@pytest.fixture()
def a_valid_mnemonic_phrase(twelve_valid_words):
return MnemonicPhrase(twelve_valid_words)
def test_reading_the_file_works():
there_are_2048_words = len(WORDS) == 2048
first_word_is_abandon = WORDS[0] == "abandon"
last_word_is_zoo = WORDS[2047] == "zoo"
word_1957_is_virus = WORDS[1956] == "virus"
assert (
there_are_2048_words
and first_word_is_abandon
and last_word_is_zoo
and word_1957_is_virus
)
def test_passing_bip39_valid_word_instantiates_mnemonic_word_correctly():
word = MnemonicWord("abandon")
assert isinstance(word, MnemonicWord)
def test_passing_random_word_to_mnemonic_word_raises_exception():
with pytest.raises(ValueError):
word = MnemonicWord("Machiavelli")
def test_mnemonic_word_can_be_represented_as_binary():
word = MnemonicWord("abandon")
word_as_bin = word.as_binary()
assert is_a_bin_num_with_0b(word_as_bin)
def test_mnemonic_word_can_be_represented_as_int():
word = MnemonicWord("abandon")
word_as_int = word.as_int()
assert isinstance(word_as_int, int)
def test_mnemonic_phrase_instantiates_properly_with_12_words(
twelve_valid_words
):
phrase = MnemonicPhrase(twelve_valid_words)
assert isinstance(phrase, MnemonicPhrase)
def test_mnemonic_phrase_fails_with_13_words(twelve_valid_words):
twelve_valid_words.append(MnemonicWord("abandon"))
thirteen_valid_words = twelve_valid_words
with pytest.raises(ValueError):
phrase = MnemonicPhrase(thirteen_valid_words)
def test_mnemonic_phrase_is_iterable(a_valid_mnemonic_phrase):
for word in a_valid_mnemonic_phrase:
word
def test_mnemonic_phrase_as_bin_returns_a_valid_bin(a_valid_mnemonic_phrase):
bin_representation = a_valid_mnemonic_phrase.as_bin()
assert is_a_bin_num_with_0b(bin_representation)

View file

@ -0,0 +1,65 @@
# Mastering Bitcoin notes
So, after years of messing around with Bitcoin, the time has finally come to
reach the bottom of the rabbit hole and understand things down to the code
level.
I'm going to use this document to keep my notes and thoughts while going
through the book.
----------
"There's a lot more to Bitcoin than first meets the eye." Joder, ya te digo.
----------
Finally, I get to discover what the hell is an `SPV`: simplified payment
verification, which is fancy pants language for a client that keeps keys and
performs operations but relies on another full-node for following the protocol
and getting the blockchain data from other peers. So, if I understand
correctly, an Electrum Personal Server fits the definition of an SPV.
----------
Andreas mentions that we should talk about "mnemonic phrase", and not a "seed
phrase", because "even though common, its use is incorrect". But, why? -> So
it works this way: technically, the seed is a 512-bit piece of data, which is
the actual piece of information used to generate the keys of a wallet. The
mnemonic is just a human-readeable proxy to this seed, hence why mnemonic !=
seed.
This sparked my curiosity and I have been reading more low level details on how
seed generation works.
The first thing that's required is randomness. To get this, a series of bits is
generated. Specifically, the entropy should be between 128 and 256 bits (that
means, 128 to 256 random zeros and ones).
The checksum for this entropy is computed the following way:
- Generate the SHA256 hash of the entropy.
- Starting from the left, grab one bit of the hash for every 32 bits of length
in the original entropy (if the entropy is 128 bits, 128/32 = 4, you grab the 4
first bits of the hash).
```python
entropy = "01000010011111001011100101101111001010010110101011011111000001101000110111111001010000101000000110011010110011110110010000011001"
entropy_len = len(entropy)
import hashlib
hash_of_entropy = hashlib.sha256(entropy)
print(hash_of_entropy)
```
----------
Another interesting fact: miner fees for a transaction are not explicitly
specified anywhere, but are simply calculated as the difference between the
inputs and the outputs of the transaction.

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
pytest