mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-11-29 13:28:11 +03:00
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:
parent
8dca011f5e
commit
194e0c24ea
@ -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.
|
||||
|
6
electron-react/package-lock.json
generated
6
electron-react/package-lock.json
generated
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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>
|
||||
|
@ -57,7 +57,7 @@ describe("header", () => {
|
||||
total_iters: "168608294357",
|
||||
weight: "3520789508784128"
|
||||
},
|
||||
harvester_signature:
|
||||
plot_signature:
|
||||
"0x53d04fa0ee31bdc249e6db50161a6dc09d1c48ad011533b10a3eda25eb456a8b3562a1509b97f1931e334d5ed8ec6593133ecbbec5c423b733ea3cdfccc3022a7c09b0fc87fd4d0fd6cbfbedbb69d9fd43aab5c07e3452cb1f91524678340b50"
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
|
8
setup.py
8
setup.py
@ -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
|
||||
|
@ -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 "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:
|
||||
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"
|
||||
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)
|
||||
|
||||
|
||||
|
@ -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:
|
||||
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)
|
||||
else:
|
||||
print(
|
||||
"There is no mnemonic for this key, since it was imported without a seed. (Or migrated from keys.yaml)."
|
||||
)
|
||||
|
||||
|
||||
def delete(args):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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(
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
validates = AugSchemeMPL.verify(
|
||||
block.proof_of_space.pool_public_key,
|
||||
pool_target_m,
|
||||
block.header.data.aggregated_signature,
|
||||
)
|
||||
]
|
||||
if not block.header.data.aggregated_signature.validate(hash_key_pairs):
|
||||
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
|
||||
|
@ -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]
|
||||
)
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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(
|
||||
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
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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()}"
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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():
|
||||
|
@ -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
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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]:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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):
|
||||
"""
|
||||
@ -151,37 +151,37 @@ class 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)
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -67,7 +67,10 @@ def strictdataclass(cls: Any):
|
||||
try:
|
||||
item = f_type(item)
|
||||
except (TypeError, AttributeError, ValueError):
|
||||
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
|
||||
|
@ -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])
|
||||
|
@ -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)
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
29
src/wallet/derive_keys.py
Normal 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)
|
@ -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 ())))) "
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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}"
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
4
tests/plots/.gitignore
vendored
4
tests/plots/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
@ -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,
|
||||
|
@ -79,13 +79,21 @@ async def setup_full_node(
|
||||
30,
|
||||
config["target_peer_count"],
|
||||
)
|
||||
FullNodeApi = FullNodeSimulator if simulator else FullNode
|
||||
api = FullNodeApi(
|
||||
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)]
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user