New blspy, key formats, and new plot format (#309)

* Bump blspy-ietf
* wip lots of errors
* Respond to bram feedback, use eip 2334 for keys
* Fix most tests, use latest blspy
* Improve simulation test constants
* Fixed issue with plotting and reading plots
* Fix simulation test
* Override puzzle hash if we don't have key, for safety. Bump protocol version
* Fix superlint issue
* Switch to 1.0 proof of space format
* Temporary genesis block
* Update chiabip158, chiavdf versions

Co-authored-by: Gene Hoffman <hoffmang@hoffmang.com>
Co-authored-by: Alex Wice <alex@chia.net>
This commit is contained in:
Mariano Sorgente 2020-07-13 09:40:32 -07:00 committed by Gene Hoffman
parent 8dca011f5e
commit 194e0c24ea
64 changed files with 663 additions and 716 deletions

View File

@ -41,8 +41,12 @@ timelord.sanitizer_mode = True, the timelord will work on those challenges.
- Improved create_plots and check_plots scripts, which are now "chia plots create" and "chia plots check"
- Add plot directories with "chia plots add"
- Use real plot sizes in UI instead of formula
- Use chacha proof of space **(not on master yet)**
- Use chacha/blake3 proof of space **(not on master yet)**
- Plot refreshing happens during all new challenges, and only new/modified files are read
- Updated blspy and use new IETF standard for bls signatures
- HD keys now use EIP 2333 format instead of BIP32, for compatibility with other chains
- Keys are now derived with the EIP 2334 (m/12381/8444/a/b).
- Removed the ability to pass in sk_seed to plotting, to increase security
### Fixed
- uPnP now works on Windows.

View File

@ -8618,9 +8618,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash._reinterpolate": {
"version": "3.0.0",

View File

@ -189,11 +189,11 @@ export const handle_message = (store, payload) => {
}
} else if (payload.command === "get_private_key") {
const text =
"Extended private key: " +
payload.data.private_key.esk +
"Private key: " +
payload.data.private_key.sk +
"\n" +
"Extended public key: " +
payload.data.private_key.epk +
"Public key: " +
payload.data.private_key.pk +
"\n" +
(payload.data.private_key.seed
? "seed: " + payload.data.private_key.seed

View File

@ -55,7 +55,7 @@ class Block extends Component {
super(props);
this.state = {
headerHash: "",
plotSeed: ""
plotId: ""
};
}
@ -75,10 +75,10 @@ class Block extends Component {
hex_to_array(this.props.block.proof_of_space.plot_public_key)
);
const bufHash = await sha256(buf);
const plotSeed = arr_to_hex(bufHash);
const plotId = arr_to_hex(bufHash);
this.setState({
headerHash,
plotSeed
plotId
});
}
@ -109,7 +109,7 @@ class Block extends Component {
diff = block.header.data.weight - prevHeader.data.weight;
}
const headerHash = "0x" + this.state.headerHash;
const plotSeed = "0x" + this.state.plotSeed;
const plotId = "0x" + this.state.plotId;
const chia_cb = chia_formatter(
parseFloat(calculate_block_reward(block.header.data.height)),
@ -159,8 +159,8 @@ class Block extends Component {
{ name: "Plot Public Key", value: block.proof_of_space.plot_public_key },
{ name: "Pool Public Key", value: block.proof_of_space.pool_public_key },
{
name: "Plot Seed",
value: plotSeed,
name: "Plot Id",
value: plotId,
tooltip:
"The seed used to create the plot, this depends on the pool pk and plot pk"
},

View File

@ -386,7 +386,7 @@ const Plots = props => {
<TableRow>
<TableCell>Filename</TableCell>
<TableCell align="right">Size</TableCell>
<TableCell align="right">Plot seed</TableCell>
<TableCell align="right">Plot id</TableCell>
<TableCell align="right">Plot pk</TableCell>
<TableCell align="right">Pool pk</TableCell>
<TableCell align="right">Delete</TableCell>

View File

@ -57,7 +57,7 @@ describe("header", () => {
total_iters: "168608294357",
weight: "3520789508784128"
},
harvester_signature:
plot_signature:
"0x53d04fa0ee31bdc249e6db50161a6dc09d1c48ad011533b10a3eda25eb456a8b3562a1509b97f1931e334d5ed8ec6593133ecbbec5c423b733ea3cdfccc3022a7c09b0fc87fd4d0fd6cbfbedbb69d9fd43aab5c07e3452cb1f91524678340b50"
});

View File

@ -19,11 +19,11 @@ export async function hash_header(header) {
buf = buf.concat(
big_int_to_array(BigInt(header.data.pool_target.max_height), 4)
);
buf = buf.concat(hex_to_array(header.data.aggregated_signature.sig));
buf = buf.concat(hex_to_array(header.data.aggregated_signature));
buf = buf.concat(big_int_to_array(BigInt(header.data.cost), 8));
buf = buf.concat(hex_to_array(header.data.extension_data));
buf = buf.concat(hex_to_array(header.data.generator_hash));
buf = buf.concat(hex_to_array(header.harvester_signature));
buf = buf.concat(hex_to_array(header.plot_signature));
let hash = await sha256(buf);
return arr_to_hex(hash);

View File

@ -3,10 +3,10 @@ from setuptools import setup
dependencies = [
"aiter==0.13.20191203", # Used for async generator tools
"blspy==0.2b1", # Signature library
"chiavdf==0.3b12", # timelord and vdf verification
"chiabip158==0.1b1", # bip158-style wallet filters
"chiapos==0.1b1", # proof of space
"blspy==0.2c0", # Signature library
"chiavdf==0.12.19", # timelord and vdf verification
"chiabip158==0.15", # bip158-style wallet filters
"chiapos==0.1b2", # proof of space
"clvm==0.4", # contract language
"clvm-tools==0.1.1", # clvm compiler tools
"aiohttp==3.6.2", # HTTP server for full node rpc

View File

@ -7,8 +7,8 @@ from src.util.keychain import Keychain
from src.util.config import unflatten_properties
from pathlib import Path
from src.types.BLSSignature import BLSPublicKey
from src.consensus.coinbase import create_puzzlehash_for_pk
from src.util.ints import uint32
from src.util.config import (
config_path_for_filename,
@ -21,6 +21,7 @@ from src.util.path import mkdir
import yaml
from src.ssl.create_ssl import generate_selfsigned_cert
from src.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_pool_sk
def make_parser(parser: ArgumentParser):
@ -42,19 +43,31 @@ def dict_add_new_default(
def check_keys(new_root):
keychain: Keychain = Keychain()
all_pubkeys = keychain.get_all_public_keys()
if len(all_pubkeys) == 0:
all_sks = keychain.get_all_private_keys()
if len(all_sks) == 0:
print(
"No keys are present in the keychain. Generate them with 'chia keys generate'"
)
return
all_child_pubkeys = [epk.public_child(0).get_public_key() for epk in all_pubkeys]
all_targets = [
create_puzzlehash_for_pk(BLSPublicKey(bytes(pk))).hex()
for pk in all_child_pubkeys
]
config: Dict = load_config(new_root, "config.yaml")
pool_child_pubkeys = [master_sk_to_pool_sk(sk).get_g1() for sk, _ in all_sks]
all_targets = []
stop_searching_for_farmer = "xch_target_puzzle_hash" not in config["farmer"]
stop_searching_for_pool = "xch_target_puzzle_hash" not in config["pool"]
for i in range(500):
if stop_searching_for_farmer and stop_searching_for_pool and i > 0:
break
for sk, _ in all_sks:
all_targets.append(
create_puzzlehash_for_pk(
master_sk_to_wallet_sk(sk, uint32(i)).get_g1()
).hex()
)
if all_targets[-1] == config["farmer"].get("xch_target_puzzle_hash"):
stop_searching_for_farmer = True
if all_targets[-1] == config["pool"].get("xch_target_puzzle_hash"):
stop_searching_for_pool = True
# Set the destinations
if "xch_target_puzzle_hash" not in config["farmer"]:
@ -63,31 +76,36 @@ def check_keys(new_root):
)
config["farmer"]["xch_target_puzzle_hash"] = all_targets[0]
elif config["farmer"]["xch_target_puzzle_hash"] not in all_targets:
assert len(config["farmer"]["xch_target_puzzle_hash"]) == 64
print(
"WARNING: farmer using a puzzle hash which we don't have the private keys for"
f"WARNING: farmer using a puzzle hash which we don't have the private"
f" keys for. Overriding "
f"{config['farmer']['xch_target_puzzle_hash']} with {all_targets[0]}"
)
config["farmer"]["xch_target_puzzle_hash"] = all_targets[0]
if "pool" in config:
if "xch_target_puzzle_hash" not in config["pool"]:
print(
f"Setting the xch destination address for coinbase reward to {all_targets[0]}"
)
config["pool"]["xch_target_puzzle_hash"] = all_targets[0]
elif config["pool"]["xch_target_puzzle_hash"] not in all_targets:
assert len(config["pool"]["xch_target_puzzle_hash"]) == 64
print(
"WARNING: pool using a puzzle hash which we don't have the private keys for"
)
if "pool" not in config:
config["pool"] = {}
if "xch_target_puzzle_hash" not in config["pool"]:
print(
f"Setting the xch destination address for coinbase reward to {all_targets[0]}"
)
config["pool"]["xch_target_puzzle_hash"] = all_targets[0]
elif config["pool"]["xch_target_puzzle_hash"] not in all_targets:
print(
f"WARNING: pool using a puzzle hash which we don't have the private"
f" keys for. Overriding "
f"{config['pool']['xch_target_puzzle_hash']} with {all_targets[0]}"
)
config["pool"]["xch_target_puzzle_hash"] = all_targets[0]
# Set the pool pks in the farmer
all_pubkeys_hex = set([bytes(pk).hex() for pk in all_child_pubkeys])
pool_pubkeys_hex = set(bytes(pk).hex() for pk in pool_child_pubkeys)
if "pool_public_keys" in config["farmer"]:
for pk_hex in config["farmer"]["pool_public_keys"]:
# Add original ones in config
all_pubkeys_hex.add(pk_hex)
pool_pubkeys_hex.add(pk_hex)
config["farmer"]["pool_public_keys"] = all_pubkeys_hex
config["farmer"]["pool_public_keys"] = pool_pubkeys_hex
save_config(new_root, "config.yaml", config)

View File

@ -6,7 +6,12 @@ from src.util.keychain import (
Keychain,
bytes_from_mnemonic,
)
from src.types.BLSSignature import BLSPublicKey
from src.wallet.derive_keys import (
master_sk_to_pool_sk,
master_sk_to_farmer_sk,
master_sk_to_wallet_sk,
)
from src.util.ints import uint32
from src.consensus.coinbase import create_puzzlehash_for_pk
command_list = [
@ -99,8 +104,8 @@ def add_private_key_seed(mnemonic):
try:
entropy = bytes_from_mnemonic(mnemonic)
passphrase = ""
esk = keychain.add_private_key(entropy, passphrase)
fingerprint = esk.get_public_key().get_fingerprint()
sk = keychain.add_private_key(entropy, passphrase)
fingerprint = sk.get_g1().get_fingerprint()
print(
f"Added private key with public key fingerprint {fingerprint} and mnemonic"
)
@ -139,23 +144,29 @@ def show_all_keys():
print("Showing all private keys:")
for sk, seed in private_keys:
print("")
print("Fingerprint:", sk.get_public_key().get_fingerprint())
print("Extended Public key:", sk.get_extended_public_key())
print("Public key:", sk.get_public_key())
addr = create_puzzlehash_for_pk(
BLSPublicKey(sk.public_child(0).get_public_key())
).hex()
print("First address:", addr)
print("Extended private key:", bytes(sk).hex())
if seed is not None:
mnemonic = bytes_to_mnemonic(seed)
mnemonic_string = mnemonic_to_string(mnemonic)
print("Mnemonic seed:")
print(mnemonic_string)
else:
print(
"There is no mnemonic for this key, since it was imported without a seed. (Or migrated from keys.yaml)."
)
print("Fingerprint:", sk.get_g1().get_fingerprint())
print("Master public key (m):", sk.get_g1())
print("Master private key (m):", bytes(sk).hex())
print(
"Farmer public key (m/12381/8444/0/0)::",
master_sk_to_farmer_sk(sk).get_g1(),
)
print("Pool public key (m/12381/8444/1/0):", master_sk_to_pool_sk(sk).get_g1())
print(
"First wallet key (m/12381/8444/2/0):",
master_sk_to_wallet_sk(sk, uint32(0)).get_g1(),
)
print(
"First wallet address:",
create_puzzlehash_for_pk(
master_sk_to_wallet_sk(sk, uint32(0)).get_g1()
).hex(),
)
assert seed is not None
mnemonic = bytes_to_mnemonic(seed)
mnemonic_string = mnemonic_to_string(mnemonic)
print(" Mnemonic seed:")
print(mnemonic_string)
def delete(args):

View File

@ -1,23 +1,21 @@
import blspy
from blspy import G1Element, PrivateKey, G2Element, AugSchemeMPL
from src.types.sized_bytes import bytes32
from src.util.ints import uint64, uint32
from src.types.coin import Coin
from src.types.BLSSignature import BLSSignature, BLSPublicKey
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
from src.util.hash import std_hash
def create_puzzlehash_for_pk(pub_key: BLSPublicKey) -> bytes32:
return puzzle_for_pk(pub_key).get_tree_hash()
def create_puzzlehash_for_pk(pub_key: G1Element) -> bytes32:
return puzzle_for_pk(bytes(pub_key)).get_tree_hash()
def signature_for_coinbase(coin: Coin, pool_private_key: blspy.PrivateKey):
message_hash = blspy.Util.hash256(bytes(coin))
return BLSSignature(bytes(pool_private_key.sign_prepend_prehashed(message_hash)))
def signature_for_coinbase(coin: Coin, pool_private_key: PrivateKey):
return G2Element.from_bytes(bytes(AugSchemeMPL.sign(pool_private_key, bytes(coin))))
def sign_coinbase_coin(coin: Coin, private_key: blspy.PrivateKey):
def sign_coinbase_coin(coin: Coin, private_key: PrivateKey):
if private_key is None:
raise ValueError("unknown private key")
return signature_for_coinbase(coin, private_key)

View File

@ -74,7 +74,7 @@ testnet_kwargs = {
"NUMBER_OF_HEADS": 3, # The number of tips each full node keeps track of and propagates
# DIFFICULTY_STARTING is the starting difficulty for the first epoch, which is then further
# multiplied by another factor of 2^32, to be used in the VDF iter calculation formula.
"DIFFICULTY_STARTING": 2 ** 20,
"DIFFICULTY_STARTING": 2 ** 15,
"DIFFICULTY_FACTOR": 3, # The next difficulty is truncated to range [prev / FACTOR, prev * FACTOR]
# These 3 constants must be changed at the same time
"DIFFICULTY_EPOCH": 256, # The number of blocks per epoch
@ -89,7 +89,7 @@ testnet_kwargs = {
# will be contant and required for all blocks.
"MIN_ITERS_PROPORTION": 10,
# For the first epoch, since we have no previous blocks, we can't estimate vdf iterations per second
"MIN_ITERS_STARTING": (2 ** 22),
"MIN_ITERS_STARTING": (2 ** 20),
"MAX_FUTURE_TIME": 7200, # The next block can have a timestamp of at most these many seconds more
"NUMBER_OF_TIMESTAMPS": 11, # Than the average of the last NUMBEBR_OF_TIMESTAMPS blocks
# If an unfinished block is more than these many seconds slower than the best unfinished block,
@ -100,7 +100,7 @@ testnet_kwargs = {
"PROPAGATION_DELAY_THRESHOLD": 1500,
# Hardcoded genesis block, generated using tests/block_tools.py
# Replace this any time the constants change.
"GENESIS_BLOCK": b"\x00\x00Q\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x08fc\x9e\xd4w,K\x91v\xf61:\xbe\x9a\x0b\xe9w\xb9\x01\x19\xb0\xd4\x8d\x9f\x0b\xac\r;km\xc2\x04}Y\xbd\x86\x82s\xca`A\xc6\xbc\xc3\x0b\xbb\xb7\x14p\xfbI\xda\xc5\xd0\x02\xbbr\x1eV\x86\xaeN\x84\xfdo\x00\xfeL\xba\x987o*\x95\x8b\x8a\xb2G\xc4\x90g_\x15DL;\xa5E\x03\x8c\xbb\xf0\xbe,\x11\x14\x00\x00\x00\xa0^H\xf1\x1a\xd0E\x945\xf8}c\x8d\xc3\x9a\x03\x1c\xeaO\xe3R\"=\xaaM\xb4\xd9\xee1`\xde\xf1\xc4\xef\xa4#`\xe3\xe3K\x0fE\xa9n\xe9j\x98\xef\xcdP0\xb8t,?wL\xc8\x84H\xd0\xd1\xef\xbd\xa1i}L\xe8\n\x1ep\x1e\xe0hK\xeb\xb7\xc2\xd4,\x89y\x99w\x86\x8aF\xf1KV\x18V\x9a\xb6\xc2\xc6\x19k\xf5\xa6|\x03\xc0c\xc5\x9fQ\x0e\xc6n\xde\x8b\xa6\x01\x83\xa8\xaa\xe5\x84j\x1e\xfa7&\x06\x83\xd9k\x01$\x9f\x1f\xb3\xf7d\xec\xd5\xef\x0f\x9b\x17:\xb6'\xaf\x84\n\x0fS\x1e\xd5\xdab\xa9}|\xce\x1b\x9a\x05\x01\x00\x00Q\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x00\x00\x00\x00\x00ra\x12\x00S\x91\xbe{\xbb\xceU\x9e\xfe\xaf\xc8\"\x87p\xb5+S\xaeE']\xee\"LNV\xe1\xcd]\x95\xa4\xfd=\xec\xe3L\x16)\xdc\x01\x06\x15\x8d\xee\xeb\xd8\xe6\x13Q1i\xa07\xea\xdarohH\x9e\xf6\xd0\xb6p\xff\xaf\x1f\xa83z7\x86j2\x11i\xbb{\x89\xd6\x94\xb2\x97\xad\xf7U\x03B\xfb\x89\x8f\xebS\xcc\\\x17\xec\xd6.s\xd0Ba\xca\xa0\x14(\xff\xf5\xdcAP<\x8e\x19_\x15\x1b\xc3\xaa>\xd7\xb0\xc4.!R_\x13\x00\x00\x00\x00\x82\x00Tq\xeef\x8e\xbb\x90\xc4\xc5\x96\x98\xbd\xfd\x89\xb5\xcc\x82\x1b\x8e\xe6>\xd2\x18\x8f\x1e\x81iaT\xa0\x9c\xa0\xec3r\xc1\x7f\x90\x10\x0e\xf5@\xbb\xc4\x8bI\xff\xa8\x064c?\xba2\xb0]\x98z\x06\xad/\xfc\xbe\xc5\x00\x11Xu1'\x14^\xdc\xd5\xd7\xacwd\xe8\x16\x07\x85f\x90\xe7\xf8\xe8\ti\xbfQ\x05\xdf\xf2\xfc\xf7Z\xf3]\x97Z\xa0O\xef\x95T'\x10\xe1\xbe\x7f\xdb\x0cj\x1a\xc3\xb4\x96X\x88\x05\x1e~\xd0\x83\x08J\xd3k\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\xfe\x83G\xd8)\x8e\x0e\xe4\xe4_Ac\xc1\x9d\x16z\x13[\xd4\\*Fy\xd6\xa7Z\xfb\xea\x9b[\x8d\x185Q\xa0Mo\xc8V\x04\xce\x7f7\x15\xfd~\xd4\xb4\xd7\xafMH\xc9\xb9,\x88 \x84\x8b@\x1f\xcaB\x99\xa5`\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00ra\x12\xab\x80\xcb\xf0\x0b\xf3w\xe1\x14\xe4\xef\x85o\x82\xa6\x97\x0b\xa8\x1e\x94S\xa4\xb9D\xa2\xe5`l\xaeL\xbbz\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x94B\x19af\x95\xe4\x8fz\x9bT\xb3\x88w\x10J\x1f_\xbe\x85\xc6\x1d\xa2\xfb\xe3RuA\x8ad\xbc\x00\x00\x01\xd1\xa9J \x000\x94B\x19af\x95\xe4\x8fz\x9bT\xb3\x88w\x10J\x1f_\xbe\x85\xc6\x1d\xa2\xfb\xe3RuA\x8ad\xbc\x00\x00\x00\x00Y\xe6\xb2@\x86t\xd0\x01vq\x8al\x93y\x85\x8f\xb6\xdcb\xd6d\x89@\xda\x8eD\"\x10u\x1a\x82o\xe5\x1e\xe3\xc7\x0ceb\xbc\xb7_&\x87\xb4\r\x9d\xe2\t\xe4\xe9\xbe$\t\x19|\x95N\xb2\xeaU\xf0\xb9\x02\xe3\xbc\xe34Z1\xb2\xa6rM\xf24G\xa2\x14\xe9\xc4\xd0\xea\x1c\xdd\xf1\xc1\xb1\x04p?\x89\xa8\x1c8\xf2\x00\x00\x00\x00\x00\x00\x00\x00$\xaa\xdc\xcb\xa8\x83o\xa2\xcb\xaaf\x9c\x14\xcd_Bn-\xfe\xde:(\xe2\x05\xf4\xbaM\t|\x8d\x8cb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xce`\xb5\x1fd\x9brcf\x17\xf1\x18]\x82\x95L\x01U\xa8\x9bZY\x16\xc7\x83\x95\xf4\xfa\xfe\x8f0\xf8\xe2TM\x1fV\xc0\xa1\xdd8\x8b\xb1y\x1d\x0e>\xee\x0b\xd9\xdd\t&\xa7\xf8%\xf3\xf4\xa4\xcc\xa3\xf4\xb3m\xae\x8fe\xb0\xc4\x8a\x08^F\x96\x86\xee\xadV\xcb\xeb5R19\xd91N\x12G\xbe\xdbr\x8a\xc3'\xcc\x00\x00\x00\x00\x04\x01@\x02\x18", # noqa: E501
"GENESIS_BLOCK": b'\x00\x00Q\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x8d\xd5D\xaf\xf6\x90\xf7Ac\xff\x00[9\x1b\x8dI\xeb\xcb\xf5\xa9\xb2qK\\\xfb\x01\xb4R\xff\x07|a\xae\x03\xee/(\xb0\xe5\x01\x98\x01\xa7\xe6\xeb\xfd\xf2.\xa6\t_\xd9\xe4\x00\xa9_\xe3\x9a\xfc[M\x04\xd3\xa82\x94\xe1H\xd7;Q\xb4\x9c"i?\xd9\xf9\xa95\xb8\xd2j\xe1\x11\x11\x06\x16l\xf6I\xd1\xedl\x8c\xa9\x12\x00\x00\x00\x90\xb5=\xe9\r\xe8\x01#\xac\xaa\x04\xdd\xf7RF\xcc"[\x91\x93\xad0\x14\xe2\xb8(\x1a\xe8Z\xa0\xad\x07\xcb]\xd8\x05uu\t0\xfc\xc8\x8a\xf1\xab*\xe8\x05-l\x98]\xd9g\x81\xa8\xd1\x96{\x009)\xf0\x9c\x04b\xef\xach\x851\x90\x15KT\\\xe4\x9f\xf5\x82:$\xde\x13\xe8j\x12\xae\x878N\x8dN\xa4(\xff]\xf65:.\xcaH\x1f\xec\xc6\xde\x14\t\t\xb9\x8e\xa5\x16\x93\xe9\xae\x01ibh\xff\x06\xefXr\x93\xa8#\xb40\x05-\x07\xfdu3\xb309\x00\xbf|"\x83\x01\x00\x00Q\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x16\x00\x00\x00\x00\x00\xd5\xc9\x82\x00\x14\xe9\xd0\x16\x83a\xf6$6\xc2\x92o\xc3\xdf\xda~J\xcae\x06\x00b\xde\x1b[\xb8>>\xc1\xc8\xfc1\xe2\xeff1\x1e\xeed\xaa\xc4\xa2\xf3\x94\xd4W\xec\xe8\x8f\xc3u\xdd\xddp\x97\xa5\xd9o"\xad\xbf;\xcap\xff\xef\xe3U\xad\xaa\xb1!\xc5&g\xd6,\xd4\x16\xdd\x7f\xf5\xd5\\\xdf\xd3\x80,\xaba\x98\xa6@\xa2A\x8c\tD\xe6`\x8c\xb8\x1b\x84\xa1\x18A!a\x13\x96\xce\x18\x92\xbbm\xc8\x9f\xcc\xb7\xa9|7\xa0\xefY\x9bQ\xad\x00\x00\x00\x00\x82\x004\xeb\xa5\xfc\x12Y\x95\x0bl\xf8\xcd\xe3Vy\xd83\xb0\r\xd3h\xea1J\x88\xe1\x1d\x18\xccv\x1dp\'p\xa2\xec\x16\xca\x8a\xc6\x08ab\xd7\'\xfc\xd0\x9ax\xe8\x90T"\xe9\xaf\xe7&-\x15\xd8\xcb\xaa\x04\x92\x0c\xff\xe2\xae5\xb9\x0b\x95\x11C\xe7\x86cY\x86:\x86Uu\xc4aK\x1b\x98\x9c\xb3`\xc5ur\x0c\xec\xf0G\x1f\x9d\xbb\x86jO\xe7]D7U,\xccu\x83\xb1\x9a\x8bX\xa3\xfb\xf4D\x00V\x9a\xf1\x1aP\xc4Te\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\x0cy\xbc\xd8)\x8e\x0e\xe4\xe4_Ac\xc1\x9d\x16z\x13[\xd4\\*Fy\xd6\xa7Z\xfb\xea\x9b[\x8d\x185Q\xa0FMz\xcf\x9ay\xb9\xcaT\x8bW}\xa7F\x9b\xc8\x9b_,\xb4s\xdb\x15\xb5\xa4k\x82%\x97\xec\xb6\xd1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\xd5\xc9\x82\xab\x80\xcb\xf0\x0b\xf3w\xe1\x14\xe4\xef\x85o\x82\xa6\x97\x0b\xa8\x1e\x94S\xa4\xb9D\xa2\xe5`l\xaeL\xbbz\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x94B\x19af\x95\xe4\x8fz\x9bT\xb3\x88w\x10J\x1f_\xbe\x85\xc6\x1d\xa2\xfb\xe3RuA\x8ad\xbc\x00\x00\x01\xd1\xa9J \x000\x94B\x19af\x95\xe4\x8fz\x9bT\xb3\x88w\x10J\x1f_\xbe\x85\xc6\x1d\xa2\xfb\xe3RuA\x8ad\xbc\x00\x00\x00\x00\xa4_\xca9\x18\x13l\x8d>\xc9\xb3n\xac2?\xf9\xb7\t\xf1\xe1\xaa:\xe9\x01[\\\xfb\xce\x91\xa4@\xc4\xd6\xf3\x0f\xc5\x06\xdc\xd8I\x1f$k\x0e\xf4\x05\x8c\x9f\xb9\x9bD[\xbc\x90e0\x17m+z\xd6\x84b9\xa6\xcb\xa56\xcf\xacj\x02\xf3D\x7f:^\x9c\x8e\x96,\xd7\x83\xee\xf1\\\xce9\xf3\x8a\x1f\x96\x16\x9c\x1d3\x00\x00\x00\x00\x00\x00\x00\x00(\xfc\xe5\xe3\x17\x8e\'\xb3\xb4\xc8T\xd6+\x0e\xbe0S\xc8\xd6^--\xbeD\x8a-c\x8f\xb4\xc0\xda\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x96Ae\xaa3\x92\xe6t\xa6~\x83\x7fh\xaf\x16\xfc-X2#w~\x17\x94-j\xb9\x82\xa6\x1b>\x80u\x1bb\xea\xf7\x94&`zN\xf4yK\xf4\xff\xa6\xaf\xa8zv\x1c\xa7\x87n\xd7f2\xb8\xda\xfdv~\xb6\xae\xe0\x18\x9d\xb0g\xf4\xea\x96ozN\x1c\xb8\xc9\xa1\x97\x8d#\xfe\xfe\xbe\x94|\x1e\xc3;\xbak\x05\x00\x00\x00\x00\x04\x01@\x02\x18', # noqa: E501
# Target tx count per sec
"TX_PER_SEC": 20,
# Size of mempool = 10x the size of block

View File

@ -30,6 +30,7 @@ def calculate_iterations(
of the quality. The quality must be retrieved from the proof.
"""
quality: bytes32 = proof_of_space.verify_and_get_quality_string(num_zero_bits)
assert quality is not None
return calculate_iterations_quality(
quality, proof_of_space.size, difficulty, min_iterations
)

View File

@ -2,7 +2,7 @@ import asyncio
import logging
from typing import Dict, List, Set, Optional, Callable, Tuple
from blspy import Util, InsecureSignature, PrependSignature, PublicKey
from blspy import G1Element, G2Element, AugSchemeMPL
from src.util.keychain import Keychain
from src.consensus.constants import ConsensusConstants
@ -15,6 +15,7 @@ from src.types.sized_bytes import bytes32
from src.types.pool_target import PoolTarget
from src.util.api_decorators import api_request
from src.util.ints import uint32, uint64, uint128, uint8
from src.wallet.derive_keys import master_sk_to_farmer_sk, master_sk_to_pool_sk
log = logging.getLogger(__name__)
@ -44,7 +45,7 @@ class Farmer:
self.seen_challenges: Set[bytes32] = set()
self.unfinished_challenges: Dict[uint128, List[bytes32]] = {}
self.current_weight: uint128 = uint128(0)
self.proof_of_time_estimate_ips: uint64 = uint64(10000)
self.proof_of_time_estimate_ips: uint64 = uint64(100000)
self.constants = consensus_constants
self._shut_down = False
self.server = None
@ -58,7 +59,7 @@ class Farmer:
# This is the farmer configuration
self.wallet_target = bytes.fromhex(self.config["xch_target_puzzle_hash"])
self.pool_public_keys = [
PublicKey.from_bytes(bytes.fromhex(pk))
G1Element.from_bytes(bytes.fromhex(pk))
for pk in self.config["pool_public_keys"]
]
@ -66,7 +67,7 @@ class Farmer:
self.pool_target = bytes.fromhex(pool_config["xch_target_puzzle_hash"])
self.pool_sks_map: Dict = {}
for key in self._get_private_keys():
self.pool_sks_map[bytes(key.get_public_key())] = key
self.pool_sks_map[bytes(key.get_g1())] = key
assert len(self.wallet_target) == 32
assert len(self.pool_target) == 32
@ -107,15 +108,12 @@ class Farmer:
self.state_changed_callback(change)
def _get_public_keys(self):
return [
epk.public_child(0).get_public_key()
for epk in self.keychain.get_all_public_keys()
]
return [child_sk.get_g1() for child_sk in self._get_private_keys()]
def _get_private_keys(self):
return [
esk.private_child(0).get_private_key()
for esk, _ in self.keychain.get_all_private_keys()
all_sks = self.keychain.get_all_private_keys()
return [master_sk_to_farmer_sk(sk) for sk, _ in all_sks] + [
master_sk_to_pool_sk(sk) for sk, _ in all_sks
]
async def _get_required_iters(
@ -251,9 +249,9 @@ class Farmer:
)
return
pool_target: PoolTarget = PoolTarget(self.pool_target, uint32(0))
pool_target_signature: PrependSignature = self.pool_sks_map[
pool_pk
].sign_prepend(bytes(pool_target))
pool_target_signature: G2Element = AugSchemeMPL.sign(
self.pool_sks_map[pool_pk], bytes(pool_target)
)
request2 = farmer_protocol.RequestHeaderHash(
challenge_hash,
@ -279,21 +277,16 @@ class Farmer:
proof_of_space: bytes32 = self.header_hash_to_pos[header_hash]
validates: bool = False
for sk in self._get_private_keys():
pk = sk.get_public_key()
pk = sk.get_g1()
if pk == response.farmer_pk:
agg_pk = ProofOfSpace.generate_plot_public_key(
response.harvester_pk, pk
)
agg_pk = ProofOfSpace.generate_plot_public_key(response.local_pk, pk)
assert agg_pk == proof_of_space.plot_public_key
new_m = bytes(agg_pk) + Util.hash256(header_hash)
farmer_share = sk.sign_insecure(new_m)
agg_sig = PrependSignature.from_insecure_sig(
InsecureSignature.aggregate(
[response.message_signature, farmer_share]
)
farmer_share = AugSchemeMPL.sign(sk, header_hash, agg_pk)
agg_sig = AugSchemeMPL.aggregate(
[response.message_signature, farmer_share]
)
validates = AugSchemeMPL.verify(agg_pk, header_hash, agg_sig)
validates = agg_sig.verify([Util.hash256(header_hash)], [agg_pk])
if validates:
break
assert validates

View File

@ -46,10 +46,12 @@ async def validate_unfinished_block_header(
# TODO: change numbers
# 3. Check harvester signature of header data is valid based on harvester key
if not block_header.harvester_signature.verify(
[blspy.Util.hash256(block_header.data.get_hash())],
[proof_of_space.plot_public_key],
):
validates = blspy.AugSchemeMPL.verify(
proof_of_space.plot_public_key,
block_header.data.get_hash(),
block_header.plot_signature,
)
if not validates:
return (Err.INVALID_PLOT_SIGNATURE, None)
# 4. If not genesis, the previous block must exist
@ -236,10 +238,12 @@ def pre_validate_finished_block_header(constants: ConsensusConstants, data: byte
return False, None
# 9. Check harvester signature of header data is valid based on harvester key
if not block.header.harvester_signature.verify(
[blspy.Util.hash256(block.header.data.get_hash())],
[block.proof_of_space.plot_public_key],
):
validates = blspy.AugSchemeMPL.verify(
block.proof_of_space.plot_public_key,
block.header.data.get_hash(),
block.header.plot_signature,
)
if not validates:
return False, None
# 10. Check proof of space based on challenge

View File

@ -5,12 +5,12 @@ from enum import Enum
import multiprocessing
import concurrent
from typing import Dict, List, Optional, Set, Tuple
from blspy import AugSchemeMPL
from chiabip158 import PyBIP158
from clvm.casts import int_from_bytes
from src.consensus.block_rewards import calculate_base_fee
from src.types.BLSSignature import BLSSignature
from src.consensus.constants import ConsensusConstants
from src.full_node.block_header_validation import (
validate_unfinished_block_header,
@ -30,7 +30,7 @@ from src.types.header import Header
from src.types.header_block import HeaderBlock
from src.types.sized_bytes import bytes32
from src.util.blockchain_check_conditions import blockchain_check_conditions_dict
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
from src.util.condition_tools import pkm_pairs_for_conditions_dict
from src.util.cost_calculator import calculate_cost_of_program
from src.util.errors import ConsensusError, Err
from src.util.hash import std_hash
@ -579,12 +579,12 @@ class Blockchain:
# 17. Verify the pool signature even if there are no transactions
pool_target_m = bytes(block.header.data.pool_target)
hash_key_pairs = [
BLSSignature.PkMessagePair(
block.proof_of_space.pool_public_key, std_hash(pool_target_m)
)
]
if not block.header.data.aggregated_signature.validate(hash_key_pairs):
validates = AugSchemeMPL.verify(
block.proof_of_space.pool_public_key,
pool_target_m,
block.header.data.aggregated_signature,
)
if not validates:
return Err.BAD_AGGREGATE_SIGNATURE
return None
@ -705,7 +705,6 @@ class Blockchain:
removal_counter = collections.Counter(removals)
for k, v in removal_counter.items():
if v > 1:
log.error(f"DOUBLE SPEND! 1 {k}")
return Err.DOUBLE_SPEND
# Check if removals exist and were not previously spend. (unspent_db + diff_store + this_block)
@ -759,7 +758,6 @@ class Blockchain:
# (We ignore all coins confirmed after fork)
if unspent.spent == 1 and unspent.spent_block_index <= fork_h:
# Spend in an ancestor block, so this is a double spend
log.error(f"DOUBLE SPEND! 2 {unspent}")
return Err.DOUBLE_SPEND
# If it's a coinbase, check that it's not frozen
if unspent.coinbase == 1:
@ -795,8 +793,6 @@ class Blockchain:
# and coins created after fork (additions_since_fork)>
if rem in removals_since_fork:
# This coin was spent in the fork
log.error(f"DOUBLE SPEND! 3 {rem}")
log.error(f"removals_since_fork: {removals_since_fork}")
return Err.DOUBLE_SPEND
# Check fees
@ -840,11 +836,8 @@ class Blockchain:
# The pool signature on the pool target is checked here as well, since the pool signature is
# aggregated along with the transaction signatures
hash_key_pairs = [
BLSSignature.PkMessagePair(
block.proof_of_space.pool_public_key, std_hash(pool_target_m)
)
]
pairs_pks = [block.proof_of_space.pool_public_key]
pairs_msgs = [pool_target_m]
for npc in npc_list:
unspent = removal_coin_records[npc.coin_name]
error = blockchain_check_conditions_dict(
@ -852,15 +845,21 @@ class Blockchain:
)
if error:
return error
hash_key_pairs.extend(
hash_key_pairs_for_conditions_dict(npc.condition_dict, npc.coin_name)
)
for pk, m in pkm_pairs_for_conditions_dict(
npc.condition_dict, npc.coin_name
):
pairs_pks.append(pk)
pairs_msgs.append(m)
# Verify aggregated signature
# TODO: move this to pre_validate_blocks_multiprocessing so we can sync faster
if not block.header.data.aggregated_signature:
return Err.BAD_AGGREGATE_SIGNATURE
if not block.header.data.aggregated_signature.validate(hash_key_pairs):
validates = AugSchemeMPL.agg_verify(
pairs_pks, pairs_msgs, block.header.data.aggregated_signature
)
if not validates:
return Err.BAD_AGGREGATE_SIGNATURE
return None

View File

@ -10,6 +10,7 @@ from typing import AsyncGenerator, Dict, List, Optional, Tuple, Callable
import aiosqlite
from chiabip158 import PyBIP158
from chiapos import Verifier
from blspy import G2Element, AugSchemeMPL
from src.consensus.block_rewards import calculate_base_fee, calculate_block_reward
from src.consensus.constants import ConsensusConstants
@ -34,7 +35,6 @@ from src.protocols.wallet_protocol import GeneratorResponse
from src.server.connection import PeerConnections
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
from src.server.server import ChiaServer
from src.types.BLSSignature import BLSSignature
from src.types.challenge import Challenge
from src.types.coin import Coin, hash_coin_list
from src.types.full_block import FullBlock
@ -240,7 +240,7 @@ class FullNode:
Message("new_unfinished_block", unfinished_block_msg),
delivery,
)
return
break
for challenge_msg in challenge_requests:
yield OutboundMessage(
NodeType.TIMELORD, Message("challenge_start", challenge_msg), delivery
@ -1203,11 +1203,11 @@ class FullNode:
Creates a block body and header, with the proof of space, coinbase, and fee targets provided
by the farmer, and sends the hash of the header data back to the farmer.
"""
plot_seed: bytes32 = request.proof_of_space.get_plot_seed()
plot_id: bytes32 = request.proof_of_space.get_plot_id()
# Checks that the proof of space is valid
quality_string: bytes = Verifier().validate_proof(
plot_seed,
plot_id,
request.proof_of_space.size,
request.challenge_hash,
bytes(request.proof_of_space.proof),
@ -1241,13 +1241,13 @@ class FullNode:
SpendBundle
] = await self.mempool_manager.create_bundle_for_tip(target_tip)
spend_bundle_fees = 0
aggregate_sig: BLSSignature = BLSSignature(bytes(request.pool_target_signature))
aggregate_sig: G2Element = request.pool_target_signature
solution_program: Optional[Program] = None
if spend_bundle:
solution_program = best_solution_program(spend_bundle)
spend_bundle_fees = spend_bundle.fees()
aggregate_sig = BLSSignature.aggregate(
aggregate_sig = AugSchemeMPL.aggregate(
[spend_bundle.aggregated_signature, aggregate_sig]
)

View File

@ -37,7 +37,7 @@ class FullNodeStore:
await self.db.commit()
self.proof_of_time_estimate_ips = uint64(10000)
self.proof_of_time_estimate_ips = uint64(100000)
self.proof_of_time_heights = {}
self.unfinished_blocks_leader = (
uint32(0),

View File

@ -5,6 +5,7 @@ import logging
from chiabip158 import PyBIP158
from clvm.casts import int_from_bytes
from blspy import G1Element, G2Element, AugSchemeMPL
from src.consensus.constants import ConsensusConstants
from src.types.condition_opcodes import ConditionOpcode
@ -22,7 +23,7 @@ from src.full_node.coin_store import CoinStore
from src.util.errors import Err
from src.util.cost_calculator import calculate_cost_of_program
from src.util.mempool_check_conditions import mempool_check_conditions_dict
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
from src.util.condition_tools import pkm_pairs_for_conditions_dict
from src.util.ints import uint64, uint32
from src.types.mempool_inclusion_status import MempoolInclusionStatus
from sortedcontainers import SortedDict
@ -277,7 +278,8 @@ class MempoolManager:
continue
# Verify conditions, create hash_key list for aggsig check
hash_key_pairs = []
pks: List[G1Element] = []
msgs: List[G2Element] = []
error: Optional[Err] = None
for npc in npc_list:
coin_record: CoinRecord = removal_record_dict[npc.coin_name]
@ -303,17 +305,21 @@ class MempoolManager:
potential_error = error
break
hash_key_pairs.extend(
hash_key_pairs_for_conditions_dict(
npc.condition_dict, npc.coin_name
)
)
for pk, m in pkm_pairs_for_conditions_dict(
npc.condition_dict, npc.coin_name
):
pks.append(pk)
msgs.append(m)
if error:
errors.append(error)
continue
# Verify aggregated signature
if not new_spend.aggregated_signature.validate(hash_key_pairs):
validates = AugSchemeMPL.agg_verify(
pks, msgs, new_spend.aggregated_signature
)
if not validates:
return None, MempoolInclusionStatus.FAILED, Err.BAD_AGGREGATE_SIGNATURE
# Remove all conflicting Coins and SpendBundles

View File

@ -1,11 +1,11 @@
import logging
import asyncio
from pathlib import Path
from typing import Dict, Optional, Tuple, List, Callable
from typing import Dict, Optional, Tuple, List, Callable, Set
import time
import concurrent
from blspy import PublicKey, Util, InsecureSignature
from blspy import G1Element, G2Element, AugSchemeMPL
from chiapos import DiskProver
from src.protocols import harvester_protocol
@ -24,10 +24,10 @@ log = logging.getLogger(__name__)
class Harvester:
config: Dict
provers: Dict[Path, PlotInfo]
failed_to_open_filenames: List[Path]
no_key_filenames: List[Path]
farmer_public_keys: List[PublicKey]
pool_public_keys: List[PublicKey]
failed_to_open_filenames: Set[Path]
no_key_filenames: Set[Path]
farmer_public_keys: List[G1Element]
pool_public_keys: List[G1Element]
cached_challenges: List[harvester_protocol.NewChallenge]
root_path: Path
_is_shutdown: bool
@ -41,8 +41,8 @@ class Harvester:
# From filename to prover
self.provers = {}
self.failed_to_open_filenames = []
self.no_key_filenames = []
self.failed_to_open_filenames = set()
self.no_key_filenames = set()
self._is_shutdown = False
self.global_connections: Optional[PeerConnections] = None
@ -87,7 +87,7 @@ class Harvester:
"pool_public_key": plot_info.pool_public_key,
"farmer_public_key": plot_info.farmer_public_key,
"plot_public_key": plot_info.plot_public_key,
"harvester_sk": plot_info.harvester_sk,
"local_sk": plot_info.local_sk,
"file_size": plot_info.file_size,
"time_modified": plot_info.time_modified,
}
@ -108,6 +108,7 @@ class Harvester:
self.no_key_filenames,
) = load_plots(
self.provers,
self.failed_to_open_filenames,
self.farmer_public_keys,
self.pool_public_keys,
self.root_path,
@ -248,7 +249,7 @@ class Harvester:
Delivery.RESPOND,
)
log.info(
f"Time taken to lookup qualities in {len(awaitables)} plots: {time.time() - start}. "
f"{len(awaitables)} plots were eligible for farming for this challenge, time: {time.time() - start}. "
f"Total {len(self.provers)} plots"
)
@ -278,7 +279,7 @@ class Harvester:
plot_info.pool_public_key,
plot_info.farmer_public_key,
plot_info.plot_public_key,
plot_info.harvester_sk,
plot_info.local_sk,
plot_info.file_size,
plot_info.time_modified,
)
@ -290,7 +291,7 @@ class Harvester:
plot_info = self.provers[filename]
plot_public_key = ProofOfSpace.generate_plot_public_key(
plot_info.harvester_sk.get_public_key(), plot_info.farmer_public_key
plot_info.local_sk.get_g1(), plot_info.farmer_public_key
)
proof_of_space: ProofOfSpace = ProofOfSpace(
@ -319,20 +320,19 @@ class Harvester:
"""
plot_info = self.provers[Path(request.plot_id).resolve()]
harvester_sk = plot_info.harvester_sk
local_sk = plot_info.local_sk
agg_pk = ProofOfSpace.generate_plot_public_key(
harvester_sk.get_public_key(), plot_info.farmer_public_key
local_sk.get_g1(), plot_info.farmer_public_key
)
new_m = bytes(agg_pk) + Util.hash256(request.message)
# This is only a partial signature. When combined with the farmer's half, it will
# form a complete PrependSignature.
signature: InsecureSignature = harvester_sk.sign_insecure(new_m)
signature: G2Element = AugSchemeMPL.sign(local_sk, request.message, agg_pk)
response: harvester_protocol.RespondSignature = harvester_protocol.RespondSignature(
request.plot_id,
request.message,
harvester_sk.get_public_key(),
local_sk.get_g1(),
plot_info.farmer_public_key,
signature,
)

View File

@ -1,11 +1,12 @@
import logging
from chiapos import Verifier
from collections import Counter
from blspy import PublicKey
from blspy import G1Element
from src.util.keychain import Keychain
from src.util.config import load_config
from src.plotting.plot_tools import load_plots
from src.util.hash import std_hash
from src.wallet.derive_keys import master_sk_to_farmer_sk
log = logging.getLogger(__name__)
@ -21,13 +22,13 @@ def check_plots(args, root_path):
v = Verifier()
log.info("Loading plots in config.yaml using plot_tools loading code\n")
kc: Keychain = Keychain()
pks = [epk.public_child(0).get_public_key() for epk in kc.get_all_public_keys()]
pks = [master_sk_to_farmer_sk(sk).get_g1() for sk, _ in kc.get_all_private_keys()]
pool_public_keys = [
PublicKey.from_bytes(bytes.fromhex(pk))
G1Element.from_bytes(bytes.fromhex(pk))
for pk in config["farmer"]["pool_public_keys"]
]
_, provers, failed_to_open_filenames, no_key_filenames = load_plots(
{}, pks, pool_public_keys, root_path, open_no_key_filenames=True,
{}, set(), pks, pool_public_keys, root_path, open_no_key_filenames=True,
)
if len(provers) > 0:
log.info("")
@ -42,7 +43,7 @@ def check_plots(args, root_path):
log.info(f"Testing plot {plot_path} k={pr.get_size()}")
log.info(f"\tPool public key: {plot_info.pool_public_key}")
log.info(f"\tFarmer public key: {plot_info.farmer_public_key}")
log.info(f"\tHarvester sk: {plot_info.harvester_sk}")
log.info(f"\tLocal sk: {plot_info.local_sk}")
total_proofs = 0
try:
for i in range(num):

View File

@ -1,7 +1,8 @@
from pathlib import Path
from secrets import token_bytes
from typing import Optional, List
import logging
from blspy import PrivateKey, PublicKey
from blspy import PrivateKey, G1Element
from chiapos import DiskPlotter
from datetime import datetime
from src.types.proof_of_space import ProofOfSpace
@ -14,68 +15,62 @@ from src.plotting.plot_tools import (
stream_plot_info,
add_plot_directory,
)
from src.wallet.derive_keys import (
master_sk_to_farmer_sk,
master_sk_to_pool_sk,
master_sk_to_local_sk,
)
log = logging.getLogger(__name__)
def get_default_public_key() -> PublicKey:
def get_default_farmer_public_key() -> G1Element:
keychain: Keychain = Keychain()
epk = keychain.get_first_public_key()
if epk is None:
sk_ent = keychain.get_first_private_key()
if sk_ent is None:
raise RuntimeError(
"No keys, please run 'chia keys generate' or provide a public key with -f"
)
return PublicKey.from_bytes(bytes(epk.public_child(0).get_public_key()))
return master_sk_to_farmer_sk(sk_ent[0]).get_g1()
def create_plots(args, root_path, use_datetime=True):
def get_default_pool_public_key() -> G1Element:
keychain: Keychain = Keychain()
sk_ent = keychain.get_first_private_key()
if sk_ent is None:
raise RuntimeError(
"No keys, please run 'chia keys generate' or provide a public key with -f"
)
return master_sk_to_pool_sk(sk_ent[0]).get_g1()
def create_plots(
args, root_path, use_datetime=True, test_private_keys: Optional[List] = None
):
config_filename = config_path_for_filename(root_path, "config.yaml")
if args.sk_seed is None and args.index is not None:
log.info(
"You have specified the -i (index) argument without the -s (sk_seed) argument."
" The program has changes, so that the sk_seed is now generated randomly, so -i is no longer necessary."
" Please run the program without -i."
)
quit()
if args.tmp2_dir is None:
args.tmp2_dir = args.final_dir
if args.index is None:
args.index = 0
# The seed is what will be used to generate a private key for each plot
if args.sk_seed is not None:
sk_seed: bytes = bytes.fromhex(args.sk_seed)
log.info(f"Using the provided sk_seed {sk_seed.hex()}.")
else:
sk_seed = token_bytes(32)
log.info(
f"Using sk_seed {sk_seed.hex()}. Note that sk seed is now generated randomly. "
f"If you want to use a specific seed, use the -s argument."
)
farmer_public_key: PublicKey
farmer_public_key: G1Element
if args.farmer_public_key is not None:
farmer_public_key = PublicKey.from_bytes(bytes.fromhex(args.farmer_public_key))
farmer_public_key = G1Element.from_bytes(bytes.fromhex(args.farmer_public_key))
else:
farmer_public_key = get_default_public_key()
farmer_public_key = get_default_farmer_public_key()
pool_public_key: PublicKey
pool_public_key: G1Element
if args.pool_public_key is not None:
pool_public_key = bytes.fromhex(args.pool_public_key)
else:
pool_public_key = get_default_public_key()
pool_public_key = get_default_pool_public_key()
if args.num is not None:
num = args.num
else:
num = 1
log.info(
f"Creating {num} plots, from index {args.index} to "
f"{args.index + num - 1}, of size {args.size}, sk_seed "
f"{sk_seed.hex()} pool public key "
f"{bytes(pool_public_key).hex()} farmer public key {bytes(farmer_public_key).hex()}"
f"Creating {num} plots of size {args.size}, pool public key: "
f"{bytes(pool_public_key).hex()} farmer public key: {bytes(farmer_public_key).hex()}"
)
mkdir(args.tmp_dir)
@ -84,27 +79,29 @@ def create_plots(args, root_path, use_datetime=True):
finished_filenames = []
config = load_config(root_path, config_filename)
plot_filenames = get_plot_filenames(config["harvester"])
for i in range(args.index, args.index + num):
# Generate a sk based on the seed, plot size (k), and index
sk: PrivateKey = PrivateKey.from_seed(
sk_seed + args.size.to_bytes(1, "big") + i.to_bytes(4, "big")
)
for i in range(num):
# Generate a random master secret key
if test_private_keys is not None:
assert len(test_private_keys) == num
sk: PrivateKey = test_private_keys[i]
else:
sk = PrivateKey.from_seed(token_bytes(32))
# The plot public key is the combination of the harvester and farmer keys
plot_public_key = ProofOfSpace.generate_plot_public_key(
sk.get_public_key(), farmer_public_key
master_sk_to_local_sk(sk).get_g1(), farmer_public_key
)
# The plot seed is based on the harvester, farmer, and pool keys
plot_seed: bytes32 = ProofOfSpace.calculate_plot_seed(
# The plot id is based on the harvester, farmer, and pool keys
plot_id: bytes32 = ProofOfSpace.calculate_plot_id(
pool_public_key, plot_public_key
)
dt_string = datetime.now().strftime("%Y-%m-%d-%H-%M")
if use_datetime:
filename: str = f"plot-k{args.size}-{dt_string}-{plot_seed}.plot"
filename: str = f"plot-k{args.size}-{dt_string}-{plot_id}.plot"
else:
filename = f"plot-k{args.size}-{plot_seed}.plot"
filename = f"plot-k{args.size}-{plot_id}.plot"
full_path: Path = args.final_dir / filename
if args.final_dir.resolve() not in plot_filenames:
@ -116,6 +113,7 @@ def create_plots(args, root_path, use_datetime=True):
config = add_plot_directory(str(args.final_dir.resolve()), root_path)
if not full_path.exists():
log.info(f"Starting plot {num}")
# Creates the plot. This will take a long time for larger plots.
plotter: DiskPlotter = DiskPlotter()
plotter.create_plot_disk(
@ -125,7 +123,7 @@ def create_plots(args, root_path, use_datetime=True):
filename,
args.size,
stream_plot_info(pool_public_key, farmer_public_key, sk),
plot_seed,
plot_id,
args.buffer,
)
finished_filenames.append(filename)

View File

@ -1,12 +1,13 @@
from typing import List, Dict, Optional, Tuple
from typing import List, Dict, Optional, Tuple, Set
from pathlib import Path
from blspy import PrivateKey, PublicKey
from blspy import PrivateKey, G1Element
from chiapos import DiskProver
from dataclasses import dataclass
import logging
import traceback
from src.types.proof_of_space import ProofOfSpace
from src.util.config import load_config, save_config
from src.wallet.derive_keys import master_sk_to_local_sk
log = logging.getLogger(__name__)
@ -15,10 +16,10 @@ log = logging.getLogger(__name__)
@dataclass
class PlotInfo:
prover: DiskProver
pool_public_key: PublicKey
farmer_public_key: PublicKey
plot_public_key: PublicKey
harvester_sk: PrivateKey
pool_public_key: G1Element
farmer_public_key: G1Element
plot_public_key: G1Element
local_sk: PrivateKey
file_size: int
time_modified: float
@ -48,21 +49,23 @@ def get_plot_filenames(config: Dict) -> Dict[Path, List[Path]]:
return all_files
def parse_plot_info(memo: bytes) -> Tuple[PublicKey, PublicKey, PrivateKey]:
def parse_plot_info(memo: bytes) -> Tuple[G1Element, G1Element, PrivateKey]:
# Parses the plot info bytes into keys
assert len(memo) == (48 + 48 + 32)
return (
PublicKey.from_bytes(memo[:48]),
PublicKey.from_bytes(memo[48:96]),
G1Element.from_bytes(memo[:48]),
G1Element.from_bytes(memo[48:96]),
PrivateKey.from_bytes(memo[96:]),
)
def stream_plot_info(
pool_public_key: PublicKey, farmer_public_key: PublicKey, harvester_sk: PrivateKey,
pool_public_key: G1Element,
farmer_public_key: G1Element,
local_master_sk: PrivateKey,
):
# Streams the plot info keys into bytes
data = bytes(pool_public_key) + bytes(farmer_public_key) + bytes(harvester_sk)
data = bytes(pool_public_key) + bytes(farmer_public_key) + bytes(local_master_sk)
assert len(data) == (48 + 48 + 32)
return data
@ -77,15 +80,15 @@ def add_plot_directory(str_path, root_path):
def load_plots(
provers: Dict[Path, PlotInfo],
farmer_public_keys: Optional[List[PublicKey]],
pool_public_keys: Optional[List[PublicKey]],
failed_to_open_filenames: Set[Path],
farmer_public_keys: Optional[List[G1Element]],
pool_public_keys: Optional[List[G1Element]],
root_path: Path,
open_no_key_filenames=False,
) -> Tuple[bool, Dict[Path, PlotInfo], List[Path], List[Path]]:
) -> Tuple[bool, Dict[Path, PlotInfo], Set[Path], Set[Path]]:
config_file = load_config(root_path, "config.yaml", "harvester")
changed = False
failed_to_open_filenames: List[Path] = []
no_key_filenames: List[Path] = []
no_key_filenames: Set[Path] = []
log.info(f'Searching directories {config_file["plot_directories"]}')
plot_filenames: Dict[Path, List[Path]] = get_plot_filenames(config_file)
@ -100,12 +103,16 @@ def load_plots(
if stat_info.st_mtime == provers[filename].time_modified:
total_size += stat_info.st_size
continue
if filename in failed_to_open_filenames:
continue
if filename.exists():
try:
prover = DiskProver(str(filename))
(pool_public_key, farmer_public_key, harvester_sk,) = parse_plot_info(
prover.get_memo()
)
(
pool_public_key,
farmer_public_key,
local_master_sk,
) = parse_plot_info(prover.get_memo())
# Only use plots that correct keys associated with them
if (
farmer_public_keys is not None
@ -114,7 +121,7 @@ def load_plots(
log.warning(
f"Plot {filename} has a farmer public key that is not in the farmer's pk list."
)
no_key_filenames.append(filename)
no_key_filenames.add(filename)
if not open_no_key_filenames:
continue
@ -125,20 +132,21 @@ def load_plots(
log.warning(
f"Plot {filename} has a pool public key that is not in the farmer's pool pk list."
)
no_key_filenames.append(filename)
no_key_filenames.add(filename)
if not open_no_key_filenames:
continue
stat_info = filename.stat()
plot_public_key: PublicKey = ProofOfSpace.generate_plot_public_key(
harvester_sk.get_public_key(), farmer_public_key
local_sk = master_sk_to_local_sk(local_master_sk)
plot_public_key: G1Element = ProofOfSpace.generate_plot_public_key(
local_sk.get_g1(), farmer_public_key
)
provers[filename] = PlotInfo(
prover,
pool_public_key,
farmer_public_key,
plot_public_key,
harvester_sk,
local_sk,
stat_info.st_size,
stat_info.st_mtime,
)
@ -147,8 +155,8 @@ def load_plots(
except Exception as e:
tb = traceback.format_exc()
log.error(f"Failed to open file {filename}. {e} {tb}")
failed_to_open_filenames.append(filename)
break
failed_to_open_filenames.add(filename)
continue
log.info(
f"Found plot {filename} of size {provers[filename].prover.get_size()}"
)

View File

@ -1,5 +1,5 @@
from dataclasses import dataclass
from blspy import PrependSignature
from blspy import G2Element
from src.types.proof_of_space import ProofOfSpace
from src.types.sized_bytes import bytes32
from src.types.pool_target import PoolTarget
@ -35,7 +35,7 @@ class RequestHeaderHash:
challenge_hash: bytes32
proof_of_space: ProofOfSpace
pool_target: PoolTarget
pool_target_signature: PrependSignature
pool_target_signature: G2Element
farmer_rewards_puzzle_hash: bytes32
@ -51,7 +51,7 @@ class HeaderHash:
class HeaderSignature:
pos_hash: bytes32
header_hash: bytes32
header_signature: PrependSignature
header_signature: G2Element
@dataclass(frozen=True)

View File

@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import List
from blspy import PublicKey, InsecureSignature
from blspy import G1Element, G2Element
from src.types.proof_of_space import ProofOfSpace
from src.types.sized_bytes import bytes32
@ -17,8 +17,8 @@ Protocol between harvester and farmer.
@dataclass(frozen=True)
@cbor_message
class HarvesterHandshake:
farmer_public_keys: List[PublicKey]
pool_public_keys: List[PublicKey]
farmer_public_keys: List[G1Element]
pool_public_keys: List[G1Element]
@dataclass(frozen=True)
@ -65,6 +65,6 @@ class RequestSignature:
class RespondSignature:
plot_id: str
message: bytes32
harvester_pk: PublicKey
farmer_pk: PublicKey
message_signature: InsecureSignature
local_pk: G1Element
farmer_pk: G1Element
message_signature: G2Element

View File

@ -5,7 +5,7 @@ from src.types.sized_bytes import bytes32
from src.util.cbor_message import cbor_message
from src.util.ints import uint16
protocol_version = "0.0.16"
protocol_version = "0.0.17"
"""
Handshake when establishing a connection between two servers.

View File

@ -506,23 +506,23 @@ class WalletRpcApi:
async def get_public_keys(self, request: Dict):
fingerprints = [
(esk.get_public_key().get_fingerprint(), seed is not None)
for (esk, seed) in self.service.keychain.get_all_private_keys()
(sk.get_g1().get_fingerprint(), seed is not None)
for (sk, seed) in self.service.keychain.get_all_private_keys()
]
response = {"success": True, "public_key_fingerprints": fingerprints}
return response
async def get_private_key(self, request):
fingerprint = request["fingerprint"]
for esk, seed in self.service.keychain.get_all_private_keys():
if esk.get_public_key().get_fingerprint() == fingerprint:
for sk, seed in self.service.keychain.get_all_private_keys():
if sk.get_g1().get_fingerprint() == fingerprint:
s = bytes_to_mnemonic(seed) if seed is not None else None
return {
"success": True,
"private_key": {
"fingerprint": fingerprint,
"esk": bytes(esk).hex(),
"epk": bytes(esk.get_extended_public_key()).hex(),
"sk": bytes(sk).hex(),
"pk": bytes(sk.get_g1()).hex(),
"seed": s,
},
}
@ -542,11 +542,11 @@ class WalletRpcApi:
mnemonic = request["mnemonic"]
entropy = bytes_from_mnemonic(mnemonic)
passphrase = ""
esk = self.service.keychain.add_private_key(entropy, passphrase)
sk = self.service.keychain.add_private_key(entropy, passphrase)
else:
return {"success": False}
fingerprint = esk.get_public_key().get_fingerprint()
fingerprint = sk.get_g1().get_fingerprint()
await self.stop_wallet()
# Makes sure the new key is added to config properly

View File

@ -15,19 +15,14 @@ from src.types.spend_bundle import SpendBundle
from src.types.header import Header
from src.util.api_decorators import api_request
from src.util.ints import uint64
from src.util.block_tools import BlockTools
bt: Optional[BlockTools] = None
OutboundMessageGenerator = AsyncGenerator[OutboundMessage, None]
class FullNodeSimulator(FullNode):
def __init__(self, config, root_path, consensus_constants, name):
def __init__(self, config, root_path, consensus_constants, name, bt):
super().__init__(config, root_path, consensus_constants, name)
global bt
if bt is None:
bt = BlockTools()
self.bt = bt
def _set_server(self, server: ChiaServer):

View File

@ -8,6 +8,7 @@ from src.util.default_root import DEFAULT_ROOT_PATH
from src.util.path import mkdir, path_from_root
from src.simulator.full_node_simulator import FullNodeSimulator
from src.simulator.simulator_constants import test_constants
from src.util.block_tools import BlockTools
# See: https://bugs.python.org/issue29288
@ -28,6 +29,7 @@ def service_kwargs_for_full_node(root_path):
root_path=root_path,
consensus_constants=test_constants,
name=service_name,
bt=BlockTools(),
)
async def start_callback():

View File

@ -1,53 +0,0 @@
from dataclasses import dataclass
from typing import List
import blspy
from src.types.sized_bytes import bytes48, bytes96, bytes32
from src.util.streamable import Streamable, streamable
ZERO96 = bytes96([0] * 96)
class BLSPublicKey(bytes48):
pass
# TODO Stop using this after BLSLibrary is updated
@dataclass(frozen=True)
@streamable
class BLSSignature(Streamable):
"""
This wraps the blspy.BLSPublicKey and resolves a couple edge cases around aggregation and validation.
"""
@dataclass(frozen=True)
@streamable
class PkMessagePair(Streamable):
public_key: BLSPublicKey
message_hash: bytes32
sig: bytes96
@classmethod
def aggregate(cls, sigs):
sigs = [_ for _ in sigs if _.sig != ZERO96]
if len(sigs) == 0:
sig = ZERO96
else:
wrapped_sigs = [blspy.PrependSignature.from_bytes(_.sig) for _ in sigs]
sig = bytes(blspy.PrependSignature.aggregate(wrapped_sigs))
return cls(sig)
def validate(self, hash_key_pairs: List[PkMessagePair]) -> bool:
# check for special case of 0
if len(hash_key_pairs) == 0:
return True
message_hashes = [_.message_hash for _ in hash_key_pairs]
public_keys = [blspy.PublicKey.from_bytes(_.public_key) for _ in hash_key_pairs]
try:
# when the signature is invalid, this method chokes
signature = blspy.PrependSignature.from_bytes(self.sig)
return signature.verify(message_hashes, public_keys)
except Exception:
return False

View File

@ -1,11 +1,10 @@
from dataclasses import dataclass
from blspy import PrependSignature
from blspy import G2Element
from src.types.sized_bytes import bytes32
from src.util.ints import uint64, uint32, uint128
from src.util.streamable import Streamable, streamable
from src.types.BLSSignature import BLSSignature
from src.types.pool_target import PoolTarget
@ -24,7 +23,7 @@ class HeaderData(Streamable):
farmer_rewards_puzzle_hash: bytes32
total_transaction_fees: uint64
pool_target: PoolTarget
aggregated_signature: BLSSignature
aggregated_signature: G2Element
cost: uint64
extension_data: bytes32
generator_hash: bytes32 # This needs to be a tree hash
@ -34,7 +33,7 @@ class HeaderData(Streamable):
@streamable
class Header(Streamable):
data: HeaderData
harvester_signature: PrependSignature
plot_signature: G2Element
@property
def height(self):

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
from typing import Optional
from bitstring import BitArray
from blspy import PublicKey
from blspy import G1Element
from chiapos import Verifier
from src.types.sized_bytes import bytes32
@ -15,48 +15,44 @@ from src.util.hash import std_hash
@streamable
class ProofOfSpace(Streamable):
challenge_hash: bytes32
pool_public_key: PublicKey
plot_public_key: PublicKey
pool_public_key: G1Element
plot_public_key: G1Element
size: uint8
proof: bytes
def get_plot_seed(self) -> bytes32:
return self.calculate_plot_seed(self.pool_public_key, self.plot_public_key)
def get_plot_id(self) -> bytes32:
return self.calculate_plot_id(self.pool_public_key, self.plot_public_key)
def verify_and_get_quality_string(self, num_zero_bits: uint8) -> Optional[bytes32]:
v: Verifier = Verifier()
plot_seed: bytes32 = self.get_plot_seed()
quality_str = v.validate_proof(
plot_seed, self.size, self.challenge_hash, bytes(self.proof)
)
plot_id: bytes32 = self.get_plot_id()
if not self.can_create_proof(plot_seed, self.challenge_hash, num_zero_bits):
if not self.can_create_proof(plot_id, self.challenge_hash, num_zero_bits):
return None
quality_str = v.validate_proof(
plot_id, self.size, self.challenge_hash, bytes(self.proof)
)
if not quality_str:
return None
return quality_str
@staticmethod
def can_create_proof(
plot_seed: bytes32, challenge_hash: bytes32, num_zero_bits: uint8
plot_id: bytes32, challenge_hash: bytes32, num_zero_bits: uint8
) -> bool:
h = BitArray(std_hash(bytes(plot_seed) + bytes(challenge_hash)))
h = BitArray(std_hash(bytes(plot_id) + bytes(challenge_hash)))
return h[:num_zero_bits].uint == 0
@staticmethod
def calculate_plot_seed(
pool_public_key: PublicKey, plot_public_key: PublicKey,
def calculate_plot_id(
pool_public_key: G1Element, plot_public_key: G1Element,
) -> bytes32:
return bytes32(std_hash(bytes(pool_public_key) + bytes(plot_public_key)))
@staticmethod
def generate_plot_public_key(
harvester_pk: PublicKey, farmer_pk: PublicKey
) -> PublicKey:
# Insecure aggregation is fine here, since the harvester can choose any public key
# she wants. Insecure refers to suceptibility to the rogue pk attack:
# https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html
# This however, is not relevant since the consensus will just verify the 2/2 signature as
# if it was a normal signature.
return PublicKey.aggregate_insecure([harvester_pk, farmer_pk])
local_pk: G1Element, farmer_pk: G1Element
) -> G1Element:
return local_pk + farmer_pk

View File

@ -5,8 +5,8 @@ from src.types.coin import Coin
from src.types.sized_bytes import bytes32
from src.util.chain_utils import additions_for_solution
from src.util.streamable import Streamable, streamable
from .BLSSignature import BLSSignature
from .coin_solution import CoinSolution
from blspy import G2Element, AugSchemeMPL
@dataclass(frozen=True)
@ -20,7 +20,7 @@ class SpendBundle(Streamable):
"""
coin_solutions: List[CoinSolution]
aggregated_signature: BLSSignature
aggregated_signature: G2Element
@classmethod
def aggregate(cls, spend_bundles):
@ -29,7 +29,7 @@ class SpendBundle(Streamable):
for _ in spend_bundles:
coin_solutions += _.coin_solutions
sigs.append(_.aggregated_signature)
aggregated_signature = BLSSignature.aggregate(sigs)
aggregated_signature = AugSchemeMPL.aggregate(sigs)
return cls(coin_solutions, aggregated_signature)
def additions(self) -> List[Coin]:

View File

@ -9,12 +9,7 @@ from pathlib import Path
from typing import Dict, List, Tuple, Optional
from argparse import Namespace
from blspy import (
PrependSignature,
PublicKey,
InsecureSignature,
Util,
)
from blspy import G1Element, G2Element, AugSchemeMPL, PrivateKey
from chiavdf import prove
from chiabip158 import PyBIP158
@ -24,7 +19,6 @@ from src.consensus.coinbase import create_puzzlehash_for_pk
from src.consensus.constants import ConsensusConstants
from src.cmds.init import create_default_chia_config, initialize_ssl
from src.cmds.plots import create_plots
from src.types.BLSSignature import BLSPublicKey
from src.consensus import block_rewards, pot_iterations
from src.consensus.pot_iterations import calculate_min_iters_from_iterations
from src.consensus.block_rewards import calculate_block_reward
@ -32,7 +26,6 @@ from src.consensus.coinbase import create_coinbase_coin, create_fees_coin
from src.types.challenge import Challenge
from src.types.classgroup import ClassgroupElement
from src.types.full_block import FullBlock, additions_for_npc
from src.types.BLSSignature import BLSSignature
from src.types.coin import Coin, hash_coin_list
from src.types.program import Program
from src.types.header import Header, HeaderData
@ -50,6 +43,11 @@ from src.util.mempool_check_conditions import get_name_puzzle_conditions
from src.plotting.plot_tools import load_plots
from src.util.logging import initialize_logging
from src.util.wallet_tools import WalletTool
from src.wallet.derive_keys import (
master_sk_to_farmer_sk,
master_sk_to_pool_sk,
master_sk_to_wallet_sk,
)
def get_plot_dir():
@ -80,22 +78,16 @@ class BlockTools:
initialize_ssl(root_path)
# No real plots supplied, so we will use the small test plots
self.use_any_pos = True
self.keychain = Keychain("testing-1.8", True)
self.keychain = Keychain("testing-1.8.0", True)
self.keychain.delete_all_keys()
self.farmer_pk = (
self.keychain.add_private_key(b"block_tools farmer key", "")
.public_child(0)
.get_public_key()
self.farmer_master_sk = self.keychain.add_private_key(
b"block_tools farmer key 1.8", ""
)
self.pool_pk = (
self.keychain.add_private_key(b"block_tools pool key", "")
.public_child(0)
.get_public_key()
self.pool_master_sk = self.keychain.add_private_key(
b"block_tools pool key 1.8", ""
)
self.farmer_ph = create_puzzlehash_for_pk(
BLSPublicKey(bytes(self.farmer_pk))
)
self.pool_ph = create_puzzlehash_for_pk(BLSPublicKey(bytes(self.pool_pk)))
self.farmer_pk = master_sk_to_farmer_sk(self.farmer_master_sk).get_g1()
self.pool_pk = master_sk_to_pool_sk(self.pool_master_sk).get_g1()
plot_dir = get_plot_dir()
mkdir(plot_dir)
@ -114,9 +106,17 @@ class BlockTools:
args.tmp_dir = temp_dir
args.tmp2_dir = plot_dir
args.final_dir = plot_dir
test_private_keys = [
PrivateKey.from_seed(std_hash(bytes([i]))) for i in range(args.num)
]
try:
# No datetime in the filename, to get deterministic filenames and not replot
create_plots(args, root_path, use_datetime=False)
create_plots(
args,
root_path,
use_datetime=False,
test_private_keys=test_private_keys,
)
except KeyboardInterrupt:
shutil.rmtree(plot_dir, ignore_errors=True)
sys.exit(1)
@ -124,54 +124,64 @@ class BlockTools:
initialize_ssl(root_path)
self.keychain = Keychain()
self.use_any_pos = False
pk = self.keychain.get_all_public_keys()[0].public_child(0).get_public_key()
self.farmer_ph = create_puzzlehash_for_pk(BLSPublicKey(bytes(pk)))
self.pool_ph = create_puzzlehash_for_pk(BLSPublicKey(bytes(pk)))
sk_and_ent = self.keychain.get_first_private_key()
assert sk_and_ent is not None
self.farmer_master_sk = sk_and_ent[0]
self.pool_master_sk = sk_and_ent[0]
self.all_esks = self.keychain.get_all_private_keys()
self.all_pubkeys: List[PublicKey] = [
esk[0].public_child(0).get_public_key() for esk in self.all_esks
self.farmer_ph = create_puzzlehash_for_pk(
master_sk_to_wallet_sk(self.farmer_master_sk, uint32(0)).get_g1()
)
self.pool_ph = create_puzzlehash_for_pk(
master_sk_to_wallet_sk(self.pool_master_sk, uint32(0)).get_g1()
)
self.all_sks = self.keychain.get_all_private_keys()
self.pool_pubkeys: List[G1Element] = [
master_sk_to_pool_sk(sk).get_g1() for sk, _ in self.all_sks
]
if len(self.all_pubkeys) == 0:
farmer_pubkeys: List[G1Element] = [
master_sk_to_farmer_sk(sk).get_g1() for sk, _ in self.all_sks
]
if len(self.pool_pubkeys) == 0 or len(farmer_pubkeys) == 0:
raise RuntimeError("Keys not generated. Run `chia generate keys`")
_, self.plots, _, _ = load_plots(
{}, self.all_pubkeys, self.all_pubkeys, root_path
{}, set(), farmer_pubkeys, self.pool_pubkeys, root_path
)
def get_plot_signature(
self, header_data: HeaderData, plot_pk: PublicKey
) -> Optional[PrependSignature]:
self, header_data: HeaderData, plot_pk: G1Element
) -> Optional[G2Element]:
"""
Returns the plot signature of the header data.
"""
farmer_sk = self.all_esks[0][0].private_child(0).get_private_key()
farmer_sk = master_sk_to_farmer_sk(self.all_sks[0][0])
for _, plot_info in self.plots.items():
agg_pk = ProofOfSpace.generate_plot_public_key(
plot_info.harvester_sk.get_public_key(), plot_info.farmer_public_key
plot_info.local_sk.get_g1(), plot_info.farmer_public_key
)
if agg_pk == plot_pk:
m = bytes(agg_pk) + Util.hash256(header_data.get_hash())
harv_share = plot_info.harvester_sk.sign_insecure(m)
farm_share = farmer_sk.sign_insecure(m)
return PrependSignature.from_insecure_sig(
InsecureSignature.aggregate([harv_share, farm_share])
)
m = header_data.get_hash()
harv_share = AugSchemeMPL.sign(plot_info.local_sk, m, agg_pk)
farm_share = AugSchemeMPL.sign(farmer_sk, m, agg_pk)
return AugSchemeMPL.aggregate([harv_share, farm_share])
return None
def get_pool_key_signature(
self, pool_target: PoolTarget, pool_pk: PublicKey
) -> Optional[PrependSignature]:
for esk, _ in self.all_esks:
sk = esk.private_child(0).get_private_key()
if sk.get_public_key() == pool_pk:
return sk.sign_prepend(bytes(pool_target))
self, pool_target: PoolTarget, pool_pk: G1Element
) -> Optional[G2Element]:
for sk, _ in self.all_sks:
sk_child = master_sk_to_pool_sk(sk)
if sk_child.get_g1() == pool_pk:
return AugSchemeMPL.sign(sk_child, bytes(pool_target))
return None
def get_farmer_wallet_tool(self):
return WalletTool(self.all_esks[0][0])
return WalletTool(self.farmer_master_sk)
def get_pool_wallet_tool(self):
return WalletTool(self.all_esks[1][0])
return WalletTool(self.pool_master_sk)
def get_consecutive_blocks(
self,
@ -181,7 +191,7 @@ class BlockTools:
seconds_per_block=None,
seed: bytes = b"",
reward_puzzlehash: bytes32 = None,
transaction_data_at_height: Dict[int, Tuple[Program, BLSSignature]] = None,
transaction_data_at_height: Dict[int, Tuple[Program, G2Element]] = None,
fees: uint64 = uint64(0),
) -> List[FullBlock]:
if transaction_data_at_height is None:
@ -318,7 +328,7 @@ class BlockTools:
timestamp += time_taken
transactions: Optional[Program] = None
aggsig: Optional[BLSSignature] = None
aggsig: Optional[G2Element] = None
if next_height in transaction_data_at_height:
transactions, aggsig = transaction_data_at_height[next_height]
@ -379,7 +389,7 @@ class BlockTools:
seed: bytes = b"",
reward_puzzlehash: bytes32 = None,
transactions: Program = None,
aggsig: BLSSignature = None,
aggsig: G2Element = None,
fees: uint64 = uint64(0),
) -> FullBlock:
"""
@ -438,7 +448,7 @@ class BlockTools:
genesis: bool = False,
reward_puzzlehash: bytes32 = None,
transactions: Program = None,
aggsig: BLSSignature = None,
aggsig: G2Element = None,
fees: uint64 = uint64(0),
) -> FullBlock:
"""
@ -459,9 +469,9 @@ class BlockTools:
# Allow passing in seed, to create reorgs and different chains
seeded_pn = random.randint(0, len(plots) - 1)
plot_info = plots[seeded_pn]
plot_seed = plot_info.prover.get_id()
plot_id = plot_info.prover.get_id()
ccp = ProofOfSpace.can_create_proof(
plot_seed,
plot_id,
challenge_hash,
test_constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"],
)
@ -476,9 +486,9 @@ class BlockTools:
for i in range(len(plots)):
plot_info = plots[i]
j = 0
plot_seed = plot_info.prover.get_id()
plot_id = plot_info.prover.get_id()
ccp = ProofOfSpace.can_create_proof(
plot_seed,
plot_id,
challenge_hash,
test_constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"],
)
@ -503,8 +513,7 @@ class BlockTools:
)
plot_pk = ProofOfSpace.generate_plot_public_key(
selected_plot_info.harvester_sk.get_public_key(),
selected_plot_info.farmer_public_key,
selected_plot_info.local_sk.get_g1(), selected_plot_info.farmer_public_key,
)
proof_of_space: ProofOfSpace = ProofOfSpace(
challenge_hash,
@ -513,6 +522,7 @@ class BlockTools:
selected_plot_info.prover.get_size(),
proof_xs,
)
number_iters: uint64 = pot_iterations.calculate_iterations(
proof_of_space,
difficulty,
@ -610,9 +620,9 @@ class BlockTools:
pool_target, proof_of_space.pool_public_key
)
assert pool_target_signature is not None
final_aggsig: BLSSignature = BLSSignature(bytes(pool_target_signature))
final_aggsig: G2Element = pool_target_signature
if aggsig is not None:
final_aggsig = BLSSignature.aggregate([final_aggsig, aggsig])
final_aggsig = AugSchemeMPL.aggregate([final_aggsig, aggsig])
header_data: HeaderData = HeaderData(
height,
@ -633,9 +643,7 @@ class BlockTools:
generator_hash,
)
header_hash_sig: PrependSignature = self.get_plot_signature(
header_data, plot_pk
)
header_hash_sig: G2Element = self.get_plot_signature(header_data, plot_pk)
header: Header = Header(header_data, header_hash_sig)

View File

@ -1,6 +1,6 @@
from typing import Optional, Tuple, List, Dict
import blspy
from blspy import G1Element
import clvm
from clvm.EvalError import EvalError
from clvm.casts import int_from_bytes
@ -8,7 +8,6 @@ from clvm.subclass_sexp import BaseSExp
from src.types.condition_var_pair import ConditionVarPair
from src.types.condition_opcodes import ConditionOpcode
from src.types.BLSSignature import BLSSignature, BLSPublicKey
from src.types.coin import Coin
from src.types.program import Program
from src.types.sized_bytes import bytes32
@ -71,27 +70,20 @@ def conditions_by_opcode(
return d
def hash_key_pairs_for_conditions_dict(
def pkm_pairs_for_conditions_dict(
conditions_dict: Dict[ConditionOpcode, List[ConditionVarPair]],
coin_name: bytes32 = None,
) -> List[BLSSignature.PkMessagePair]:
pairs: List[BLSSignature.PkMessagePair] = []
) -> List[Tuple[G1Element, bytes]]:
ret: List[Tuple[G1Element, bytes]] = []
for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG, []):
# TODO: check types
# assert len(_) == 3
blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
message: bytes32 = cvp.var2
pairs.append(BLSSignature.PkMessagePair(blspubkey, message))
assert cvp.var2 is not None
ret.append((G1Element.from_bytes(cvp.var1), cvp.var2))
if coin_name is not None:
for cvp in conditions_dict.get(ConditionOpcode.AGG_SIG_ME, []):
aggsigme_blspubkey: BLSPublicKey = BLSPublicKey(cvp.var1)
aggsigme_message: bytes32 = bytes32(
blspy.Util.hash256(cvp.var2 + coin_name)
)
pairs.append(
BLSSignature.PkMessagePair(aggsigme_blspubkey, aggsigme_message)
)
return pairs
ret.append((G1Element.from_bytes(cvp.var1), cvp.var2 + coin_name))
return ret
def aggsig_in_conditions_dict(

View File

@ -9,7 +9,7 @@ import keyring as keyring_main
import pkg_resources
from bitstring import BitArray
from blspy import ExtendedPrivateKey, ExtendedPublicKey
from blspy import PrivateKey, G1Element
from keyrings.cryptfile.cryptfile import CryptFileKeyring
from src.util.hash import std_hash
@ -96,9 +96,9 @@ def entropy_to_seed(entropy: bytes, passphrase):
class Keychain:
"""
The keychain stores two types of keys: private keys, which are ExtendedPrivateKeys from blspy,
The keychain stores two types of keys: private keys, which are PrivateKeys from blspy,
and private key seeds, which are bytes objects that are used as a seed to construct
ExtendedPrivateKeys. Private key seeds are converted to mnemonics when shown to users.
PrivateKeys. Private key seeds are converted to mnemonics when shown to users.
Both types of keys are stored as hex strings in the python keyring, and the implementation of
the keyring depends on OS. Both types of keys can be added, and get_private_keys returns a
@ -108,7 +108,7 @@ class Keychain:
testing: bool
user: str
def __init__(self, user: str = "user-1.8", testing: bool = False):
def __init__(self, user: str = "user-1.8.0", testing: bool = False):
self.testing = testing
self.user = user
@ -121,20 +121,20 @@ class Keychain:
else:
return f"chia-{self.user}"
def _get_pk_and_entropy(
self, user: str
) -> Optional[Tuple[ExtendedPublicKey, bytes]]:
def _get_pk_and_entropy(self, user: str) -> Optional[Tuple[G1Element, bytes]]:
"""
Returns the keychain conntents for a specific 'user' (key index). The contents
include an ExtendedPublicKey and the entropy required to generate the private key.
include an G1Element and the entropy required to generate the private key.
Note that generating the actual private key also requires the passphrase.
"""
epks = ExtendedPublicKey.EXTENDED_PUBLIC_KEY_SIZE
read_str = keyring.get_password(self._get_service(), user)
if read_str is None or len(read_str) == 0:
return None
str_bytes = bytes.fromhex(read_str)
return (ExtendedPublicKey.from_bytes(str_bytes[:epks]), str_bytes[epks:])
return (
G1Element.from_bytes(str_bytes[: G1Element.SIZE]),
str_bytes[G1Element.SIZE :],
)
def _get_private_key_user(self, index: int):
"""
@ -147,41 +147,41 @@ class Keychain:
def _get_free_private_key_index(self) -> int:
"""
Get the index of the first free spot in the keychain.
Get the index of the first free spot in the keychain.
"""
index = 0
while True:
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
pk = self._get_private_key_user(index)
pkent = self._get_pk_and_entropy(pk)
if pkent is None:
return index
index += 1
def add_private_key(self, entropy: bytes, passphrase: str) -> ExtendedPrivateKey:
def add_private_key(self, entropy: bytes, passphrase: str) -> PrivateKey:
"""
Adds a private key to the keychain, with the given entropy and passphrase. The
keychain itself will store the extended public key, and the entropy bytes,
keychain itself will store the public key, and the entropy bytes,
but not the passphrase.
"""
seed = entropy_to_seed(entropy, passphrase)
index = self._get_free_private_key_index()
key = ExtendedPrivateKey.from_seed(seed)
fingerprint = key.get_public_key().get_fingerprint()
if fingerprint in [
epk.get_public_key().get_fingerprint() for epk in self.get_all_public_keys()
]:
key = PrivateKey.from_seed(seed)
fingerprint = key.get_g1().get_fingerprint()
if fingerprint in [pk.get_fingerprint() for pk in self.get_all_public_keys()]:
# Prevents duplicate add
return key
keyring.set_password(
self._get_service(),
self._get_private_key_user(index),
bytes(key.get_extended_public_key()).hex() + entropy.hex(),
bytes(key.get_g1()).hex() + entropy.hex(),
)
return key
def get_first_private_key(
self, passphrases: List[str] = [""]
) -> Optional[Tuple[ExtendedPrivateKey, Optional[bytes]]]:
) -> Optional[Tuple[PrivateKey, bytes]]:
"""
Returns the first key in the keychain that has one of the passed in passphrases.
"""
@ -189,11 +189,11 @@ class Keychain:
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
while index <= MAX_KEYS:
if pkent is not None:
epk, ent = pkent
pk, ent = pkent
for pp in passphrases:
seed = entropy_to_seed(ent, pp)
key = ExtendedPrivateKey.from_seed(seed)
if key.get_extended_public_key() == epk:
key = PrivateKey.from_seed(seed)
if key.get_g1() == pk:
return (key, ent)
index += 1
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
@ -201,53 +201,53 @@ class Keychain:
def get_all_private_keys(
self, passphrases: List[str] = [""]
) -> List[Tuple[ExtendedPrivateKey, bytes]]:
) -> List[Tuple[PrivateKey, bytes]]:
"""
Returns all private keys which can be retrieved, with the given passphrases.
A tuple of key, and entropy bytes (i.e. mnemonic) is returned for each key.
"""
all_keys: List[Tuple[ExtendedPrivateKey, bytes]] = []
all_keys: List[Tuple[PrivateKey, bytes]] = []
index = 0
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
while index <= MAX_KEYS:
if pkent is not None:
epk, ent = pkent
pk, ent = pkent
for pp in passphrases:
seed = entropy_to_seed(ent, pp)
key = ExtendedPrivateKey.from_seed(seed)
if key.get_extended_public_key() == epk:
key = PrivateKey.from_seed(seed)
if key.get_g1() == pk:
all_keys.append((key, ent))
index += 1
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
return all_keys
def get_all_public_keys(self) -> List[ExtendedPublicKey]:
def get_all_public_keys(self) -> List[G1Element]:
"""
Returns all extended public keys.
Returns all public keys.
"""
all_keys: List[Tuple[ExtendedPublicKey, bytes]] = []
all_keys: List[Tuple[G1Element, bytes]] = []
index = 0
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
while index <= MAX_KEYS:
if pkent is not None:
epk, ent = pkent
all_keys.append(epk)
pk, ent = pkent
all_keys.append(pk)
index += 1
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
return all_keys
def get_first_public_key(self) -> Optional[ExtendedPublicKey]:
def get_first_public_key(self) -> Optional[G1Element]:
"""
Returns the first extended public key.
Returns the first public key.
"""
index = 0
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
while index <= MAX_KEYS:
if pkent is not None:
epk, ent = pkent
return epk
pk, ent = pkent
return pk
index += 1
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
return None
@ -261,8 +261,8 @@ class Keychain:
pkent = self._get_pk_and_entropy(self._get_private_key_user(index))
while index <= MAX_KEYS:
if pkent is not None:
epk, ent = pkent
if epk.get_public_key().get_fingerprint() == fingerprint:
pk, ent = pkent
if pk.get_fingerprint() == fingerprint:
keyring.delete_password(
self._get_service(), self._get_private_key_user(index)
)

View File

@ -10,16 +10,7 @@ from src.util.byte_types import hexstr_to_bytes
from src.types.program import Program
from src.util.hash import std_hash
from blspy import (
ChainCode,
ExtendedPrivateKey,
ExtendedPublicKey,
InsecureSignature,
PrependSignature,
PrivateKey,
PublicKey,
Signature,
)
from blspy import PrivateKey, G1Element, G2Element
from src.types.sized_bytes import bytes32
from src.util.ints import uint32, uint64, int64, uint128, int512
@ -35,23 +26,13 @@ pp = pprint.PrettyPrinter(indent=1, width=120, compact=True)
# TODO: Remove hack, this allows streaming these objects from binary
size_hints = {
"PrivateKey": PrivateKey.PRIVATE_KEY_SIZE,
"PublicKey": PublicKey.PUBLIC_KEY_SIZE,
"Signature": Signature.SIGNATURE_SIZE,
"InsecureSignature": InsecureSignature.SIGNATURE_SIZE,
"PrependSignature": PrependSignature.SIGNATURE_SIZE,
"ExtendedPublicKey": ExtendedPublicKey.EXTENDED_PUBLIC_KEY_SIZE,
"ExtendedPrivateKey": ExtendedPrivateKey.EXTENDED_PRIVATE_KEY_SIZE,
"ChainCode": ChainCode.CHAIN_CODE_KEY_SIZE,
"G1Element": G1Element.SIZE,
"G2Element": G2Element.SIZE,
}
unhashable_types = [
PrivateKey,
PublicKey,
Signature,
PrependSignature,
InsecureSignature,
ExtendedPublicKey,
ExtendedPrivateKey,
ChainCode,
G1Element,
G2Element,
Program,
]
# JSON does not support big ints, so these types must be serialized differently in JSON

View File

@ -67,7 +67,10 @@ def strictdataclass(cls: Any):
try:
item = f_type(item)
except (TypeError, AttributeError, ValueError):
item = f_type.from_bytes(item)
try:
item = f_type.from_bytes(item)
except Exception:
item = f_type.from_bytes(bytes(item))
if not isinstance(item, f_type):
raise ValueError(f"Wrong type for {f_name}")
return item

View File

@ -2,22 +2,21 @@ from typing import List, Optional, Dict, Tuple
import clvm
from clvm.casts import int_to_bytes, int_from_bytes
from os import urandom
from blspy import ExtendedPrivateKey
from secrets import token_bytes
from blspy import PrivateKey, AugSchemeMPL
from src.types.condition_var_pair import ConditionVarPair
from src.types.condition_opcodes import ConditionOpcode
from src.types.program import Program
from src.types.BLSSignature import BLSSignature
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.spend_bundle import SpendBundle
from src.util.ints import uint32
from src.util.condition_tools import (
conditions_by_opcode,
hash_key_pairs_for_conditions_dict,
pkm_pairs_for_conditions_dict,
conditions_for_solution,
)
from src.wallet.BLSPrivateKey import BLSPrivateKey
from src.wallet.puzzles.p2_conditions import puzzle_for_conditions
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
from src.wallet.puzzles.puzzle_utils import (
@ -30,6 +29,7 @@ from src.wallet.puzzles.puzzle_utils import (
make_assert_time_exceeds_condition,
make_assert_fee_condition,
)
from src.wallet.derive_keys import master_sk_to_wallet_sk
class WalletTool:
@ -37,23 +37,20 @@ class WalletTool:
next_address = 0
pubkey_num_lookup: Dict[str, int] = {}
def __init__(self, esk: Optional[ExtendedPrivateKey] = None):
def __init__(self, sk: Optional[PrivateKey] = None):
self.current_balance = 0
self.my_utxos: set = set()
if esk is not None:
self.extended_secret_key = esk
if sk is not None:
self.private_key = sk
else:
self.seed = urandom(1024)
self.extended_secret_key = ExtendedPrivateKey.from_seed(self.seed)
self.private_key = PrivateKey.from_seed(token_bytes(32))
self.generator_lookups: Dict = {}
self.name = "MyChiaWallet"
self.puzzle_pk_cache: Dict = {}
self.get_new_puzzle()
def get_next_public_key(self):
pubkey = self.extended_secret_key.public_child(
self.next_address
).get_public_key()
pubkey = master_sk_to_wallet_sk(self.private_key, self.next_address).get_g1()
self.pubkey_num_lookup[bytes(pubkey)] = self.next_address
self.next_address = self.next_address + 1
return pubkey
@ -66,7 +63,9 @@ class WalletTool:
map(
lambda child: hash
== puzzle_for_pk(
bytes(self.extended_secret_key.public_child(child).get_public_key())
bytes(
master_sk_to_wallet_sk(self.private_key, uint32(child)).get_g1()
)
).get_tree_hash(),
reversed(range(self.next_address)),
)
@ -75,26 +74,26 @@ class WalletTool:
def get_keys(self, puzzle_hash):
if puzzle_hash in self.puzzle_pk_cache:
child = self.puzzle_pk_cache[puzzle_hash]
pubkey = self.extended_secret_key.public_child(child).get_public_key()
private = self.extended_secret_key.private_child(child).get_private_key()
private = master_sk_to_wallet_sk(self.private_key, uint32(child))
pubkey = private.get_g1()
return pubkey, private
else:
for child in range(self.next_address):
pubkey = self.extended_secret_key.public_child(child).get_public_key()
pubkey = master_sk_to_wallet_sk(
self.private_key, uint32(child)
).get_g1()
if puzzle_hash == puzzle_for_pk(bytes(pubkey)).get_tree_hash():
return (
pubkey,
self.extended_secret_key.private_child(child).get_private_key(),
master_sk_to_wallet_sk(self.private_key, uint32(child)),
)
raise RuntimeError(f"Do not have the keys for puzzle hash {puzzle_hash}")
def puzzle_for_pk(self, pubkey):
def puzzle_for_pk(self, pubkey: bytes):
return puzzle_for_pk(pubkey)
def get_first_puzzle(self):
pubkey = self.extended_secret_key.public_child(
self.next_address
).get_public_key()
pubkey = master_sk_to_wallet_sk(self.private_key, self.next_address).get_g1()
puzzle = puzzle_for_pk(bytes(pubkey))
self.puzzle_pk_cache[puzzle.get_tree_hash()] = 0
return puzzle
@ -112,11 +111,10 @@ class WalletTool:
return puzzlehash
def sign(self, value, pubkey):
privatekey = self.extended_secret_key.private_child(
self.pubkey_num_lookup[pubkey]
).get_private_key()
blskey = BLSPrivateKey(privatekey)
return blskey.sign(value)
privatekey = master_sk_to_wallet_sk(
self.private_key, self.pubkey_num_lookup[pubkey]
)
return AugSchemeMPL.sign(privatekey, value)
def make_solution(
self, condition_dic: Dict[ConditionOpcode, List[ConditionVarPair]]
@ -159,7 +157,7 @@ class WalletTool:
if secretkey is None:
pubkey, secretkey = self.get_keys(puzzle_hash)
else:
pubkey = secretkey.get_public_key()
pubkey = secretkey.get_g1()
puzzle = puzzle_for_pk(bytes(pubkey))
if ConditionOpcode.CREATE_COIN not in condition_dic:
condition_dic[ConditionOpcode.CREATE_COIN] = []
@ -192,7 +190,6 @@ class WalletTool:
puzzle: Program
for puzzle, solution in spends: # type: ignore # noqa
pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash)
secretkey = BLSPrivateKey(secretkey)
code_ = [puzzle, solution.solution]
sexp = Program.to(code_)
err, con, cost = conditions_for_solution(sexp)
@ -200,12 +197,12 @@ class WalletTool:
return
conditions_dict = conditions_by_opcode(con)
for _ in hash_key_pairs_for_conditions_dict(
for _, msg in pkm_pairs_for_conditions_dict(
conditions_dict, bytes(solution.coin)
):
signature = secretkey.sign(_.message_hash)
signature = AugSchemeMPL.sign(secretkey, msg)
sigs.append(signature)
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
solution_list: List[CoinSolution] = [
CoinSolution(
coin_solution.coin, clvm.to_sexp_f([puzzle, coin_solution.solution])

View File

@ -1,33 +0,0 @@
import dataclasses
import blspy
from src.types.BLSSignature import BLSSignature, BLSPublicKey
from src.types.sized_bytes import bytes32
@dataclasses.dataclass
class BLSPrivateKey:
pk: blspy.PrivateKey
@classmethod
def from_bytes(cls, blob):
return cls(blspy.PrivateKey.from_bytes(blob))
@classmethod
def from_secret_exponent(cls, secret_exponent):
blob = secret_exponent.to_bytes(32, "big")
return cls(blspy.PrivateKey.from_bytes(blob))
def sign(self, message_hash: bytes32) -> BLSSignature:
return BLSSignature(bytes(self.pk.sign_prepend_prehashed(message_hash)))
def public_key(self) -> BLSPublicKey:
return BLSPublicKey(self.pk.get_public_key())
def secret_exponent(self):
return int.from_bytes(bytes(self), "big")
def __bytes__(self):
return bytes(self.pk)

View File

@ -3,7 +3,7 @@ import time
import clvm
from typing import Dict, Optional, List, Any, Set
from src.types.BLSSignature import BLSSignature
from blspy import G2Element, AugSchemeMPL
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.condition_opcodes import ConditionOpcode
@ -13,11 +13,10 @@ from src.types.sized_bytes import bytes32
from src.util.byte_types import hexstr_to_bytes
from src.util.condition_tools import (
conditions_dict_for_solution,
hash_key_pairs_for_conditions_dict,
pkm_pairs_for_conditions_dict,
)
from src.util.json_util import dict_to_json_str
from src.util.ints import uint64, uint32
from src.wallet.BLSPrivateKey import BLSPrivateKey
from src.wallet.block_record import BlockRecord
from src.wallet.cc_wallet.cc_info import CCInfo
from src.wallet.cc_wallet.cc_wallet_puzzles import (
@ -616,19 +615,16 @@ class CCWallet:
self.log.info(f"Successfully selected coins: {used_coins}")
return used_coins
async def get_sigs(
self, innerpuz: Program, innersol: Program
) -> List[BLSSignature]:
async def get_sigs(self, innerpuz: Program, innersol: Program) -> List[G2Element]:
puzzle_hash = innerpuz.get_tree_hash()
pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash)
private = BLSPrivateKey(private)
sigs: List[BLSSignature] = []
sigs: List[G2Element] = []
code_ = [innerpuz, innersol]
sexp = Program.to(code_)
error, conditions, cost = conditions_dict_for_solution(sexp)
if conditions is not None:
for _ in hash_key_pairs_for_conditions_dict(conditions):
signature = private.sign(_.message_hash)
for _, msg in pkm_pairs_for_conditions_dict(conditions):
signature = AugSchemeMPL.sign(private, msg)
sigs.append(signature)
return sigs
@ -655,7 +651,7 @@ class CCWallet:
origin_id: bytes32 = None,
coins: Set[Coin] = None,
) -> Optional[TransactionRecord]:
sigs: List[BLSSignature] = []
sigs: List[G2Element] = []
# Get coins and calculate amount of change required
if coins is None:
@ -784,7 +780,7 @@ class CCWallet:
list_of_solutions.append(create_spend_for_ephemeral(coin, auditor, 0))
list_of_solutions.append(create_spend_for_auditor(auditor, coin))
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
spend_bundle = SpendBundle(list_of_solutions, aggsig)
tx_record = TransactionRecord(
@ -887,7 +883,7 @@ class CCWallet:
# Loop through coins and create solution for innerpuzzle
list_of_solutions = []
output_created = None
sigs: List[BLSSignature] = []
sigs: List[G2Element] = []
for coin in cc_spends:
if output_created is None:
newinnerpuzhash = await self.get_new_inner_hash()
@ -931,7 +927,7 @@ class CCWallet:
)
sigs = sigs + await self.get_sigs(innerpuz, innersol)
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
spend_bundle = SpendBundle(list_of_solutions, aggsig)
return spend_bundle

View File

@ -1,9 +1,9 @@
from typing import Optional, Tuple
from clvm_tools import binutils
from blspy import AugSchemeMPL
import clvm
import string
from src.types.BLSSignature import BLSSignature
from src.types.program import Program
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
@ -120,7 +120,7 @@ def cc_generate_eve_spend(coin: Coin, full_puzzle: Program):
coin.parent_coin_info, coin.puzzle_hash, coin.amount
)
list_of_solutions = [CoinSolution(coin, clvm.to_sexp_f([full_puzzle, solution]),)]
aggsig = BLSSignature.aggregate([])
aggsig = AugSchemeMPL.aggregate([])
spend_bundle = SpendBundle(list_of_solutions, aggsig)
return spend_bundle

View File

@ -1,5 +1,5 @@
from dataclasses import dataclass
from blspy import PublicKey
from blspy import G1Element
from src.types.sized_bytes import bytes32
from src.util.streamable import Streamable, streamable
@ -17,6 +17,6 @@ class DerivationRecord(Streamable):
index: uint32
puzzle_hash: bytes32
pubkey: PublicKey
pubkey: G1Element
wallet_type: WalletType
wallet_id: uint32

29
src/wallet/derive_keys.py Normal file
View File

@ -0,0 +1,29 @@
from blspy import PrivateKey
from src.util.ints import uint32
# EIP 2334 bls key derivation
# https://eips.ethereum.org/EIPS/eip-2334
# 12381 = bls spec number
# 8444 = Chia blockchain number and port number
# 0, 1, 2, 3, farmer, pool, wallet, local key numbers
def master_sk_to_farmer_sk(master: PrivateKey) -> PrivateKey:
return master.derive_child(12381).derive_child(8444).derive_child(0).derive_child(0)
def master_sk_to_pool_sk(master: PrivateKey) -> PrivateKey:
return master.derive_child(12381).derive_child(8444).derive_child(1).derive_child(0)
def master_sk_to_wallet_sk(master: PrivateKey, index: uint32) -> PrivateKey:
return (
master.derive_child(12381)
.derive_child(8444)
.derive_child(2)
.derive_child(index)
)
def master_sk_to_local_sk(master: PrivateKey) -> PrivateKey:
return master.derive_child(12381).derive_child(8444).derive_child(3).derive_child(0)

View File

@ -22,7 +22,7 @@ from src.types.program import Program
from . import p2_conditions
def puzzle_for_pk(public_key) -> Program:
def puzzle_for_pk(public_key: bytes) -> Program:
aggsig = ConditionOpcode.AGG_SIG[0]
TEMPLATE = (
f"(c (c (q {aggsig}) (c (q 0x%s) (c (sha256tree (f (a))) (q ())))) "

View File

@ -8,11 +8,10 @@ from typing import Dict, Optional, List, Tuple, Any
import clvm
import json
from blspy import ExtendedPrivateKey
from blspy import PrivateKey, AugSchemeMPL
from clvm_tools import binutils
from src.server.server import ChiaServer
from src.types.BLSSignature import BLSSignature
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.program import Program
@ -33,6 +32,7 @@ from src.wallet.wallet import Wallet
from src.wallet.wallet_coin_record import WalletCoinRecord
from src.wallet.wallet_info import WalletInfo
from src.wallet.derivation_record import DerivationRecord
from src.wallet.derive_keys import master_sk_to_wallet_sk
@dataclass(frozen=True)
@ -49,7 +49,7 @@ class RLInfo(Streamable):
class RLWallet:
private_key: ExtendedPrivateKey
private_key: PrivateKey
key_config: Dict
config: Dict
server: Optional[ChiaServer]
@ -77,8 +77,10 @@ class RLWallet:
assert unused is not None
sk_hex = key_config["wallet_sk"]
private_key = ExtendedPrivateKey.from_bytes(bytes.fromhex(sk_hex))
pubkey_bytes: bytes = bytes(private_key.public_child(unused).get_public_key())
private_key = PrivateKey.from_bytes(bytes.fromhex(sk_hex))
pubkey_bytes: bytes = bytes(
master_sk_to_wallet_sk(private_key, unused).get_g1()
)
rl_info = RLInfo("admin", pubkey_bytes, None, None, None, None, None, None)
info_as_string = json.dumps(rl_info.to_json_dict())
@ -127,9 +129,9 @@ class RLWallet:
assert unused is not None
sk_hex = key_config["wallet_sk"]
private_key = ExtendedPrivateKey.from_bytes(bytes.fromhex(sk_hex))
private_key = PrivateKey.from_bytes(bytes.fromhex(sk_hex))
pubkey_bytes: bytes = bytes(
private_key.public_child(unused).get_public_key()
master_sk_to_wallet_sk(private_key, unused).get_g1()
)
rl_info = RLInfo("user", None, pubkey_bytes, None, None, None, None, None)
@ -173,7 +175,7 @@ class RLWallet:
self.config = config
self.key_config = key_config
sk_hex = self.key_config["wallet_sk"]
self.private_key = ExtendedPrivateKey.from_bytes(bytes.fromhex(sk_hex))
self.private_key = PrivateKey.from_bytes(bytes.fromhex(sk_hex))
if name:
self.log = logging.getLogger(name)
else:
@ -342,10 +344,10 @@ class RLWallet:
index_for_puzzlehash = await self.wallet_state_manager.puzzle_store.index_for_puzzle_hash(
puzzle_hash
)
if index_for_puzzlehash == -1:
raise Exception("index_for_puzzlehash == -1")
pubkey = self.private_key.public_child(index_for_puzzlehash).get_public_key()
private = self.private_key.private_child(index_for_puzzlehash).get_private_key()
if index_for_puzzlehash is None:
raise Exception("index_for_puzzlehash is None")
private = master_sk_to_wallet_sk(self.private_key, index_for_puzzlehash)
pubkey = private.get_g1()
return pubkey, private
async def get_keys_pk(self, clawback_pubkey: bytes):
@ -355,10 +357,11 @@ class RLWallet:
index_for_pubkey = await self.wallet_state_manager.puzzle_store.index_for_pubkey(
clawback_pubkey.hex()
)
if index_for_pubkey == -1:
raise Exception("index_for_pubkey == -1")
pubkey = self.private_key.public_child(index_for_pubkey).get_public_key()
private = self.private_key.private_child(index_for_pubkey).get_private_key()
if index_for_pubkey is None:
raise Exception("index_for_pubkey is None")
private = master_sk_to_wallet_sk(self.private_key, index_for_pubkey)
pubkey = private.get_g1()
return pubkey, private
async def get_rl_coin(self) -> Optional[Coin]:
@ -424,7 +427,7 @@ class RLWallet:
signature = secretkey.sign(Program(solution.solution).get_hash())
sigs.append(signature)
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
solution_list: List[CoinSolution] = []
for puzzle, coin_solution in spends:
@ -468,7 +471,7 @@ class RLWallet:
pubkey, secretkey = await self.get_keys_pk(clawback_pubkey)
signature = secretkey.sign(Program(solution.solution).get_hash())
sigs.append(signature)
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
solution_list = []
for puzzle, coin_solution in spends:
solution_list.append(
@ -559,7 +562,7 @@ class RLWallet:
)
)
aggsig = BLSSignature.aggregate([signature])
aggsig = AugSchemeMPL.aggregate([signature])
return SpendBundle(list_of_coinsolutions, aggsig)

View File

@ -6,8 +6,8 @@ from typing import Dict, Optional, Tuple, List, Any
import logging
import clvm
from blspy import AugSchemeMPL, G2Element
from src.types.BLSSignature import BLSSignature
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.program import Program
@ -552,7 +552,7 @@ class TradeManager:
),
inner_solution,
)
aggsig = BLSSignature.aggregate([BLSSignature.aggregate(sig), aggsig])
aggsig = AugSchemeMPL.aggregate([sig, aggsig])
inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash(
coloured_coin.puzzle_hash
)
@ -647,8 +647,10 @@ class TradeManager:
)
)
sig = await wallets[colour].get_sigs(auditor_inner_puzzle, innersol)
aggsig = BLSSignature.aggregate([BLSSignature.aggregate(sig), aggsig])
sigs: List[G2Element] = await wallets[colour].get_sigs(
auditor_inner_puzzle, innersol
)
aggsig = AugSchemeMPL.aggregate(sigs + [aggsig])
solution = cc_wallet_puzzles.cc_make_solution(
core,

View File

@ -2,7 +2,7 @@ import time
from typing import Dict, Optional, List, Tuple, Set, Any
import clvm
import logging
from src.types.BLSSignature import BLSSignature
from blspy import G2Element, AugSchemeMPL
from src.types.coin import Coin
from src.types.coin_solution import CoinSolution
from src.types.program import Program
@ -12,10 +12,9 @@ from src.util.condition_tools import (
conditions_for_solution,
conditions_dict_for_solution,
conditions_by_opcode,
hash_key_pairs_for_conditions_dict,
pkm_pairs_for_conditions_dict,
)
from src.util.ints import uint64, uint32
from src.wallet.BLSPrivateKey import BLSPrivateKey
from src.wallet.abstract_wallet import AbstractWallet
from src.wallet.puzzles.p2_conditions import puzzle_for_conditions
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
@ -292,7 +291,6 @@ class Wallet(AbstractWallet):
return None
pubkey, secretkey = keys
secretkey = BLSPrivateKey(secretkey)
code_ = [puzzle, solution.solution]
sexp = clvm.to_sexp_f(code_)
@ -305,14 +303,14 @@ class Wallet(AbstractWallet):
conditions_dict = conditions_by_opcode(con)
# Create signature
for pk_message in hash_key_pairs_for_conditions_dict(
for _, msg in pkm_pairs_for_conditions_dict(
conditions_dict, bytes(solution.coin)
):
signature = secretkey.sign(pk_message.message_hash)
signature = AugSchemeMPL.sign(secretkey, msg)
signatures.append(signature)
# Aggregate signatures
aggsig = BLSSignature.aggregate(signatures)
aggsig = AugSchemeMPL.aggregate(signatures)
solution_list: List[CoinSolution] = [
CoinSolution(
coin_solution.coin, clvm.to_sexp_f([puzzle, coin_solution.solution])
@ -399,17 +397,16 @@ class Wallet(AbstractWallet):
# I think this should be a the default way the wallet gets signatures in sign_transaction()
async def get_sigs_for_innerpuz_with_innersol(
self, innerpuz: Program, innersol: Program
) -> List[BLSSignature]:
) -> List[G2Element]:
puzzle_hash = innerpuz.get_tree_hash()
pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash)
private = BLSPrivateKey(private)
sigs: List[BLSSignature] = []
sigs: List[G2Element] = []
code_ = [innerpuz, innersol]
sexp = Program.to(code_)
error, conditions, cost = conditions_dict_for_solution(sexp)
if conditions is not None:
for _ in hash_key_pairs_for_conditions_dict(conditions):
signature = private.sign(_.message_hash)
for _, msg in pkm_pairs_for_conditions_dict(conditions):
signature = AugSchemeMPL.sign(private, msg)
sigs.append(signature)
return sigs
@ -438,7 +435,7 @@ class Wallet(AbstractWallet):
# Create coin solutions for each utxo
output_created = None
sigs: List[BLSSignature] = []
sigs: List[G2Element] = []
for coin in utxos:
pubkey, secretkey = await self.wallet_state_manager.get_keys(
coin.puzzle_hash
@ -457,6 +454,6 @@ class Wallet(AbstractWallet):
new_sigs = await self.get_sigs_for_innerpuz_with_innersol(puzzle, solution)
sigs = sigs + new_sigs
aggsig = BLSSignature.aggregate(sigs)
aggsig = AugSchemeMPL.aggregate(sigs)
spend_bundle = SpendBundle(list_of_solutions, aggsig)
return spend_bundle

View File

@ -7,7 +7,7 @@ from pathlib import Path
import random
import logging
import traceback
from blspy import ExtendedPrivateKey
from blspy import PrivateKey
from src.full_node.full_node import OutboundMessageGenerator
from src.types.peer_info import PeerInfo
@ -121,10 +121,10 @@ class WalletNode:
)
return False
private_key: Optional[ExtendedPrivateKey] = None
private_key: Optional[PrivateKey] = None
if public_key_fingerprint is not None:
for sk, _ in private_keys:
if sk.get_public_key().get_fingerprint() == public_key_fingerprint:
if sk.get_g1().get_fingerprint() == public_key_fingerprint:
private_key = sk
break
else:
@ -133,7 +133,7 @@ class WalletNode:
if private_key is None:
raise RuntimeError("Invalid fingerprint {public_key_fingerprint}")
db_path_key_suffix = str(private_key.get_public_key().get_fingerprint())
db_path_key_suffix = str(private_key.get_g1().get_fingerprint())
path = path_from_root(
self.root_path, f"{self.config['database_path']}-{db_path_key_suffix}"
)

View File

@ -1,5 +1,5 @@
import asyncio
from blspy import PublicKey
from blspy import G1Element
from typing import Set, Tuple, Optional, List
import aiosqlite
import logging
@ -124,7 +124,7 @@ class WalletPuzzleStore:
return DerivationRecord(
row[0],
bytes.fromhex(row[2]),
PublicKey.from_bytes(bytes.fromhex(row[1])),
G1Element.from_bytes(bytes.fromhex(row[1])),
row[3],
row[4],
)
@ -147,7 +147,7 @@ class WalletPuzzleStore:
return DerivationRecord(
row[0],
bytes.fromhex(row[2]),
PublicKey.from_bytes(bytes.fromhex(row[1])),
G1Element.from_bytes(bytes.fromhex(row[1])),
row[3],
row[4],
)
@ -177,7 +177,7 @@ class WalletPuzzleStore:
return row is not None
async def index_for_pubkey(self, pubkey: PublicKey) -> Optional[uint32]:
async def index_for_pubkey(self, pubkey: G1Element) -> Optional[uint32]:
"""
Returns derivation paths for the given pubkey.
Returns None if not present.

View File

@ -8,7 +8,7 @@ import asyncio
import aiosqlite
from chiabip158 import PyBIP158
from blspy import PublicKey, ExtendedPrivateKey
from blspy import PrivateKey, G1Element
from src.consensus.constants import ConsensusConstants
from src.types.coin import Coin
@ -44,6 +44,7 @@ from src.types.program import Program
from src.wallet.derivation_record import DerivationRecord
from src.wallet.util.wallet_types import WalletType
from src.consensus.find_fork_point import find_fork_point_in_chain
from src.wallet.derive_keys import master_sk_to_wallet_sk
class WalletStateManager:
@ -81,14 +82,14 @@ class WalletStateManager:
main_wallet: Wallet
wallets: Dict[uint32, Any]
private_key: ExtendedPrivateKey
private_key: PrivateKey
trade_manager: TradeManager
generate_count: int
@staticmethod
async def create(
private_key: ExtendedPrivateKey,
private_key: PrivateKey,
config: Dict,
db_path: Path,
constants: ConsensusConstants,
@ -198,18 +199,15 @@ class WalletStateManager:
return self
def get_public_key(self, index: uint32) -> PublicKey:
pubkey = self.private_key.public_child(index).get_public_key()
return pubkey
def get_public_key(self, index: uint32) -> G1Element:
return master_sk_to_wallet_sk(self.private_key, index).get_g1()
async def get_keys(
self, hash: bytes32
) -> Optional[Tuple[PublicKey, ExtendedPrivateKey]]:
async def get_keys(self, hash: bytes32) -> Optional[Tuple[G1Element, PrivateKey]]:
index_for_puzzlehash = await self.puzzle_store.index_for_puzzle_hash(hash)
if index_for_puzzlehash == -1:
if index_for_puzzlehash is None:
raise ValueError(f"No key for this puzzlehash {hash})")
pubkey = self.private_key.public_child(index_for_puzzlehash).get_public_key()
private = self.private_key.private_child(index_for_puzzlehash).get_private_key()
private = master_sk_to_wallet_sk(self.private_key, index_for_puzzlehash)
pubkey = private.get_g1()
return pubkey, private
async def create_more_puzzle_hashes(self, from_zero: bool = False):
@ -247,7 +245,7 @@ class WalletStateManager:
start_index = 0
for index in range(start_index, unused + to_generate):
pubkey: PublicKey = self.get_public_key(uint32(index))
pubkey: G1Element = self.get_public_key(uint32(index))
puzzle: Program = target_wallet.puzzle_for_pk(bytes(pubkey))
puzzlehash: bytes32 = puzzle.get_tree_hash()
self.log.info(

View File

@ -1,10 +1,11 @@
import asyncio
import time
from pathlib import Path
from secrets import token_bytes
import aiosqlite
import pytest
from blspy import PrivateKey
from blspy import PrivateKey, AugSchemeMPL
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
from src.types.full_block import FullBlock
@ -14,7 +15,6 @@ from src.util.ints import uint8, uint64, uint32
from src.util.errors import Err
from src.types.sized_bytes import bytes32
from src.types.pool_target import PoolTarget
from src.types.BLSSignature import BLSSignature
from src.full_node.block_store import BlockStore
from src.full_node.coin_store import CoinStore
from src.consensus.find_fork_point import find_fork_point_in_chain
@ -107,7 +107,7 @@ class TestBlockValidation:
blocks[9].header.data.extension_data,
blocks[9].header.data.generator_hash,
),
blocks[9].header.harvester_signature,
blocks[9].header.plot_signature,
),
blocks[9].transactions_generator,
blocks[9].transactions_filter,
@ -245,7 +245,9 @@ class TestBlockValidation:
blocks[9].proof_of_time,
Header(
blocks[9].header.data,
PrivateKey.from_seed(b"0").sign_prepend(b"random junk"),
AugSchemeMPL.sign(
PrivateKey.from_seed(bytes([5] * 32)), token_bytes(32)
),
),
blocks[9].transactions_generator,
blocks[9].transactions_filter,
@ -416,7 +418,7 @@ class TestBlockValidation:
blocks[9].header.data.farmer_rewards_puzzle_hash,
blocks[9].header.data.total_transaction_fees,
pool_target,
BLSSignature(bytes(agg_sig)),
agg_sig,
blocks[9].header.data.cost,
blocks[9].header.data.extension_data,
blocks[9].header.data.generator_hash,
@ -462,7 +464,7 @@ class TestBlockValidation:
blocks[9].header.data.farmer_rewards_puzzle_hash,
blocks[9].header.data.total_transaction_fees,
blocks[9].header.data.pool_target,
BLSSignature(bytes(agg_sig)),
agg_sig,
blocks[9].header.data.cost,
blocks[9].header.data.extension_data,
blocks[9].header.data.generator_hash,

View File

@ -7,7 +7,6 @@ from clvm.casts import int_to_bytes
from src.server.outbound_message import OutboundMessage
from src.protocols import full_node_protocol
from src.types.BLSSignature import BLSSignature
from src.types.coin_solution import CoinSolution
from src.types.condition_var_pair import ConditionVarPair
from src.types.condition_opcodes import ConditionOpcode
@ -16,7 +15,7 @@ from src.types.spend_bundle import SpendBundle
from src.util.condition_tools import (
conditions_for_solution,
conditions_by_opcode,
hash_key_pairs_for_conditions_dict,
pkm_pairs_for_conditions_dict,
)
from src.util.ints import uint64
from tests.setup_nodes import setup_two_nodes, test_constants, bt
@ -864,13 +863,10 @@ class TestMempool:
assert con is not None
conditions_dict = conditions_by_opcode(con)
hash_key_pairs = hash_key_pairs_for_conditions_dict(
conditions_dict, solution.coin.name()
)
assert len(hash_key_pairs) == 1
pkm_pairs = pkm_pairs_for_conditions_dict(conditions_dict, solution.coin.name())
assert len(pkm_pairs) == 1
pk_pair: BLSSignature.PkMessagePair = hash_key_pairs[0]
assert pk_pair.message_hash == solution.solution.first().get_tree_hash()
assert pkm_pairs[0][1] == solution.solution.first().get_tree_hash()
spend_bundle = wallet_a.sign_transaction(unsigned)
assert spend_bundle is not None

View File

@ -1,31 +1,24 @@
import blspy
from blspy import PrivateKey
from src.types.coin_solution import CoinSolution
from src.types.spend_bundle import SpendBundle
from src.wallet.BLSPrivateKey import BLSPrivateKey
from src.wallet.puzzles import p2_delegated_puzzle
from src.wallet.puzzles.puzzle_utils import make_create_coin_condition
from tests.util.key_tool import KeyTool
from src.util.ints import uint32
from src.wallet.derive_keys import master_sk_to_wallet_sk
HIERARCHICAL_PRIVATE_KEY = blspy.ExtendedPrivateKey.from_seed(b"foo")
MASTER_KEY = PrivateKey.from_seed(bytes([1] * 32))
def bls_private_key_for_index(index):
return BLSPrivateKey.from_bytes(
bytes(HIERARCHICAL_PRIVATE_KEY.private_child(index).get_private_key())
def puzzle_program_for_index(index: uint32):
return p2_delegated_puzzle.puzzle_for_pk(
bytes(master_sk_to_wallet_sk(MASTER_KEY, index).get_g1())
)
def public_key_bytes_for_index(index):
return bytes(HIERARCHICAL_PRIVATE_KEY.private_child(index).get_public_key())
def puzzle_program_for_index(index):
return p2_delegated_puzzle.puzzle_for_pk(public_key_bytes_for_index(index))
def puzzle_hash_for_index(index):
def puzzle_hash_for_index(index: uint32):
return puzzle_program_for_index(index).get_hash()
@ -39,7 +32,7 @@ def conditions_for_payment(puzzle_hash_amount_pairs):
def make_default_keyUtil():
keychain = KeyTool()
private_keys = [bls_private_key_for_index(_) for _ in range(10)]
private_keys = [master_sk_to_wallet_sk(MASTER_KEY, uint32(i)) for i in range(10)]
secret_exponents = [int.from_bytes(bytes(_), "big") for _ in private_keys]
keychain.add_secret_exponents(secret_exponents)
return keychain

View File

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -102,7 +102,7 @@ class TestRpc:
filename,
18,
stream_plot_info(
bt.pool_pk, bt.farmer_pk, PrivateKey.from_seed(b"123")
bt.pool_pk, bt.farmer_pk, PrivateKey.from_seed(bytes([4] * 32))
),
token_bytes(32),
128,

View File

@ -79,13 +79,21 @@ async def setup_full_node(
30,
config["target_peer_count"],
)
FullNodeApi = FullNodeSimulator if simulator else FullNode
api = FullNodeApi(
config=config,
root_path=bt.root_path,
consensus_constants=consensus_constants,
name=f"full_node_{port}",
)
if not simulator:
api: FullNode = FullNode(
config=config,
root_path=bt.root_path,
consensus_constants=consensus_constants,
name=f"full_node_{port}",
)
else:
api = FullNodeSimulator(
config=config,
root_path=bt.root_path,
consensus_constants=consensus_constants,
name=f"full_node_sim_{port}",
bt=bt,
)
started = asyncio.Event()
@ -146,7 +154,7 @@ async def setup_wallet_node(
consensus_constants = constants_for_dic(dic)
first_pk = keychain.get_first_public_key()
assert first_pk is not None
db_path_key_suffix = str(first_pk.get_public_key().get_fingerprint())
db_path_key_suffix = str(first_pk.get_fingerprint())
db_name = f"test-wallet-db-{port}"
db_path = bt.root_path / f"test-wallet-db-{port}-{db_path_key_suffix}"
if db_path.exists():
@ -263,7 +271,7 @@ async def setup_farmer(port, full_node_port: Optional[uint16] = None, dic={}):
consensus_constants = constants_for_dic(dic)
config["xch_target_puzzle_hash"] = bt.farmer_ph.hex()
config["pool_public_keys"] = [bytes(pk).hex() for pk in bt.all_pubkeys]
config["pool_public_keys"] = [bytes(pk).hex() for pk in bt.pool_pubkeys]
config_pool["xch_target_puzzle_hash"] = bt.pool_ph.hex()
if full_node_port:
connect_peers = [PeerInfo(self_hostname, full_node_port)]

View File

@ -10,8 +10,8 @@ from tests.time_out_assert import time_out_assert, time_out_assert_custom_interv
test_constants, bt = make_test_constants_with_genesis(
{
"DIFFICULTY_STARTING": 500,
"MIN_ITERS_STARTING": 500,
"DIFFICULTY_STARTING": 1000,
"MIN_ITERS_STARTING": 100000,
"NUMBER_ZERO_BITS_CHALLENGE_SIG": 1,
}
)
@ -40,7 +40,7 @@ class TestSimulation:
node1, node2, _, _, _, _, _, _, _ = simulation
# Use node2 to test node communication, since only node1 extends the chain.
await time_out_assert(300, node_height_at_least, True, node2, 7)
await time_out_assert(500, node_height_at_least, True, node2, 10)
# Wait additional 2 minutes to get a compact block.
max_height = node1.blockchain.lca_block.height

View File

@ -8,12 +8,13 @@ from src.util.condition_tools import (
from src.wallet.puzzles import p2_delegated_puzzle
from src.wallet.puzzles.puzzle_utils import make_create_coin_condition
from tests.keys import puzzle_program_for_index
from src.util.ints import uint32
def test_1():
puzzle_program_0 = puzzle_program_for_index(0)
puzzle_program_1 = puzzle_program_for_index(1)
puzzle_program_2 = puzzle_program_for_index(2)
puzzle_program_0 = puzzle_program_for_index(uint32(0))
puzzle_program_1 = puzzle_program_for_index(uint32(1))
puzzle_program_2 = puzzle_program_for_index(uint32(2))
conditions = [
make_create_coin_condition(std_hash(bytes(pp)), amount)

View File

@ -12,8 +12,8 @@ class TestProofOfSpace:
for _ in range(num_trials):
challenge_hash = token_bytes(32)
plot_seed = token_bytes(32)
if ProofOfSpace.can_create_proof(plot_seed, challenge_hash, 8):
plot_id = token_bytes(32)
if ProofOfSpace.can_create_proof(plot_id, challenge_hash, 8):
success_count += 1
assert abs((success_count * 256 / num_trials) - 1) < 0.3

View File

@ -1,17 +1,17 @@
import time
from secrets import token_bytes
from blspy import ExtendedPrivateKey
from blspy import PrivateKey, AugSchemeMPL
from clvm import run_program
from clvm_tools import binutils
from src.types.condition_opcodes import ConditionOpcode
from src.types.condition_var_pair import ConditionVarPair
from src.types.BLSSignature import BLSSignature
from src.types.program import Program
from src.wallet.BLSPrivateKey import BLSPrivateKey
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
from src.util.wallet_tools import WalletTool
from src.util.ints import uint32
from src.wallet.derive_keys import master_sk_to_wallet_sk
def float_to_str(f):
@ -128,16 +128,14 @@ if __name__ == "__main__":
"""
wallet_tool = WalletTool()
benchmark_all_operators()
extended_secret_key: ExtendedPrivateKey = ExtendedPrivateKey.from_seed(b"a")
secret_key: PrivateKey = PrivateKey.from_seed(bytes([2] * 32))
puzzles = []
solutions = []
private_keys = []
public_keys = []
for i in range(0, 1000):
private_key: BLSPrivateKey = BLSPrivateKey(
extended_secret_key.private_child(i).get_private_key()
)
private_key: PrivateKey = master_sk_to_wallet_sk(secret_key, uint32(i))
public_key = private_key.public_key()
solution = wallet_tool.make_solution(
{
@ -148,7 +146,7 @@ if __name__ == "__main__":
]
}
)
puzzle = puzzle_for_pk(public_key)
puzzle = puzzle_for_pk(bytes(public_key))
puzzles.append(puzzle)
solutions.append(solution)
private_keys.append(private_key)
@ -166,17 +164,18 @@ if __name__ == "__main__":
print(f"Puzzle_time is: {puzzle_time}")
print(f"Puzzle cost sum is: {clvm_cost}")
private_key = BLSPrivateKey(extended_secret_key.private_child(0).get_private_key())
public_key = private_key.public_key()
private_key = master_sk_to_wallet_sk(secret_key, uint32(0))
public_key = private_key.get_g1()
message = token_bytes()
signature = private_key.sign(message)
pk_message_pair = BLSSignature.PkMessagePair(public_key, message)
signature = AugSchemeMPL.sign(private_key, message)
pk_message_pair = (public_key, message)
# Run AggSig 1000 times
agg_sig_start = time.time()
agg_sig_cost = 0
for i in range(0, 1000):
valid = signature.validate([pk_message_pair])
valid = AugSchemeMPL.verify(public_key, message, signature)
assert valid
agg_sig_cost += 20
agg_sig_end = time.time()
agg_sig_time = agg_sig_end - agg_sig_start

View File

@ -1,10 +1,9 @@
from src.types.BLSSignature import BLSSignature
from src.util.condition_tools import (
conditions_by_opcode,
hash_key_pairs_for_conditions_dict,
pkm_pairs_for_conditions_dict,
conditions_for_solution,
)
from src.wallet.BLSPrivateKey import BLSPrivateKey
from blspy import PrivateKey, AugSchemeMPL
class KeyTool(dict):
@ -14,21 +13,21 @@ class KeyTool(dict):
def add_secret_exponents(self, secret_exponents):
for _ in secret_exponents:
bls_private_key = BLSPrivateKey.from_secret_exponent(_)
self[bls_private_key.public_key()] = bls_private_key
bls_private_key = PrivateKey.from_bytes(_.to_bytes(32, "big"))
self[bls_private_key.get_g1()] = bls_private_key
def sign(self, aggsig_pair):
bls_private_key = self.get(aggsig_pair.public_key)
if not bls_private_key:
raise ValueError("unknown pubkey %s" % aggsig_pair.public_key)
return bls_private_key.sign(aggsig_pair.message_hash)
def sign(self, pk, msg):
private = self.get(pk)
if not private:
raise ValueError("unknown pubkey %s" % pk)
return AugSchemeMPL.sign(private, msg)
def signature_for_solution(self, solution, coin_name):
signatures = []
conditions = conditions_for_solution(solution)
assert conditions[1] is not None
conditions_dict = conditions_by_opcode(conditions[1])
for _ in hash_key_pairs_for_conditions_dict(conditions_dict, coin_name):
signature = self.sign(_)
for pk, msg in pkm_pairs_for_conditions_dict(conditions_dict, coin_name):
signature = self.sign(pk, msg)
signatures.append(signature)
return BLSSignature.aggregate(signatures)
return AugSchemeMPL.aggregate(signatures)

View File

@ -1,6 +1,6 @@
import unittest
from secrets import token_bytes
from blspy import ExtendedPrivateKey
from blspy import PrivateKey
from src.util.keychain import (
Keychain,
generate_mnemonic,
@ -42,8 +42,8 @@ class TesKeychain(unittest.TestCase):
assert len(kc.get_all_private_keys()) == 2
seed_2 = entropy_to_seed(entropy_2, "")
seed_key_2 = ExtendedPrivateKey.from_seed(seed_2)
kc.delete_key_by_fingerprint(seed_key_2.get_public_key().get_fingerprint())
seed_key_2 = PrivateKey.from_seed(seed_2)
kc.delete_key_by_fingerprint(seed_key_2.get_g1().get_fingerprint())
assert kc._get_free_private_key_index() == 1
assert len(kc.get_all_private_keys()) == 1

View File

@ -36,7 +36,7 @@ class TestPuzzleStore:
DerivationRecord(
uint32(i),
token_bytes(32),
PrivateKey.from_seed(token_bytes(5)).get_public_key(),
PrivateKey.from_seed(token_bytes(32)).get_g1(),
WalletType.STANDARD_WALLET,
uint32(1),
)
@ -45,7 +45,7 @@ class TestPuzzleStore:
DerivationRecord(
uint32(i),
token_bytes(32),
PrivateKey.from_seed(token_bytes(5)).get_public_key(),
PrivateKey.from_seed(token_bytes(32)).get_g1(),
WalletType.RATE_LIMITED,
uint32(2),
)