From 3ed1d1102c983ea7c69ca8b6cf5b5d21c0d452a8 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Sun, 21 Jan 2024 08:44:59 -0500 Subject: [PATCH] Add a bunch of block header fields, some cert stuff EvalDelta is the main place that needs a lot still --- algosdk/block.py | 113 +++++++++++++++++++++++++++++++++++--- algosdk/encoding.py | 6 +- algosdk/transaction.py | 16 +++--- algosdk/v2client/algod.py | 2 +- examples/utils.py | 10 +++- 5 files changed, 126 insertions(+), 21 deletions(-) diff --git a/algosdk/block.py b/algosdk/block.py index a4e51981..2230c4d2 100644 --- a/algosdk/block.py +++ b/algosdk/block.py @@ -2,16 +2,16 @@ class BlockInfo: - def __init__(self, block, cert): + def __init__(self, block, certificate): self.block = block - self.cert = cert + self.certificate = certificate pass @staticmethod def undictify(d): return BlockInfo( - encoding.undictify(d.get("block")), - encoding.undictify(d.get("cert")), + block=Block.undictify(d.get("block")), + certificate=Cert.undictify(d.get("cert")), ) def __str__(self): @@ -38,6 +38,20 @@ def __init__( timestamp, genesis_id, genesis_hash, + fee_sink, + rewards_pool, + rewards_level, + rewards_rate, + rewards_residue, + rewards_calculation_round, + upgrade_propose, + upgrade_delay, + upgrade_approve, + current_protocol, + next_protocol, + next_protocol_approvals, + next_protocol_vote_before, + next_protocol_switch_on, counter, payset, ): @@ -49,6 +63,20 @@ def __init__( self.timestamp = timestamp self.genesis_id = genesis_id self.genesis_hash = genesis_hash + self.fee_sink = fee_sink + self.rewards_pool = rewards_pool + self.rewards_level = rewards_level + self.rewards_rate = rewards_rate + self.rewards_residue = rewards_residue + self.rewards_calculation_round = rewards_calculation_round + self.upgrade_propose = upgrade_propose + self.upgrade_delay = upgrade_delay + self.upgrade_approve = upgrade_approve + self.current_protocol = current_protocol + self.next_protocol = next_protocol + self.next_protocol_approvals = next_protocol_approvals + self.next_protocol_vote_before = next_protocol_vote_before + self.next_protocol_switch_on = next_protocol_switch_on self.counter = counter self.payset = payset @@ -77,6 +105,20 @@ def undictify(d): timestamp=d.get("ts", 0), genesis_id=d.get("gen"), genesis_hash=d.get("gh"), + fee_sink=encoding.encode_address(d.get("fees")), + rewards_pool=encoding.encode_address(d.get("rwd")), + rewards_level=d.get("earn", 0), + rewards_rate=d.get("rate", 0), + rewards_residue=d.get("frac", 0), + rewards_calculation_round=d.get("rwcalr", 0), + upgrade_propose=d.get("upgradeprop"), + upgrade_delay=d.get("upgradedelay", 0), + upgrade_approve=d.get("upgradeyes", False), + current_protocol=d.get("proto"), + next_protocol=d.get("nextproto"), + next_protocol_approvals=d.get("nextyes", 0), + next_protocol_vote_before=d.get("nextbefore", 0), + next_protocol_switch_on=d.get("nextswitch", 0), counter=d.get("tc", 0), payset=stxns, ) @@ -95,9 +137,66 @@ def __str__(self): class Cert: - def __init__(self): - pass + def __init__( + self, round, period, step, proposal, votes, equivocation_votes + ): + self.round = round + self.period = period + self.step = step + self.proposal = proposal + self.votes = votes + self.equivocation_votes = equivocation_votes + + @staticmethod + def undictify(d): + return Cert( + round=d.get("rnd", 0), + period=d.get("per", 0), + step=d.get("step", 0), + proposal=ProposalValue.undictify(d.get("prop")), + votes=[], + equivocation_votes=[], + ) + + def __str__(self): + return ( + "{" + + ", ".join( + [ + str(key) + ": " + str(value) + for key, value in self.__dict__.items() + ] + ) + + "}" + ) + + +class ProposalValue: + def __init__( + self, original_period, original_proposer, block_digest, encoding_digest + ): + self.original_period = original_period + self.original_proposer = original_proposer + self.block_digest = block_digest + self.encoding_digest = encoding_digest @staticmethod def undictify(d): - return Cert() + return ProposalValue( + original_period=d.get("oper", 0), + original_proposer=d.get("oprop"), + block_digest=d.get("dig"), + encoding_digest=d.get("encdig"), + ) + + def __str__(self): + return ( + "{" + + ", ".join( + [ + str(key) + ": " + str(value) + for key, value in self.__dict__.items() + ] + ) + + "}" + ) diff --git a/algosdk/encoding.py b/algosdk/encoding.py index 949fd968..25893744 100644 --- a/algosdk/encoding.py +++ b/algosdk/encoding.py @@ -71,6 +71,7 @@ def msgpack_decode(enc): decoded = algo_msgp_decode(base64.b64decode(enc)) return undictify(decoded) + def algo_msgp_decode(enc): """Performs msgpack decoding on an Algorand object. Extra care is taken so that some internal fields that are marked as strings are @@ -82,6 +83,7 @@ def algo_msgp_decode(enc): raw = msgpack.unpackb(enc, raw=True, strict_map_key=False) return cook(raw) + def cook(raw): stop = {b"gd", b"ld", b"lg"} safe = {b"type"} @@ -100,6 +102,7 @@ def cook(raw): return [cook(item) for item in raw] return raw + def undictify(d): if "type" in d: return transaction.Transaction.undictify(d) @@ -113,8 +116,6 @@ def undictify(d): return transaction.LogicSigAccount.undictify(d) if "sig" in d: return transaction.SignedTransaction.undictify(d) - if "gh" in d: # must proceed next check, since `txn` is in header too, as txn root - return block.Block.undictify(d) if "txn" in d: return transaction.Transaction.undictify(d["txn"]) if "subsig" in d: @@ -130,6 +131,7 @@ def undictify(d): if "block" in d: return block.BlockInfo.undictify(d) + def is_valid_address(addr): """ Check if the string address is a valid Algorand address. diff --git a/algosdk/transaction.py b/algosdk/transaction.py index 75cbe013..40947695 100644 --- a/algosdk/transaction.py +++ b/algosdk/transaction.py @@ -2179,20 +2179,19 @@ def __eq__(self, other): and self.authorizing_address == other.authorizing_address ) + class SignedTxnWithAD: - def __init__( - self, stxn: SignedTransaction, apply_data - ): + def __init__(self, stxn: SignedTransaction, apply_data): self.stxn = stxn self.apply_data = apply_data - @staticmethod def undictify(d): stxn = SignedTransaction.undictify(d) ad = ApplyData.undictify(d) return SignedTxnWithAD(stxn, ad) + class ApplyData: def __init__( self, @@ -2203,7 +2202,7 @@ def __init__( close_rewards, eval_delta, config_asset, - application_id + application_id, ): self.closing_amount = closing_amount self.asset_closing_amount = asset_closing_amount @@ -2219,7 +2218,6 @@ def undictify(d): return ApplyData( d.get("ca", 0), d.get("aca", 0), - d.get("rs", 0), d.get("rr", 0), d.get("rc", 0), @@ -2228,16 +2226,16 @@ def undictify(d): d.get("apid", 0), ) + class EvalDelta: - def __init__( - self, gd - ): + def __init__(self, gd): self.global_delta = gd @staticmethod def undictify(d): return EvalDelta(d.get("gd")) + class MultisigTransaction: """ Represents a signed transaction. diff --git a/algosdk/v2client/algod.py b/algosdk/v2client/algod.py index 0ca25b36..ea34c1bc 100644 --- a/algosdk/v2client/algod.py +++ b/algosdk/v2client/algod.py @@ -285,7 +285,7 @@ def block_info( def block( self, - round: int|str, + round: int | str, **kwargs: Any, ) -> "block.BlockInfo": msgp = self.block_info(round, "msgpack") diff --git a/examples/utils.py b/examples/utils.py index 4a06ce37..59e84019 100644 --- a/examples/utils.py +++ b/examples/utils.py @@ -72,12 +72,18 @@ def algod_env(): if not algodata: return () try: - token = open(os.path.join(algodata, "algod.token"), "rt").read().strip() - net = "http://"+open(os.path.join(algodata, "algod.net"), "rt").read().strip() + token = ( + open(os.path.join(algodata, "algod.token"), "rt").read().strip() + ) + net = ( + "http://" + + open(os.path.join(algodata, "algod.net"), "rt").read().strip() + ) return (net, token) except FileNotFoundError: return () + def get_accounts( kmd_address: str = KMD_URL, kmd_token: str = KMD_TOKEN,