Skip to content

Commit 9eb7c40

Browse files
Merging with base changes
1 parent 45ab4d9 commit 9eb7c40

21 files changed

+816
-89
lines changed

build_scripts/build_macos.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ if [ "$LAST_EXIT_CODE" -ne 0 ]; then
4444
exit $LAST_EXIT_CODE
4545
fi
4646

47+
# sets the version for salvia-blockchain in package.json
48+
brew install jq
49+
cp package.json package.json.orig
50+
jq --arg VER "$SALVIA_INSTALLER_VERSION" '.version=$VER' package.json > temp.json && mv temp.json package.json
51+
4752
electron-packager . Salvia --asar.unpack="**/daemon/**" --platform=darwin \
4853
--icon=src/assets/img/Salvia.icns --overwrite --app-bundle-id=net.salvia.blockchain \
4954
--appVersion=$SALVIA_INSTALLER_VERSION
5055
LAST_EXIT_CODE=$?
56+
57+
# reset the package.json to the original
58+
mv package.json.orig package.json
59+
5160
if [ "$LAST_EXIT_CODE" -ne 0 ]; then
5261
echo >&2 "electron-packager failed!"
5362
exit $LAST_EXIT_CODE

build_scripts/build_windows.ps1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ $packageName = "Salvia-$packageVersion"
107107

108108
Write-Output "packageName is $packageName"
109109

110+
Write-Output " ---"
111+
Write-Output "fix version in package.json"
112+
choco install jq
113+
cp package.json package.json.orig
114+
jq --arg VER "$env:SALVIA_INSTALLER_VERSION" '.version=$VER' package.json > temp.json
115+
rm package.json
116+
mv temp.json package.json
117+
Write-Output " ---"
118+
110119
Write-Output " ---"
111120
Write-Output "electron-packager"
112121
electron-packager . Salvia --asar.unpack="**\daemon\**" --overwrite --icon=.\src\assets\img\salvia.ico --app-version=$packageVersion

salvia/consensus/blockchain.py

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from enum import Enum
77
from typing import Dict, List, Optional, Set, Tuple, Union
88

9+
from clvm.casts import int_from_bytes
10+
911
from salvia.consensus.block_body_validation import validate_block_body
1012
from salvia.consensus.block_header_validation import validate_finished_header_block, validate_unfinished_header_block
1113
from salvia.consensus.block_record import BlockRecord
@@ -18,12 +20,14 @@
1820
from salvia.consensus.multiprocess_validation import PreValidationResult, pre_validate_blocks_multiprocessing
1921
from salvia.full_node.block_store import BlockStore
2022
from salvia.full_node.coin_store import CoinStore
23+
from salvia.full_node.hint_store import HintStore
2124
from salvia.full_node.mempool_check_conditions import get_name_puzzle_conditions
2225
from salvia.types.blockchain_format.coin import Coin
2326
from salvia.types.blockchain_format.sized_bytes import bytes32
2427
from salvia.types.blockchain_format.sub_epoch_summary import SubEpochSummary
2528
from salvia.types.blockchain_format.vdf import VDFInfo
2629
from salvia.types.coin_record import CoinRecord
30+
from salvia.types.condition_opcodes import ConditionOpcode
2731
from salvia.types.end_of_slot_bundle import EndOfSubSlotBundle
2832
from salvia.types.full_block import FullBlock
2933
from salvia.types.generator_types import BlockGenerator, GeneratorArg
@@ -83,12 +87,11 @@ class Blockchain(BlockchainInterface):
8387
# Lock to prevent simultaneous reads and writes
8488
lock: asyncio.Lock
8589
compact_proof_lock: asyncio.Lock
90+
hint_store: HintStore
8691

8792
@staticmethod
8893
async def create(
89-
coin_store: CoinStore,
90-
block_store: BlockStore,
91-
consensus_constants: ConsensusConstants,
94+
coin_store: CoinStore, block_store: BlockStore, consensus_constants: ConsensusConstants, hint_store: HintStore
9295
):
9396
"""
9497
Initializes a blockchain with the BlockRecords from disk, assuming they have all been
@@ -112,6 +115,7 @@ async def create(
112115
self._shut_down = False
113116
await self._load_chain_from_store()
114117
self._seen_compact_proofs = set()
118+
self.hint_store = hint_store
115119
return self
116120

117121
def shut_down(self):
@@ -164,7 +168,12 @@ async def receive_block(
164168
block: FullBlock,
165169
pre_validation_result: Optional[PreValidationResult] = None,
166170
fork_point_with_peak: Optional[uint32] = None,
167-
) -> Tuple[ReceiveBlockResult, Optional[Err], Optional[uint32], List[CoinRecord]]:
171+
) -> Tuple[
172+
ReceiveBlockResult,
173+
Optional[Err],
174+
Optional[uint32],
175+
Tuple[List[CoinRecord], Dict[bytes, Dict[bytes32, CoinRecord]]],
176+
]:
168177
"""
169178
This method must be called under the blockchain lock
170179
Adds a new block into the blockchain, if it's valid and connected to the current
@@ -174,18 +183,13 @@ async def receive_block(
174183
"""
175184
genesis: bool = block.height == 0
176185
if self.contains_block(block.header_hash):
177-
return ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None, []
186+
return ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None, ([], {})
178187

179188
if not self.contains_block(block.prev_header_hash) and not genesis:
180-
return (
181-
ReceiveBlockResult.DISCONNECTED_BLOCK,
182-
Err.INVALID_PREV_BLOCK_HASH,
183-
None,
184-
[],
185-
)
189+
return (ReceiveBlockResult.DISCONNECTED_BLOCK, Err.INVALID_PREV_BLOCK_HASH, None, ([], {}))
186190

187191
if not genesis and (self.block_record(block.prev_header_hash).height + 1) != block.height:
188-
return ReceiveBlockResult.INVALID_BLOCK, Err.INVALID_HEIGHT, None, []
192+
return ReceiveBlockResult.INVALID_BLOCK, Err.INVALID_HEIGHT, None, ([], {})
189193

190194
npc_result: Optional[NPCResult] = None
191195
if pre_validation_result is None:
@@ -202,7 +206,7 @@ async def receive_block(
202206
try:
203207
block_generator: Optional[BlockGenerator] = await self.get_block_generator(block)
204208
except ValueError:
205-
return ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None, []
209+
return ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None, ([], {})
206210
assert block_generator is not None and block.transactions_info is not None
207211
npc_result = get_name_puzzle_conditions(
208212
block_generator,
@@ -228,7 +232,7 @@ async def receive_block(
228232
)
229233

230234
if error is not None:
231-
return ReceiveBlockResult.INVALID_BLOCK, error.code, None, []
235+
return ReceiveBlockResult.INVALID_BLOCK, error.code, None, ([], {})
232236
else:
233237
npc_result = pre_validation_result.npc_result
234238
required_iters = pre_validation_result.required_iters
@@ -247,7 +251,7 @@ async def receive_block(
247251
self.get_block_generator,
248252
)
249253
if error_code is not None:
250-
return ReceiveBlockResult.INVALID_BLOCK, error_code, None, []
254+
return ReceiveBlockResult.INVALID_BLOCK, error_code, None, ([], {})
251255

252256
block_record = block_to_block_record(
253257
self.constants,
@@ -263,7 +267,7 @@ async def receive_block(
263267
# Perform the DB operations to update the state, and rollback if something goes wrong
264268
await self.block_store.db_wrapper.begin_transaction()
265269
await self.block_store.add_full_block(header_hash, block, block_record)
266-
fork_height, peak_height, records, coin_record_change = await self._reconsider_peak(
270+
fork_height, peak_height, records, (coin_record_change, hint_changes) = await self._reconsider_peak(
267271
block_record, genesis, fork_point_with_peak, npc_result
268272
)
269273
await self.block_store.db_wrapper.commit_transaction()
@@ -286,17 +290,35 @@ async def receive_block(
286290
if fork_height is not None:
287291
# new coin records added
288292
assert coin_record_change is not None
289-
return ReceiveBlockResult.NEW_PEAK, None, fork_height, coin_record_change
293+
return ReceiveBlockResult.NEW_PEAK, None, fork_height, (coin_record_change, hint_changes)
290294
else:
291-
return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None, []
295+
return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None, ([], {})
296+
297+
def get_hint_list(self, npc_result: NPCResult) -> List[Tuple[bytes32, bytes]]:
298+
h_list = []
299+
for npc in npc_result.npc_list:
300+
for opcode, conditions in npc.conditions:
301+
if opcode == ConditionOpcode.CREATE_COIN:
302+
for condition in conditions:
303+
if len(condition.vars) > 2 and condition.vars[2] != b"":
304+
puzzle_hash, amount_bin = condition.vars[0], condition.vars[1]
305+
amount = int_from_bytes(amount_bin)
306+
coin_id = Coin(npc.coin_name, puzzle_hash, amount).name()
307+
h_list.append((coin_id, condition.vars[2]))
308+
return h_list
292309

293310
async def _reconsider_peak(
294311
self,
295312
block_record: BlockRecord,
296313
genesis: bool,
297314
fork_point_with_peak: Optional[uint32],
298315
npc_result: Optional[NPCResult],
299-
) -> Tuple[Optional[uint32], Optional[uint32], List[BlockRecord], List[CoinRecord]]:
316+
) -> Tuple[
317+
Optional[uint32],
318+
Optional[uint32],
319+
List[BlockRecord],
320+
Tuple[List[CoinRecord], Dict[bytes, Dict[bytes32, CoinRecord]]],
321+
]:
300322
"""
301323
When a new block is added, this is called, to check if the new block is the new peak of the chain.
302324
This also handles reorgs by reverting blocks which are not in the heaviest chain.
@@ -305,6 +327,8 @@ async def _reconsider_peak(
305327
"""
306328
peak = self.get_peak()
307329
lastest_coin_state: Dict[bytes32, CoinRecord] = {}
330+
hint_coin_state: Dict[bytes32, Dict[bytes32, CoinRecord]] = {}
331+
308332
if genesis:
309333
if peak is None:
310334
block: Optional[FullBlock] = await self.block_store.get_full_block(block_record.header_hash)
@@ -326,8 +350,8 @@ async def _reconsider_peak(
326350
else:
327351
added, _ = [], []
328352
await self.block_store.set_peak(block_record.header_hash)
329-
return uint32(0), uint32(0), [block_record], added
330-
return None, None, [], []
353+
return uint32(0), uint32(0), [block_record], (added, {})
354+
return None, None, [], ([], {})
331355

332356
assert peak is not None
333357
if block_record.weight > peak.weight:
@@ -372,46 +396,63 @@ async def _reconsider_peak(
372396
records_to_add = []
373397
for fetched_full_block, fetched_block_record in reversed(blocks_to_add):
374398
records_to_add.append(fetched_block_record)
375-
if fetched_block_record.is_transaction_block:
399+
if fetched_full_block.is_transaction_block():
376400
if fetched_block_record.header_hash == block_record.header_hash:
377-
tx_removals, tx_additions = await self.get_tx_removals_and_additions(
401+
tx_removals, tx_additions, npc_res = await self.get_tx_removals_and_additions(
378402
fetched_full_block, npc_result
379403
)
380404
else:
381-
tx_removals, tx_additions = await self.get_tx_removals_and_additions(fetched_full_block, None)
382-
if fetched_full_block.is_transaction_block():
383-
assert fetched_full_block.foliage_transaction_block is not None
384-
added_rec = await self.coin_store.new_block(
385-
fetched_full_block.height,
386-
fetched_full_block.foliage_transaction_block.timestamp,
387-
fetched_full_block.get_included_reward_coins(),
388-
tx_additions,
389-
tx_removals,
405+
tx_removals, tx_additions, npc_res = await self.get_tx_removals_and_additions(
406+
fetched_full_block, None
390407
)
391-
removed_rec: List[Optional[CoinRecord]] = [
392-
await self.coin_store.get_coin_record(name) for name in tx_removals
393-
]
394-
395-
# Set additions first, than removals in order to handle ephemeral coin state
396-
# Add in height order is also required
397-
record: Optional[CoinRecord]
398-
for record in added_rec:
399-
assert record
400-
lastest_coin_state[record.name] = record
401-
for record in removed_rec:
402-
assert record
403-
lastest_coin_state[record.name] = record
408+
409+
assert fetched_full_block.foliage_transaction_block is not None
410+
added_rec = await self.coin_store.new_block(
411+
fetched_full_block.height,
412+
fetched_full_block.foliage_transaction_block.timestamp,
413+
fetched_full_block.get_included_reward_coins(),
414+
tx_additions,
415+
tx_removals,
416+
)
417+
removed_rec: List[Optional[CoinRecord]] = [
418+
await self.coin_store.get_coin_record(name) for name in tx_removals
419+
]
420+
421+
# Set additions first, then removals in order to handle ephemeral coin state
422+
# Add in height order is also required
423+
record: Optional[CoinRecord]
424+
for record in added_rec:
425+
assert record
426+
lastest_coin_state[record.name] = record
427+
for record in removed_rec:
428+
assert record
429+
lastest_coin_state[record.name] = record
430+
431+
if npc_res is not None:
432+
hint_list: List[Tuple[bytes32, bytes]] = self.get_hint_list(npc_res)
433+
await self.hint_store.add_hints(hint_list)
434+
# There can be multiple coins for the same hint
435+
for coin_id, hint in hint_list:
436+
key = hint
437+
if key not in hint_coin_state:
438+
hint_coin_state[key] = {}
439+
hint_coin_state[key][coin_id] = lastest_coin_state[coin_id]
404440

405441
# Changes the peak to be the new peak
406442
await self.block_store.set_peak(block_record.header_hash)
407-
return uint32(max(fork_height, 0)), block_record.height, records_to_add, list(lastest_coin_state.values())
443+
return (
444+
uint32(max(fork_height, 0)),
445+
block_record.height,
446+
records_to_add,
447+
(list(lastest_coin_state.values()), hint_coin_state),
448+
)
408449

409450
# This is not a heavier block than the heaviest we have seen, so we don't change the coin set
410-
return None, None, [], list(lastest_coin_state.values())
451+
return None, None, [], ([], {})
411452

412453
async def get_tx_removals_and_additions(
413454
self, block: FullBlock, npc_result: Optional[NPCResult] = None
414-
) -> Tuple[List[bytes32], List[Coin]]:
455+
) -> Tuple[List[bytes32], List[Coin], Optional[NPCResult]]:
415456
if block.is_transaction_block():
416457
if block.transactions_generator is not None:
417458
if npc_result is None:
@@ -424,11 +465,11 @@ async def get_tx_removals_and_additions(
424465
safe_mode=False,
425466
)
426467
tx_removals, tx_additions = tx_removals_and_additions(npc_result.npc_list)
427-
return tx_removals, tx_additions
468+
return tx_removals, tx_additions, npc_result
428469
else:
429-
return [], []
470+
return [], [], None
430471
else:
431-
return [], []
472+
return [], [], None
432473

433474
def get_next_difficulty(self, header_hash: bytes32, new_slot: bool) -> uint64:
434475
assert self.contains_block(header_hash)

salvia/full_node/coin_store.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,13 @@ async def get_coin_records_by_names(
242242

243243
return list(coins)
244244

245+
def row_to_coin_state(self, row):
246+
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
247+
spent_h = None
248+
if row[3]:
249+
spent_h = row[2]
250+
return CoinState(coin, spent_h, row[1])
251+
245252
async def get_coin_states_by_puzzle_hashes(
246253
self,
247254
include_spent_coins: bool,
@@ -265,11 +272,7 @@ async def get_coin_states_by_puzzle_hashes(
265272

266273
await cursor.close()
267274
for row in rows:
268-
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
269-
spent_h = None
270-
if row[3]:
271-
spent_h = row[2]
272-
coins.add(CoinState(coin, spent_h, row[1]))
275+
coins.add(self.row_to_coin_state(row))
273276

274277
return list(coins)
275278

@@ -323,11 +326,7 @@ async def get_coin_state_by_ids(
323326

324327
await cursor.close()
325328
for row in rows:
326-
coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7]))
327-
spent_h = None
328-
if row[3]:
329-
spent_h = row[2]
330-
coins.add(CoinState(coin, spent_h, row[1]))
329+
coins.add(self.row_to_coin_state(row))
331330
return list(coins)
332331

333332
async def rollback_to_block(self, block_index: int) -> List[CoinRecord]:

0 commit comments

Comments
 (0)